Index | Thread | Search

From:
Stuart Henderson <stu@spacehopper.org>
Subject:
NSD update
To:
tech <tech@openbsd.org>
Date:
Wed, 3 Sep 2025 14:59:25 +0100

Download raw body.

Thread
  • Stuart Henderson:

    NSD update

Looking at updating NSD - I've polished up an old diff I had, taking
us to NSD 4.11.0.

I'll look at updating again afterwards, but there have been enough
changes in 4.11.0 that I'd like to do that as a separate stage (not
least to simplify the CVS-wrangling).

Currently running the daemon on amd64 (non-BTI machine), also I've built
nsd and run nsd-checkzone (which exercises the most delicate part, the
new SIMD zone parser for x86) on aarch64 and BTI amd64.

The SIMD code does cpuid detection and only allows a backend to run on
a supported cpu. There is a way to use a non-default choice, setting
ZONE_KERNEL=(haswell|westmere|fallback) in the environment, but IIUC
this still does the cpuid check and won't try and use e.g. the haswell
avx2 code on a machine which doesn't support it.

Obviously the zone parser rewrite means there's even more churn than
normal in this diff...

Index: Makefile.in
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/Makefile.in,v
diff -u -p -r1.38 Makefile.in
--- Makefile.in	12 Apr 2024 15:53:34 -0000	1.38
+++ Makefile.in	3 Sep 2025 13:53:30 -0000
@@ -14,6 +14,7 @@ exec_prefix = @exec_prefix@
 sbindir	= @sbindir@
 mandir = @mandir@
 datarootdir = @datarootdir@
+runstatedir = @runstatedir@
 
 # NSD specific pathnames
 configdir = @configdir@
@@ -23,6 +24,7 @@ logfile = @logfile@
 xfrdir = @xfrdir@
 xfrdfile = @xfrdfile@
 zonelistfile = @zonelistfile@
+cookiesecretsfile = @cookiesecretsfile@
 nsdconfigfile = @nsd_conf_file@
 zonesdir = @zonesdir@
 chrootdir= @chrootdir@
@@ -35,7 +37,7 @@ DNSTAP_OBJ=@DNSTAP_OBJ@
 U=
 
 CC		= @CC@
-CPPFLAGS	= @CPPFLAGS@
+CPPFLAGS	= @CPPFLAGS@ -Isimdzone/include -I@srcdir@/simdzone/include
 CFLAGS		= @CFLAGS@
 LDFLAGS		= @LDFLAGS@
 LIBS		= @LIBS@
@@ -53,6 +55,10 @@ YACC 	= @YACC@
 LEX		= @LEX@
 PROTOC_C	= @PROTOC_C@
 
+DATE != date +'%b %e, %y'
+PROJECT = @PACKAGE_NAME@
+VERSION = @PACKAGE_VERSION@
+
 COMPILE		= $(CC) $(CPPFLAGS) $(CFLAGS)
 LINK		= $(CC) $(CFLAGS) $(LDFLAGS)
 EDIT		= $(SED) \
@@ -62,30 +68,64 @@ EDIT		= $(SED) \
 			-e 's,@configdir\@,$(configdir),g' \
 			-e 's,@zonesdir\@,$(zonesdir),g' \
 			-e 's,@chrootdir\@,$(chrootdir),g' \
+			-e 's,@runstatedir\@,$(runstatedir),g' \
 			-e 's,@pidfile\@,$(pidfile),g' \
 			-e 's,@logfile\@,$(logfile),g' \
 			-e 's,@xfrdir\@,$(xfrdir),g' \
 			-e 's,@xfrdfile\@,$(xfrdfile),g' \
 			-e 's,@zonelistfile\@,$(zonelistfile),g' \
+			-e 's,@cookiesecretsfile\@,$(cookiesecretsfile),g' \
 			-e 's,@nsdconfigfile\@,$(nsdconfigfile),g' \
 			-e 's,@shell\@,$(SHELL),g' \
 			-e 's,@ratelimit_default\@,@ratelimit_default@,g' \
 			-e 's,@dnstap_socket_path\@,@opt_dnstap_socket_path@,g' \
-			-e 's,@user\@,$(user),g'
+			-e 's,@user\@,$(user),g' \
+			-e 's/@project\@/$(PROJECT)/g' \
+			-e 's/@version\@/$(VERSION)/g' \
+			-e 's/@date\@/$(DATE)/g'
 
 TARGETS=nsd nsd-checkconf nsd-checkzone nsd-control nsd.conf.sample nsd-control-setup.sh
 MANUALS=nsd.8 nsd-checkconf.8 nsd-checkzone.8 nsd-control.8 nsd.conf.5
 
 COMMON_OBJ=answer.o axfr.o ixfr.o ixfrcreate.o buffer.o configlexer.o configparser.o dname.o dns.o edns.o iterated_hash.o lookup3.o namedb.o nsec3.o options.o packet.o query.o rbtree.o radtree.o rdata.o region-allocator.o rrl.o siphash.o tsig.o tsig-openssl.o udb.o util.o bitset.o popen3.o proxy_protocol.o
 XFRD_OBJ=xfrd-catalog-zones.o xfrd-disk.o xfrd-notify.o xfrd-tcp.o xfrd.o remote.o $(DNSTAP_OBJ)
-NSD_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) difffile.o ipc.o mini_event.o netio.o nsd.o server.o dbaccess.o dbcreate.o zlexer.o zonec.o zparser.o verify.o
+NSD_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) difffile.o ipc.o mini_event.o netio.o nsd.o server.o dbaccess.o dbcreate.o zonec.o verify.o
 ALL_OBJ=$(NSD_OBJ) nsd-checkconf.o nsd-checkzone.o nsd-control.o nsd-mem.o xfr-inspect.o
 NSD_CHECKCONF_OBJ=$(COMMON_OBJ) nsd-checkconf.o
-NSD_CHECKZONE_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) dbaccess.o dbcreate.o difffile.o ipc.o mini_event.o netio.o server.o zonec.o zparser.o zlexer.o nsd-checkzone.o verify.o
+NSD_CHECKZONE_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) dbaccess.o dbcreate.o difffile.o ipc.o mini_event.o netio.o server.o zonec.o nsd-checkzone.o verify.o
 NSD_CONTROL_OBJ=$(COMMON_OBJ) nsd-control.o
-CUTEST_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) dbaccess.o dbcreate.o difffile.o ipc.o mini_event.o netio.o server.o verify.o zonec.o zparser.o zlexer.o cutest_dname.o cutest_dns.o cutest_iterated_hash.o cutest_run.o cutest_radtree.o cutest_rbtree.o cutest_namedb.o cutest_options.o cutest_region.o cutest_rrl.o cutest_udb.o cutest_util.o cutest_bitset.o cutest_popen3.o cutest_iter.o cutest_event.o cutest.o qtest.o
-NSD_MEM_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) dbaccess.o dbcreate.o difffile.o ipc.o mini_event.o netio.o verify.o server.o zonec.o zparser.o zlexer.o nsd-mem.o
-all:	$(TARGETS) $(MANUALS)
+CUTEST_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) dbaccess.o dbcreate.o difffile.o ipc.o mini_event.o netio.o server.o verify.o zonec.o cutest_dname.o cutest_dns.o cutest_iterated_hash.o cutest_run.o cutest_radtree.o cutest_rbtree.o cutest_namedb.o cutest_options.o cutest_region.o cutest_rrl.o cutest_udb.o cutest_util.o cutest_bitset.o cutest_popen3.o cutest_iter.o cutest_event.o cutest.o qtest.o
+NSD_MEM_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) dbaccess.o dbcreate.o difffile.o ipc.o mini_event.o netio.o verify.o server.o zonec.o nsd-mem.o
+
+.PHONY: all html
+
+all: $(TARGETS) $(MANUALS)
+
+doc/manual/conf.py: doc/manual/conf.py.in
+	$(EDIT) $(srcdir)/doc/manual/conf.py.in > $@
+
+doc/manual/manpages/nsd.conf.5.html: nsd.conf.5
+	mandoc -T html -O fragment nsd.conf.5 > $@
+	sed -i '/<table class="\(head\|foot\)">/,/<\/table>/ d' $@
+
+doc/manual/manpages/nsd.8.html: nsd.8
+	mandoc -T html -O fragment nsd.8 > $@
+	sed -i '/<table class="\(head\|foot\)">/,/<\/table>/ d' $@
+
+doc/manual/manpages/nsd-checkconf.8.html: nsd-checkconf.8
+	mandoc -T html -O fragment nsd-checkconf.8 > $@
+	sed -i '/<table class="\(head\|foot\)">/,/<\/table>/ d' $@
+
+doc/manual/manpages/nsd-checkzone.8.html: nsd-checkzone.8
+	mandoc -T html -O fragment nsd-checkzone.8 > $@
+	sed -i '/<table class="\(head\|foot\)">/,/<\/table>/ d' $@
+
+doc/manual/manpages/nsd-control.8.html: nsd-control.8
+	mandoc -T html -O fragment nsd-control.8 > $@
+	sed -i '/<table class="\(head\|foot\)">/,/<\/table>/ d' $@
+
+html: doc/manual/conf.py doc/manual/manpages/nsd.conf.5.html doc/manual/manpages/nsd.8.html doc/manual/manpages/nsd-checkconf.8.html doc/manual/manpages/nsd-checkzone.8.html doc/manual/manpages/nsd-control.8.html
+	sphinx-build -M html $(srcdir)/doc/manual doc/manual -N -q
 
 $(ALL_OBJ):
 	$(COMPILE) -c $<
@@ -99,25 +139,20 @@ nsd.conf.sample:	$(srcdir)/nsd.conf.samp
 	rm -f nsd.conf.sample
 	$(EDIT) $(srcdir)/nsd.conf.sample.in | $(AWK) '/RRLconfig'@ratelimit@'/ { while($$0 !~ /.*RRLend.*/) { getline; } getline; } {print} ' > nsd.conf.sample
 
-nsd.conf.5:	$(srcdir)/nsd.conf.5.in config.h
-	rm -f nsd.conf.5
-	$(EDIT) $(srcdir)/nsd.conf.5.in | $(AWK) '/rrlstart'@ratelimit@'/ { while($$0 !~ /.*rrlend.*/) { getline; } getline; } {print} ' > nsd.conf.5
-
-nsd.8:	$(srcdir)/nsd.8.in config.h
-	rm -f nsd.8
-	$(EDIT) $(srcdir)/nsd.8.in > nsd.8
-
-nsd-checkconf.8:	$(srcdir)/nsd-checkconf.8.in config.h
-	rm -f nsd-checkconf.8
-	$(EDIT) $(srcdir)/nsd-checkconf.8.in > nsd-checkconf.8
-
-nsd-checkzone.8:	$(srcdir)/nsd-checkzone.8.in config.h
-	rm -f nsd-checkzone.8
-	$(EDIT) $(srcdir)/nsd-checkzone.8.in > nsd-checkzone.8
-
-nsd-control.8:	$(srcdir)/nsd-control.8.in config.h
-	rm -f nsd-control.8
-	$(EDIT) $(srcdir)/nsd-control.8.in > nsd-control.8
+nsd.conf.5: $(srcdir)/nsd.conf.5.in config.h
+	$(EDIT) $(srcdir)/nsd.conf.5.in | $(AWK) '/rrlstart'@ratelimit@'/ { while($$0 !~ /.*rrlend.*/) { getline; } getline; } {print} ' > $@
+
+nsd.8: $(srcdir)/nsd.8.in config.h
+	$(EDIT) $(srcdir)/nsd.8.in > $@
+
+nsd-checkconf.8: $(srcdir)/nsd-checkconf.8.in config.h
+	$(EDIT) $(srcdir)/nsd-checkconf.8.in > $@
+
+nsd-checkzone.8: $(srcdir)/nsd-checkzone.8.in config.h
+	$(EDIT) $(srcdir)/nsd-checkzone.8.in > $@
+
+nsd-control.8: $(srcdir)/nsd-control.8.in config.h
+	$(EDIT) $(srcdir)/nsd-control.8.in > $@
 
 install:
 
@@ -128,6 +163,7 @@ orig-install: all
 	$(INSTALL) -d $(DESTDIR)$(xfrdir)
 	$(INSTALL) -d `dirname $(DESTDIR)$(xfrdfile)`
 	$(INSTALL) -d `dirname $(DESTDIR)$(zonelistfile)`
+	$(INSTALL) -d `dirname $(DESTDIR)$(cookiesecretsfile)`
 	$(INSTALL) -d $(DESTDIR)$(mandir)
 	$(INSTALL) -d $(DESTDIR)$(mandir)/man8
 	$(INSTALL) -d $(DESTDIR)$(mandir)/man5
@@ -150,30 +186,33 @@ uninstall:
 	rm -f -- $(DESTDIR)$(mandir)/man8/nsd-checkconf.8 $(DESTDIR)$(mandir)/man8/nsd-checkzone.8 $(DESTDIR)$(mandir)/man8/nsd-control.8
 	rm -f -- $(DESTDIR)$(pidfile)
 	@echo
-	@echo "You still need to remove $(DESTDIR)$(configdir), $(DESTDIR)$(piddir), $(DESTDIR)$(xfrdfile), $(DESTDIR)$(zonelistfile) directory by hand."
+	@echo "You still need to remove $(DESTDIR)$(configdir), $(DESTDIR)$(piddir), $(DESTDIR)$(xfrdfile), $(DESTDIR)$(zonelistfile) $(DESTDIR)$(cookiesecretsfile) directory by hand."
 
 test: 
 
-nsd:	$(NSD_OBJ) $(LIBOBJS)
-	$(LINK) -o $@ $(NSD_OBJ) $(LIBOBJS) $(SSL_LIBS) $(LIBS)
+simdzone/libzone.a:
+	$(MAKE) -C simdzone
+
+nsd:	simdzone/libzone.a $(NSD_OBJ) $(LIBOBJS)
+	$(LINK) -o $@ $(NSD_OBJ) $(LIBOBJS) simdzone/libzone.a $(SSL_LIBS) $(LIBS)
 
-nsd-checkconf:	$(NSD_CHECKCONF_OBJ) $(LIBOBJS)
-	$(LINK) -o $@ $(NSD_CHECKCONF_OBJ) $(LIBOBJS) $(LIBS)
+nsd-checkconf:	simdzone/libzone.a $(NSD_CHECKCONF_OBJ) $(LIBOBJS)
+	$(LINK) -o $@ $(NSD_CHECKCONF_OBJ) simdzone/libzone.a $(LIBOBJS) $(SSL_LIBS) $(LIBS)
 
-nsd-checkzone:	$(NSD_CHECKZONE_OBJ) $(LIBOBJS)
-	$(LINK) -o $@ $(NSD_CHECKZONE_OBJ) $(LIBOBJS) $(SSL_LIBS) $(LIBS)
+nsd-checkzone:	simdzone/libzone.a $(NSD_CHECKZONE_OBJ) $(LIBOBJS)
+	$(LINK) -o $@ $(NSD_CHECKZONE_OBJ) $(LIBOBJS) simdzone/libzone.a $(SSL_LIBS) $(LIBS)
 
-nsd-control:	$(NSD_CONTROL_OBJ) $(LIBOBJS)
-	$(LINK) -o $@ $(NSD_CONTROL_OBJ) $(LIBOBJS) $(SSL_LIBS) $(LIBS)
+nsd-control:	simdzone/libzone.a $(NSD_CONTROL_OBJ) $(LIBOBJS)
+	$(LINK) -o $@ $(NSD_CONTROL_OBJ) $(LIBOBJS) simdzone/libzone.a $(SSL_LIBS) $(LIBS)
 
-nsd-mem:	$(NSD_MEM_OBJ) $(LIBOBJS)
-	$(LINK) -o $@ $(NSD_MEM_OBJ) $(LIBOBJS) $(SSL_LIBS) $(LIBS)
+nsd-mem:	simdzone/libzone.a $(NSD_MEM_OBJ) $(LIBOBJS)
+	$(LINK) -o $@ $(NSD_MEM_OBJ) $(LIBOBJS) simdzone/libzone.a $(SSL_LIBS) $(LIBS)
 
-cutest:	$(CUTEST_OBJ) $(LIBOBJS) popen3_echo
-	$(LINK) -o $@ $(CUTEST_OBJ) $(LIBOBJS) $(SSL_LIBS) $(LIBS)
+cutest:	simdzone/libzone.a $(CUTEST_OBJ) $(LIBOBJS) popen3_echo
+	$(LINK) -o $@ $(CUTEST_OBJ) $(LIBOBJS) simdzone/libzone.a $(SSL_LIBS) $(LIBS)
 
-xfr-inspect:	xfr-inspect.o $(COMMON_OBJ) zonec.o zparser.o zlexer.o $(LIBOBJS)
-	$(LINK) -o $@ xfr-inspect.o $(COMMON_OBJ) zonec.o zparser.o zlexer.o $(LIBOBJS) $(LIBS)
+xfr-inspect:	simdzone/libzone.a xfr-inspect.o $(COMMON_OBJ) zonec.o $(LIBOBJS)
+	$(LINK) -o $@ xfr-inspect.o $(COMMON_OBJ) zonec.o $(LIBOBJS) simdzone/libzone.a $(SSL_LIBS) $(LIBS)
 
 popen3_echo: popen3.o popen3_echo.o
 	$(LINK) -o $@ popen3.o popen3_echo.o
@@ -189,21 +228,33 @@ audit: nsd nsd-checkconf nsd-checkzone n
 	./checksec --file=nsd-control
 	./checksec --file=nsd-mem
 
-clean:
+.clean:
 	rm -f *.o $(TARGETS) $(MANUALS) cutest popen3_echo xfr-inspect nsd-mem
+	rm -f doc/manual/conf.py doc/manual/manpages/nsd.conf.5.html doc/manual/manpages/nsd.8.html doc/manual/manpages/nsd-checkconf.8.html doc/manual/manpages/nsd-checkzone.8.html doc/manual/manpages/nsd-control.8.html
+	rm -rf doc/manual/doctrees doc/manual/html
 
-distclean: clean
+.distclean: .clean
 	rm -f Makefile config.h config.log config.status dnstap/dnstap_config.h
 
-realclean: distclean
+.realclean: .distclean
 	rm -rf autom4te*
-	rm -f zlexer.c zparser.h zparser.c zparser.stamp
 	rm -f configlexer.c configparser.h configparser.c configparser.stamp
 
+clean: .clean
+	$(MAKE) -C simdzone clean
+
+distclean: .distclean
+	$(MAKE) -C simdzone distclean
+
+realclean: .realclean
+	$(MAKE) -C simdzone realclean
+
 maintainer-clean: realclean
 
-devclean: realclean
+devclean: .realclean
 	rm -f config.h.in configure
+	$(MAKE) -C simdzone devclean
+
 
 basename.o:	$(srcdir)/compat/basename.c
 	$(COMPILE) -c $(srcdir)/compat/basename.c
@@ -328,16 +379,6 @@ cutest.o:	$(srcdir)/tpkg/cutest/cutest.c
 qtest.o:	$(srcdir)/tpkg/cutest/qtest.c
 	$(COMPILE) -c $(srcdir)/tpkg/cutest/qtest.c
 
-zlexer.c:	$(srcdir)/zlexer.lex
-	if test "$(LEX)" != ":"; then rm -f $@ ;\
-		echo '#include "config.h"' > $@ ;\
-		$(LEX) -i -t $(srcdir)/zlexer.lex >> $@ ;\
-	fi
-	@if test ! -f $@; then echo "No $@ : need flex and bison to compile from source repository"; exit 1; fi
-
-zparser.c zparser.h: $(srcdir)/zparser.y
-	$(YACC) -d -o zparser.c $(srcdir)/zparser.y
-
 configlexer.c:	$(srcdir)/configlexer.lex
 	if test "$(LEX)" != ":"; then rm -f $@ ;\
 		echo '#include "config.h"' > $@ ;\
@@ -345,17 +386,20 @@ configlexer.c:	$(srcdir)/configlexer.lex
 	fi
 	@if test ! -f $@; then echo "No $@ : need flex and bison to compile from source repository"; exit 1; fi
 
-configparser.c configparser.h:	$(srcdir)/configparser.y
+# Builds both util/configparser.c and util/configparser.h.
+# To avoid double-building we split one target out.
+configparser.c:	$(srcdir)/configparser.y
 	$(YACC) -d -p c_ -o configparser.c $(srcdir)/configparser.y
 
+configparser.h: configparser.c
+	touch $@
+
 # for build to run flex and bison before compiling code that needs the headers
 configlexer.o: configlexer.c config.h configparser.h
 configparser.o: configparser.c config.h configparser.h
 options.o: $(srcdir)/options.c config.h configparser.h
-zlexer.o: zlexer.c config.h zparser.h
-zparser.o: zparser.c config.h zparser.h
-dns.o: $(srcdir)/dns.c config.h zparser.h
-zonec.o: $(srcdir)/zonec.c config.h zparser.h
+dns.o: $(srcdir)/dns.c config.h
+zonec.o: $(srcdir)/zonec.c config.h
 
 # dnstap
 dnstap.o:	$(srcdir)/dnstap/dnstap.c config.h dnstap/dnstap_config.h \
@@ -398,9 +442,6 @@ depend:
 			-e 's?$$(srcdir)/dnstap/dnstap_config.h??g' \
 			-e 's?$$(srcdir)/dnstap/dnstap.pb-c.c?dnstap/dnstap.pb-c.c?g' \
 			-e 's?$$(srcdir)/dnstap/dnstap.pb-c.h?dnstap/dnstap.pb-c.h?g' \
-			-e 's?$$(srcdir)/zlexer.c?zlexer.c?g' \
-			-e 's?$$(srcdir)/zparser.c?zparser.c?g' \
-			-e 's?$$(srcdir)/zparser.h?zparser.h?g' \
 			> $(DEPEND_TMP)
 	cp $(DEPEND_TARGET) $(DEPEND_TMP2)
 	head -`$(EGREP) -n "# Dependencies" $(DEPEND_TARGET) | tail -1 | $(SED) -e 's/:.*$$//'` $(DEPEND_TMP2) > $(DEPEND_TARGET)
@@ -446,7 +487,7 @@ dname.o: $(srcdir)/dname.c config.h $(sr
  $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsd.h \
  $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/packet.h $(srcdir)/tsig.h
 dns.o: $(srcdir)/dns.c config.h $(srcdir)/dns.h $(srcdir)/zonec.h $(srcdir)/namedb.h $(srcdir)/dname.h \
- $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/radtree.h $(srcdir)/rbtree.h zparser.h
+ $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/radtree.h $(srcdir)/rbtree.h
 edns.o: $(srcdir)/edns.c config.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h \
  $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/nsd.h $(srcdir)/bitset.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h \
  $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/packet.h $(srcdir)/tsig.h
@@ -552,14 +593,9 @@ xfrd-tcp.o: $(srcdir)/xfrd-tcp.c config.
 xfr-inspect.o: $(srcdir)/xfr-inspect.c config.h $(srcdir)/util.h $(srcdir)/buffer.h \
  $(srcdir)/region-allocator.h $(srcdir)/packet.h $(srcdir)/dns.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/radtree.h $(srcdir)/rbtree.h \
  $(srcdir)/rdata.h $(srcdir)/difffile.h $(srcdir)/options.h $(srcdir)/udb.h
-zlexer.o: zlexer.c config.h $(srcdir)/zonec.h $(srcdir)/namedb.h $(srcdir)/dname.h \
- $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h zparser.h
 zonec.o: $(srcdir)/zonec.c config.h $(srcdir)/zonec.h $(srcdir)/namedb.h $(srcdir)/dname.h \
  $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/rdata.h \
- zparser.h $(srcdir)/options.h $(srcdir)/nsec3.h
-zparser.o: zparser.c config.h $(srcdir)/dname.h $(srcdir)/buffer.h \
- $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/zonec.h \
- zparser.h
+ $(srcdir)/options.h $(srcdir)/nsec3.h
 b64_ntop.o: $(srcdir)/compat/b64_ntop.c config.h
 b64_pton.o: $(srcdir)/compat/b64_pton.c config.h
 basename.o: $(srcdir)/compat/basename.c
Index: README.md
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/README.md,v
diff -u -p -r1.2 README.md
--- README.md	29 Jun 2023 19:38:49 -0000	1.2
+++ README.md	3 Sep 2025 13:53:30 -0000
@@ -1,6 +1,7 @@
 # NSD
 
-[![Cirrus Build Status](https://api.cirrus-ci.com/github/NLnetLabs/nsd.svg?branch=master)](https://cirrus-ci.com/github/NLnetLabs/nsd)
+[![GitHub Build Status](https://github.com/NLnetLabs/nsd/actions/workflows/build-test.yml/badge.svg?branch=master)](https://github.com/NLnetLabs/nsd/actions)
+[![Coverity Scan Status](https://scan.coverity.com/projects/18867/badge.svg)](https://scan.coverity.com/projects/nlnetlabs-nsd)
 [![Packaging status](https://repology.org/badge/tiny-repos/nsd.svg)](https://repology.org/project/nsd/versions)
 [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1462/badge)](https://bestpractices.coreinfrastructure.org/projects/1462)
 [![Mastodon Follow](https://img.shields.io/mastodon/follow/109262826617293067?domain=https%3A%2F%2Ffosstodon.org&style=social)](https://fosstodon.org/@nlnetlabs)
@@ -15,7 +16,7 @@ or post a message on the
 You can learn more about NSD by reading our
 [documentation](https://nsd.docs.nlnetlabs.nl/).
 
-## Compiling
+## Building
 
 Make sure you have the following installed:
   * C toolchain (the set of tools to compile C such as a compiler, linker, and assembler)
@@ -24,17 +25,24 @@ Make sure you have the following install
   * flex
   * bison
 
-The repository does not contain `./configure`, but you can generate it like
-this (note that the `./configure` is included in release tarballs so they do not have to be generated):
+When building from Git, the `configure` script and [simdzone][simdzone]
+sources are missing, use the following commands to get started (note that the
+`configure` script and sources are included in release tarballs and do not
+need to be generated/downloaded):
 
 ```
-autoreconf -fi
+$ git submodule update --init
+$ autoreconf -fi
 ```
 
-NSD can be compiled and installed using:
+> `autoreconf` should install the required auxiliary files (e.g. `config.sub`
+> and `config.guess`). Older versions of `autoreconf` may not do so, try
+> running `libtoolize -fi -c` first in that case.
+
+Compile and install using:
 
 ```
-./configure && make && make install
+$ ./configure && make && make install
 ```
 
 ## NSD configuration
@@ -45,3 +53,6 @@ installed (use `man nsd.conf`) and are a
 
 An example configuration file is located in
 [nsd.conf.sample](https://github.com/NLnetLabs/nsd/blob/master/nsd.conf.sample.in).
+
+
+[simdzone]: https://github.com/NLnetLabs/simdzone
Index: axfr.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/axfr.c,v
diff -u -p -r1.23 axfr.c
--- axfr.c	20 Dec 2023 17:29:01 -0000	1.23
+++ axfr.c	3 Sep 2025 13:53:30 -0000
@@ -187,6 +187,27 @@ static int axfr_ixfr_can_admit_query(str
 {
 	struct acl_options *acl = NULL;
 	struct zone_options* zone_opt;
+
+#ifdef HAVE_SSL
+	/* tls-auth-xfr-only is set and this is not an authenticated TLS */
+	if (nsd->options->tls_auth_xfr_only && !q->tls_auth) {
+		if (verbosity >= 2) {
+			char address[128], proxy[128];
+			addr2str(&q->client_addr, address, sizeof(address));
+			addr2str(&q->remote_addr, proxy, sizeof(proxy));
+			VERBOSITY(2, (LOG_INFO, "%s for %s from %s refused tls-auth-xfr-only",
+				(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
+				dname_to_string(q->qname, NULL),
+				address));
+		}
+		RCODE_SET(q->packet, RCODE_REFUSE);
+		/* RFC8914 - Extended DNS Errors
+		 * 4.19.  Extended DNS Error Code 18 - Prohibited */
+		q->edns.ede = EDE_PROHIBITED;
+		return 0;
+	}
+#endif
+
 	zone_opt = zone_options_find(nsd->options, q->qname);
 	if(zone_opt && q->is_proxied && acl_check_incoming_block_proxy(
 		zone_opt->pattern->provide_xfr, q, &acl) == -1) {
@@ -234,15 +255,30 @@ static int axfr_ixfr_can_admit_query(str
 		}
 		return 0;
 	}
+#ifdef HAVE_SSL
+	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "%s admitted acl %s %s %s",
+		(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
+		acl->ip_address_spec, acl->key_name?acl->key_name:"NOKEY",
+		(q->tls||q->tls_auth)?(q->tls?"tls":"tls-auth"):""));
+#else
 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "%s admitted acl %s %s",
 		(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
 		acl->ip_address_spec, acl->key_name?acl->key_name:"NOKEY"));
+#endif
 	if (verbosity >= 1) {
 		char a[128];
 		addr2str(&q->client_addr, a, sizeof(a));
+#ifdef HAVE_SSL
+		VERBOSITY(1, (LOG_INFO, "%s for %s from %s %s %s",
+			(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
+			dname_to_string(q->qname, NULL), a,
+			(q->tls||q->tls_auth)?(q->tls?"tls":"tls-auth"):"",
+			q->cert_cn?q->cert_cn:"not-verified"));
+#else
 		VERBOSITY(1, (LOG_INFO, "%s for %s from %s",
 			(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
 			dname_to_string(q->qname, NULL), a));
+#endif
 	}
 	return 1;
 }
Index: buffer.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/buffer.c,v
diff -u -p -r1.1.1.5 buffer.c
--- buffer.c	26 Nov 2013 12:50:19 -0000	1.1.1.5
+++ buffer.c	3 Sep 2025 13:53:30 -0000
@@ -41,7 +41,7 @@ buffer_create(region_type *region, size_
 }
 
 void
-buffer_create_from(buffer_type *buffer, void *data, size_t size)
+buffer_create_from(buffer_type *buffer, const void *data, size_t size)
 {
 	assert(data);
 
Index: buffer.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/buffer.h,v
diff -u -p -r1.4 buffer.h
--- buffer.h	12 Apr 2024 15:53:34 -0000	1.4
+++ buffer.h	3 Sep 2025 13:53:30 -0000
@@ -76,7 +76,7 @@ buffer_type *buffer_create(region_type *
  * and no memory allocations are done.  The buffer is fixed and cannot
  * be resized using buffer_reserve().
  */
-void buffer_create_from(buffer_type *buffer, void *data, size_t size);
+void buffer_create_from(buffer_type *buffer, const void *data, size_t size);
 
 /*
  * Clear the buffer and make it ready for writing.  The buffer's limit
Index: config.guess
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/config.guess,v
diff -u -p -r1.3 config.guess
--- config.guess	15 Apr 2024 12:44:24 -0000	1.3
+++ config.guess	3 Sep 2025 13:53:31 -0000
@@ -1,10 +1,10 @@
-#!/usr/bin/sh
+#! /bin/sh
 # Attempt to guess a canonical system name.
-#   Copyright 1992-2023 Free Software Foundation, Inc.
+#   Copyright 1992-2022 Free Software Foundation, Inc.
 
 # shellcheck disable=SC2006,SC2268 # see below for rationale
 
-timestamp='2023-06-23'
+timestamp='2022-01-09'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -47,7 +47,7 @@ me=`echo "$0" | sed -e 's,.*/,,'`
 usage="\
 Usage: $0 [OPTION]
 
-Output the configuration name of the system '$me' is run on.
+Output the configuration name of the system \`$me' is run on.
 
 Options:
   -h, --help         print this help, then exit
@@ -60,13 +60,13 @@ version="\
 GNU config.guess ($timestamp)
 
 Originally written by Per Bothner.
-Copyright 1992-2023 Free Software Foundation, Inc.
+Copyright 1992-2022 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
 
 help="
-Try '$me --help' for more information."
+Try \`$me --help' for more information."
 
 # Parse command line
 while test $# -gt 0 ; do
@@ -102,8 +102,8 @@ GUESS=
 # temporary files to be created and, as you can see below, it is a
 # headache to deal with in a portable fashion.
 
-# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still
-# use 'HOST_CC' if defined, but it is deprecated.
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
 
 # Portable tmp directory creation inspired by the Autoconf team.
 
@@ -459,7 +459,7 @@ case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME
 		UNAME_RELEASE=`uname -v`
 		;;
 	esac
-	# Japanese Language versions have a version number like '4.1.3-JL'.
+	# Japanese Language versions have a version number like `4.1.3-JL'.
 	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
 	GUESS=sparc-sun-sunos$SUN_REL
 	;;
@@ -966,12 +966,6 @@ EOF
 	GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
 	GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
 	;;
-    x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*)
-	GUESS="$UNAME_MACHINE-pc-managarm-mlibc"
-	;;
-    *:[Mm]anagarm:*:*)
-	GUESS="$UNAME_MACHINE-unknown-managarm-mlibc"
-	;;
     *:Minix:*:*)
 	GUESS=$UNAME_MACHINE-unknown-minix
 	;;
@@ -1042,7 +1036,7 @@ EOF
     k1om:Linux:*:*)
 	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
 	;;
-    loongarch32:Linux:*:* | loongarch64:Linux:*:*)
+    loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
 	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
 	;;
     m32r*:Linux:*:*)
@@ -1157,27 +1151,16 @@ EOF
 	;;
     x86_64:Linux:*:*)
 	set_cc_for_build
-	CPU=$UNAME_MACHINE
 	LIBCABI=$LIBC
 	if test "$CC_FOR_BUILD" != no_compiler_found; then
-	    ABI=64
-	    sed 's/^	    //' << EOF > "$dummy.c"
-	    #ifdef __i386__
-	    ABI=x86
-	    #else
-	    #ifdef __ILP32__
-	    ABI=x32
-	    #endif
-	    #endif
-EOF
-	    cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
-	    eval "$cc_set_abi"
-	    case $ABI in
-		x86) CPU=i686 ;;
-		x32) LIBCABI=${LIBC}x32 ;;
-	    esac
+	    if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
+		(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		grep IS_X32 >/dev/null
+	    then
+		LIBCABI=${LIBC}x32
+	    fi
 	fi
-	GUESS=$CPU-pc-linux-$LIBCABI
+	GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI
 	;;
     xtensa*:Linux:*:*)
 	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
@@ -1197,7 +1180,7 @@ EOF
 	GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
 	;;
     i*86:OS/2:*:*)
-	# If we were able to find 'uname', then EMX Unix compatibility
+	# If we were able to find `uname', then EMX Unix compatibility
 	# is probably installed.
 	GUESS=$UNAME_MACHINE-pc-os2-emx
 	;;
@@ -1338,7 +1321,7 @@ EOF
 		GUESS=ns32k-sni-sysv
 	fi
 	;;
-    PENTIUM:*:4.0*:*)	# Unisys 'ClearPath HMP IX 4000' SVR4/MP effort
+    PENTIUM:*:4.0*:*)	# Unisys `ClearPath HMP IX 4000' SVR4/MP effort
 			# says <Richard.M.Bartel@ccMail.Census.GOV>
 	GUESS=i586-unisys-sysv4
 	;;
@@ -1384,11 +1367,8 @@ EOF
     BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
 	GUESS=i586-pc-haiku
 	;;
-    ppc:Haiku:*:*)	# Haiku running on Apple PowerPC
-	GUESS=powerpc-apple-haiku
-	;;
-    *:Haiku:*:*)	# Haiku modern gcc (not bound by BeOS compat)
-	GUESS=$UNAME_MACHINE-unknown-haiku
+    x86_64:Haiku:*:*)
+	GUESS=x86_64-unknown-haiku
 	;;
     SX-4:SUPER-UX:*:*)
 	GUESS=sx4-nec-superux$UNAME_RELEASE
Index: config.h.in
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/config.h.in,v
diff -u -p -r1.44 config.h.in
--- config.h.in	15 Apr 2024 12:44:24 -0000	1.44
+++ config.h.in	3 Sep 2025 13:53:31 -0000
@@ -21,6 +21,9 @@
 /* Pathname to the NSD configuration file */
 #undef CONFIGFILE
 
+/* Pathname to the NSD cookies secrets file. */
+#undef COOKIESECRETSFILE
+
 /* number of arguments for CPU_OR is three */
 #undef CPU_OR_THREE_ARGS
 
@@ -58,6 +61,9 @@
 /* Define to 1 if you have the <arpa/inet.h> header file. */
 #undef HAVE_ARPA_INET_H
 
+/* Define to 1 if you have the `ASN1_STRING_get0_data' function. */
+#undef HAVE_ASN1_STRING_GET0_DATA
+
 /* Whether the C compiler accepts the "format" attribute */
 #undef HAVE_ATTR_FORMAT
 
@@ -301,6 +307,9 @@
 
 /* Define to 1 if you have the <openssl/ssl.h> header file. */
 #undef HAVE_OPENSSL_SSL_H
+
+/* Define to 1 if you have the <openssl/x509v3.h> header file. */
+#undef HAVE_OPENSSL_X509V3_H
 
 /* Define to 1 if you have the `ppoll' function. */
 #undef HAVE_PPOLL
Index: config.sub
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/config.sub,v
diff -u -p -r1.3 config.sub
--- config.sub	15 Apr 2024 12:44:24 -0000	1.3
+++ config.sub	3 Sep 2025 13:53:31 -0000
@@ -1,10 +1,10 @@
-#!/usr/bin/sh
+#! /bin/sh
 # Configuration validation subroutine script.
-#   Copyright 1992-2023 Free Software Foundation, Inc.
+#   Copyright 1992-2022 Free Software Foundation, Inc.
 
 # shellcheck disable=SC2006,SC2268 # see below for rationale
 
-timestamp='2023-06-23'
+timestamp='2022-01-03'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -76,13 +76,13 @@ Report bugs and patches to <config-patch
 version="\
 GNU config.sub ($timestamp)
 
-Copyright 1992-2023 Free Software Foundation, Inc.
+Copyright 1992-2022 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
 
 help="
-Try '$me --help' for more information."
+Try \`$me --help' for more information."
 
 # Parse command line
 while test $# -gt 0 ; do
@@ -130,7 +130,7 @@ IFS=$saved_IFS
 # Separate into logical components for further validation
 case $1 in
 	*-*-*-*-*)
-		echo "Invalid configuration '$1': more than four components" >&2
+		echo Invalid configuration \`"$1"\': more than four components >&2
 		exit 1
 		;;
 	*-*-*-*)
@@ -145,7 +145,7 @@ case $1 in
 			nto-qnx* | linux-* | uclinux-uclibc* \
 			| uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
 			| netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
-			| storm-chaos* | os2-emx* | rtmk-nova* | managarm-*)
+			| storm-chaos* | os2-emx* | rtmk-nova*)
 				basic_machine=$field1
 				basic_os=$maybe_os
 				;;
@@ -943,7 +943,7 @@ $basic_machine
 EOF
 		IFS=$saved_IFS
 		;;
-	# We use 'pc' rather than 'unknown'
+	# We use `pc' rather than `unknown'
 	# because (1) that's what they normally are, and
 	# (2) the word "unknown" tends to confuse beginning users.
 	i*86 | x86_64)
@@ -1075,7 +1075,7 @@ case $cpu-$vendor in
 	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
 		cpu=i586
 		;;
-	pentiumpro-* | p6-* | 6x86-* | athlon-* | athlon_*-*)
+	pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
 		cpu=i686
 		;;
 	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
@@ -1207,7 +1207,7 @@ case $cpu-$vendor in
 			| k1om \
 			| le32 | le64 \
 			| lm32 \
-			| loongarch32 | loongarch64 \
+			| loongarch32 | loongarch64 | loongarchx32 \
 			| m32c | m32r | m32rle \
 			| m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
 			| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
@@ -1285,7 +1285,7 @@ case $cpu-$vendor in
 				;;
 
 			*)
-				echo "Invalid configuration '$1': machine '$cpu-$vendor' not recognized" 1>&2
+				echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
 				exit 1
 				;;
 		esac
@@ -1341,10 +1341,6 @@ EOF
 		kernel=linux
 		os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
 		;;
-	managarm*)
-		kernel=managarm
-		os=`echo "$basic_os" | sed -e 's|managarm|mlibc|'`
-		;;
 	*)
 		kernel=
 		os=$basic_os
@@ -1758,7 +1754,7 @@ case $os in
 	     | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
 	     | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
 	     | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
-	     | fiwix* | mlibc* )
+	     | fiwix* )
 		;;
 	# This one is extra strict with allowed versions
 	sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
@@ -1766,11 +1762,8 @@ case $os in
 		;;
 	none)
 		;;
-	kernel* )
-		# Restricted further below
-		;;
 	*)
-		echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2
+		echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2
 		exit 1
 		;;
 esac
@@ -1779,24 +1772,14 @@ esac
 # (given a valid OS), if there is a kernel.
 case $kernel-$os in
 	linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \
-		   | linux-musl* | linux-relibc* | linux-uclibc* | linux-mlibc* )
+		   | linux-musl* | linux-relibc* | linux-uclibc* )
 		;;
 	uclinux-uclibc* )
 		;;
-	managarm-mlibc* | managarm-kernel* )
-		;;
-	-dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* | -mlibc* )
+	-dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* )
 		# These are just libc implementations, not actual OSes, and thus
 		# require a kernel.
-		echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2
-		exit 1
-		;;
-	-kernel* )
-		echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2
-		exit 1
-		;;
-	*-kernel* )
-		echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2
+		echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2
 		exit 1
 		;;
 	kfreebsd*-gnu* | kopensolaris*-gnu*)
@@ -1813,7 +1796,7 @@ case $kernel-$os in
 		# Blank kernel with real OS is always fine.
 		;;
 	*-*)
-		echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2
+		echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2
 		exit 1
 		;;
 esac
Index: configlexer.lex
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/configlexer.lex,v
diff -u -p -r1.25 configlexer.lex
--- configlexer.lex	12 Apr 2024 15:53:34 -0000	1.25
+++ configlexer.lex	3 Sep 2025 13:53:31 -0000
@@ -267,6 +267,7 @@ rrl-ipv4-prefix-length{COLON}	{ LEXOUT((
 rrl-ipv6-prefix-length{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV6_PREFIX_LENGTH;}
 rrl-whitelist-ratelimit{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST_RATELIMIT;}
 rrl-whitelist{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST;}
+reload-config{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RELOAD_CONFIG; }
 zonefiles-check{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_CHECK;}
 zonefiles-write{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_WRITE;}
 dnstap{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP;}
@@ -285,6 +286,7 @@ dnstap-version{COLON}	{ LEXOUT(("v(%s) "
 dnstap-log-auth-query-messages{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP_LOG_AUTH_QUERY_MESSAGES; }
 dnstap-log-auth-response-messages{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP_LOG_AUTH_RESPONSE_MESSAGES; }
 log-time-ascii{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_LOG_TIME_ASCII;}
+log-time-iso{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_LOG_TIME_ISO;}
 round-robin{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ROUND_ROBIN;}
 minimal-responses{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_MINIMAL_RESPONSES;}
 confine-to-zone{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CONFINE_TO_ZONE;}
@@ -304,11 +306,14 @@ tls-service-key{COLON}	{ LEXOUT(("v(%s) 
 tls-service-ocsp{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TLS_SERVICE_OCSP;}
 tls-service-pem{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TLS_SERVICE_PEM;}
 tls-port{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_TLS_PORT;}
+tls-auth-port{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_TLS_AUTH_PORT;}
+tls-auth-xfr-only{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_TLS_AUTH_XFR_ONLY;}
 tls-cert-bundle{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TLS_CERT_BUNDLE; }
 proxy-protocol-port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PROXY_PROTOCOL_PORT; }
 answer-cookie{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ANSWER_COOKIE;}
 cookie-secret{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_COOKIE_SECRET;}
 cookie-secret-file{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_COOKIE_SECRET_FILE;}
+cookie-staging-secret{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_COOKIE_STAGING_SECRET;}
 xfrd-tcp-max{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_TCP_MAX;}
 xfrd-tcp-pipeline{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_TCP_PIPELINE;}
 verify{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_VERIFY; }
Index: configparser.y
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/configparser.y,v
diff -u -p -r1.39 configparser.y
--- configparser.y	12 Apr 2024 15:53:34 -0000	1.39
+++ configparser.y	3 Sep 2025 13:53:31 -0000
@@ -111,10 +111,12 @@ struct component {
 %token VAR_STATISTICS
 %token VAR_XFRD_RELOAD_TIMEOUT
 %token VAR_LOG_TIME_ASCII
+%token VAR_LOG_TIME_ISO
 %token VAR_ROUND_ROBIN
 %token VAR_MINIMAL_RESPONSES
 %token VAR_CONFINE_TO_ZONE
 %token VAR_REFUSE_ANY
+%token VAR_RELOAD_CONFIG
 %token VAR_ZONEFILES_CHECK
 %token VAR_ZONEFILES_WRITE
 %token VAR_RRL_SIZE
@@ -127,6 +129,8 @@ struct component {
 %token VAR_TLS_SERVICE_PEM
 %token VAR_TLS_SERVICE_OCSP
 %token VAR_TLS_PORT
+%token VAR_TLS_AUTH_PORT
+%token VAR_TLS_AUTH_XFR_ONLY
 %token VAR_TLS_CERT_BUNDLE
 %token VAR_PROXY_PROTOCOL_PORT
 %token VAR_CPU_AFFINITY
@@ -192,6 +196,7 @@ struct component {
 %token VAR_ANSWER_COOKIE
 %token VAR_COOKIE_SECRET
 %token VAR_COOKIE_SECRET_FILE
+%token VAR_COOKIE_STAGING_SECRET
 %token VAR_MAX_REFRESH_TIME
 %token VAR_MIN_REFRESH_TIME
 %token VAR_MAX_RETRY_TIME
@@ -443,6 +448,8 @@ server_option:
       cfg_parser->opt->rrl_whitelist_ratelimit = (size_t)$2;
 #endif
     }
+  | VAR_RELOAD_CONFIG boolean
+    { cfg_parser->opt->reload_config = $2; }
   | VAR_ZONEFILES_CHECK boolean
     { cfg_parser->opt->zonefiles_check = $2; }
   | VAR_ZONEFILES_WRITE number
@@ -452,6 +459,11 @@ server_option:
       cfg_parser->opt->log_time_ascii = $2;
       log_time_asc = cfg_parser->opt->log_time_ascii;
     }
+  | VAR_LOG_TIME_ISO boolean
+    {
+      cfg_parser->opt->log_time_iso = $2;
+      log_time_iso = cfg_parser->opt->log_time_iso;
+    }
   | VAR_ROUND_ROBIN boolean
     {
       cfg_parser->opt->round_robin = $2;
@@ -479,6 +491,21 @@ server_option:
       (void)snprintf(buf, sizeof(buf), "%lld", $2);
       cfg_parser->opt->tls_port = region_strdup(cfg_parser->opt->region, buf);
     }
+  | VAR_TLS_AUTH_PORT number
+    {
+      /* port number, stored as string */
+      char buf[16];
+      (void)snprintf(buf, sizeof(buf), "%lld", $2);
+      cfg_parser->opt->tls_auth_port = region_strdup(cfg_parser->opt->region, buf);
+    }
+  | VAR_TLS_AUTH_XFR_ONLY boolean
+    {
+      if (!cfg_parser->opt->tls_auth_port) {
+        yyerror("tls-auth-xfr-only set without or before tls-auth-port");
+        YYABORT;
+      }
+      cfg_parser->opt->tls_auth_xfr_only = $2;
+    }
   | VAR_TLS_CERT_BUNDLE STRING
     { cfg_parser->opt->tls_cert_bundle = region_strdup(cfg_parser->opt->region, $2); }
   | VAR_PROXY_PROTOCOL_PORT number
@@ -492,9 +519,39 @@ server_option:
   | VAR_ANSWER_COOKIE boolean
     { cfg_parser->opt->answer_cookie = $2; }
   | VAR_COOKIE_SECRET STRING
-    { cfg_parser->opt->cookie_secret = region_strdup(cfg_parser->opt->region, $2); }
+    {
+      uint8_t secret[32];
+      ssize_t len = hex_pton($2, secret, NSD_COOKIE_SECRET_SIZE);
+
+      if(len != NSD_COOKIE_SECRET_SIZE) {
+        yyerror("expected a 128 bit hex string");
+      } else {
+        cfg_parser->opt->cookie_secret = region_strdup(cfg_parser->opt->region, $2);
+      }
+    }
+  | VAR_COOKIE_STAGING_SECRET STRING
+    {
+      uint8_t secret[32];
+      ssize_t len = hex_pton($2, secret, NSD_COOKIE_SECRET_SIZE);
+
+      if(len != NSD_COOKIE_SECRET_SIZE) {
+        yyerror("expected a 128 bit hex string");
+      } else {
+        cfg_parser->opt->cookie_staging_secret = region_strdup(cfg_parser->opt->region, $2);
+      }
+    }
   | VAR_COOKIE_SECRET_FILE STRING
-    { cfg_parser->opt->cookie_secret_file = region_strdup(cfg_parser->opt->region, $2); }
+    {
+      /* Empty filename means explicitly disabled cookies from file, internally
+       * represented as NULL.
+       * Note that after parsing, if no value was configured, then
+       * cookie_secret_file_is_default is still 1, then the default cookie
+       * secret file value will be assigned to cookie_secret_file.
+       */
+      if(*$2) cfg_parser->opt->cookie_secret_file = region_strdup(cfg_parser->opt->region, $2);
+      cfg_parser->opt->cookie_secret_file_is_default = 0;
+    }
+    
   | VAR_XFRD_TCP_MAX number
     { cfg_parser->opt->xfrd_tcp_max = (int)$2; }
   | VAR_XFRD_TCP_PIPELINE number
@@ -921,7 +978,7 @@ pattern_or_zone_option:
         yyerror("address range used for request-xfr");
       append_acl(&cfg_parser->pattern->request_xfr, acl);
     }
-	tlsauth_option
+	request_xfr_tlsauth_option
 	{ }
   | VAR_REQUEST_XFR VAR_AXFR STRING STRING
     {
@@ -933,7 +990,7 @@ pattern_or_zone_option:
         yyerror("address range used for request-xfr");
       append_acl(&cfg_parser->pattern->request_xfr, acl);
     }
-	tlsauth_option
+	request_xfr_tlsauth_option
 	{ }
   | VAR_REQUEST_XFR VAR_UDP STRING STRING
     {
@@ -964,6 +1021,8 @@ pattern_or_zone_option:
       acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
       append_acl(&cfg_parser->pattern->provide_xfr, acl);
     }
+	provide_xfr_tlsauth_option
+	{ }
   | VAR_ALLOW_QUERY STRING STRING
     {
       acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
@@ -1174,10 +1233,15 @@ boolean:
       }
     } ;
 
-tlsauth_option:
+request_xfr_tlsauth_option:
 	| STRING
 	{ char *tls_auth_name = region_strdup(cfg_parser->opt->region, $1);
 	  add_to_last_acl(&cfg_parser->pattern->request_xfr, tls_auth_name);} ;
+
+provide_xfr_tlsauth_option:
+	| STRING
+	{ char *tls_auth_name = region_strdup(cfg_parser->opt->region, $1);
+	  add_to_last_acl(&cfg_parser->pattern->provide_xfr, tls_auth_name);} ;
 
 catalog_role:
     STRING
Index: configure
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/configure,v
diff -u -p -r1.61 configure
--- configure	15 Apr 2024 12:44:24 -0000	1.61
+++ configure	3 Sep 2025 13:53:31 -0000
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for NSD 4.9.1.
+# Generated by GNU Autoconf 2.71 for NSD 4.11.0.
 #
 # Report bugs to <https://github.com/NLnetLabs/nsd/issues or nsd-bugs@nlnetlabs.nl>.
 #
@@ -612,8 +612,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='NSD'
 PACKAGE_TARNAME='nsd'
-PACKAGE_VERSION='4.9.1'
-PACKAGE_STRING='NSD 4.9.1'
+PACKAGE_VERSION='4.11.0'
+PACKAGE_STRING='NSD 4.11.0'
 PACKAGE_BUGREPORT='https://github.com/NLnetLabs/nsd/issues or nsd-bugs@nlnetlabs.nl'
 PACKAGE_URL=''
 
@@ -650,7 +650,9 @@ ac_includes_default="\
 
 ac_header_c_list=
 ac_func_c_list=
+enable_option_checking=no
 ac_subst_vars='LTLIBOBJS
+subdirs
 SYSTEMD_DAEMON_LIBS
 SYSTEMD_DAEMON_CFLAGS
 SYSTEMD_LIBS
@@ -687,6 +689,7 @@ LEX_OUTPUT_ROOT
 user
 chrootdir
 xfrdir
+cookiesecretsfile
 zonelistfile
 xfrdfile
 zonesdir
@@ -758,6 +761,7 @@ with_dbfile
 with_zonesdir
 with_xfrdfile
 with_zonelistfile
+with_cookiesecretsfile
 with_xfrdir
 with_chroot
 with_user
@@ -790,6 +794,8 @@ with_protobuf_c
 with_libfstrm
 enable_systemd
 enable_tcp_fastopen
+enable_westmere
+enable_haswell
 '
       ac_precious_vars='build_alias
 host_alias
@@ -814,7 +820,7 @@ SYSTEMD_CFLAGS
 SYSTEMD_LIBS
 SYSTEMD_DAEMON_CFLAGS
 SYSTEMD_DAEMON_LIBS'
-
+ac_subdirs_all='simdzone'
 
 # Initialize some variables set by options.
 ac_init_help=
@@ -1362,7 +1368,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures NSD 4.9.1 to adapt to many kinds of systems.
+\`configure' configures NSD 4.11.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1428,7 +1434,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of NSD 4.9.1:";;
+     short | recursive ) echo "Configuration of NSD 4.11.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1472,6 +1478,8 @@ Optional Features:
   --enable-dnstap         Enable dnstap support (requires fstrm, protobuf-c)
   --enable-systemd        compile with systemd support
   --enable-tcp-fastopen   Enable TCP Fast Open
+  --disable-westmere      Disable Westmere (SSE4.2) parser kernel
+  --disable-haswell       Disable Haswell (AVX2) parser kernel
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
@@ -1486,6 +1494,8 @@ Optional Packages:
   --with-xfrdfile=path    Pathname to the NSD xfrd zone timer state file
   --with-zonelistfile=path
                           Pathname to the NSD zone list file
+  --with-cookiesecretsfile=path
+                          Pathname to the NSD cookie secrets file
   --with-xfrdir=path      Pathname to where the NSD transfer dir is created
   --with-chroot=dir       NSD default chroot directory
   --with-user=username    User name or ID to answer the queries with
@@ -1604,7 +1614,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-NSD configure 4.9.1
+NSD configure 4.11.0
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2261,7 +2271,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by NSD $as_me 4.9.1, which was
+It was created by NSD $as_me 4.11.0, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -4367,6 +4377,22 @@ _ACEOF
 
 
 
+# default cookiesecrets file location.
+cookiesecretsfile=${dbdir}/cookiesecrets.txt
+
+# Check whether --with-cookiesecretsfile was given.
+if test ${with_cookiesecretsfile+y}
+then :
+  withval=$with_cookiesecretsfile; cookiesecretsfile=$withval
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define COOKIESECRETSFILE "`eval echo $cookiesecretsfile`"
+_ACEOF
+
+
+
 # default xfr dir location.
 xfrdir="/tmp"
 
@@ -10687,8 +10713,8 @@ fi
     if test x_$withval != x_no; then
         { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SSL" >&5
 printf %s "checking for SSL... " >&6; }
-	if test -n "$withval"; then
-        	                                                                if test ! -f "$withval/include/openssl/ssl.h" -a -f "$withval/openssl/ssl.h"; then
+        if test -n "$withval"; then
+                                                                if test ! -f "$withval/include/openssl/ssl.h" -a -f "$withval/openssl/ssl.h"; then
                         ssldir="$withval"
                         found_ssl="yes"
                         withval=""
@@ -10706,7 +10732,7 @@ printf %s "checking for SSL... " >&6; }
                                 fi
                         fi
                 fi
-	fi
+            fi
         if test x_$withval = x_ -o x_$withval = x_yes; then
             withval="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/sfw /usr/local /usr /usr/local/opt/openssl"
         fi
@@ -10717,12 +10743,12 @@ printf %s "checking for SSL... " >&6; }
                 if test x_$ssldir != x_/usr; then
                     CPPFLAGS="$CPPFLAGS -I$ssldir/include";
                 fi
-		ssldir_include="$ssldir/include"
-		if test ! -d "$ssldir/lib" -a -d "$ssldir/lib64"; then
-			ssldir_lib="$ssldir/lib64"
-		else
-			ssldir_lib="$ssldir/lib"
-		fi
+                ssldir_include="$ssldir/include"
+                if test ! -d "$ssldir/lib" -a -d "$ssldir/lib64"; then
+                    ssldir_lib="$ssldir/lib64"
+                else
+                    ssldir_lib="$ssldir/lib"
+                fi
                 break;
             fi
         done
@@ -10738,9 +10764,9 @@ printf "%s\n" "#define HAVE_SSL /**/" >>
             if test x_$ssldir != x_/usr; then
                 LDFLAGS="$LDFLAGS -L$ssldir_lib";
             fi
-	    if test x_$ssldir = x_/usr/sfw; then
-		LDFLAGS="$LDFLAGS -R$ssldir_lib";
-	    fi
+            if test x_$ssldir = x_/usr/sfw; then
+                LDFLAGS="$LDFLAGS -R$ssldir_lib";
+            fi
         fi
 
     fi
@@ -10993,6 +11019,13 @@ then :
   printf "%s\n" "#define HAVE_OPENSSL_CORE_NAMES_H 1" >>confdefs.h
 
 fi
+ac_fn_c_check_header_compile "$LINENO" "openssl/x509v3.h" "ac_cv_header_openssl_x509v3_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_openssl_x509v3_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_OPENSSL_X509V3_H 1" >>confdefs.h
+
+fi
 
 	ac_fn_c_check_func "$LINENO" "HMAC_CTX_reset" "ac_cv_func_HMAC_CTX_reset"
 if test "x$ac_cv_func_HMAC_CTX_reset" = xyes
@@ -11060,6 +11093,12 @@ then :
   printf "%s\n" "#define HAVE_SHA1_INIT 1" >>confdefs.h
 
 fi
+ac_fn_c_check_func "$LINENO" "ASN1_STRING_get0_data" "ac_cv_func_ASN1_STRING_get0_data"
+if test "x$ac_cv_func_ASN1_STRING_get0_data" = xyes
+then :
+  printf "%s\n" "#define HAVE_ASN1_STRING_GET0_DATA 1" >>confdefs.h
+
+fi
 
 	if test "$ac_cv_func_SHA1_Init" = "yes"; then
 
@@ -11120,6 +11159,9 @@ $ac_includes_default
 #endif
 #include <openssl/ssl.h>
 #include <openssl/evp.h>
+#ifdef HAVE_OPENSSL_X509V3_h
+#include <openssl/x509v3.h>
+#endif
 
 " "$ac_c_undeclared_builtin_options" "CFLAGS"
 if test "x$ac_cv_have_decl_SSL_CTX_set_ecdh_auto" = xyes
@@ -11148,6 +11190,9 @@ $ac_includes_default
 #endif
 #include <openssl/ssl.h>
 #include <openssl/evp.h>
+#ifdef HAVE_OPENSSL_X509V3_h
+#include <openssl/x509v3.h>
+#endif
 
 " "$ac_c_undeclared_builtin_options" "CFLAGS"
 if test "x$ac_cv_have_decl_SSL_CTX_set_tmp_ecdh" = xyes
@@ -12081,6 +12126,24 @@ fi
 
 ac_config_files="$ac_config_files Makefile $dnstap_config"
 
+
+# Arguments introduced specifically for simdzone.
+# Check whether --enable-westmere was given.
+if test ${enable_westmere+y}
+then :
+  enableval=$enable_westmere;
+fi
+
+# Check whether --enable-haswell was given.
+if test ${enable_haswell+y}
+then :
+  enableval=$enable_haswell;
+fi
+
+
+
+subdirs="$subdirs simdzone"
+
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
 # tests run on this system so they can be shared between configure
@@ -12580,7 +12643,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_wri
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by NSD $as_me 4.9.1, which was
+This file was extended by NSD $as_me 4.11.0, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -12644,7 +12707,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-NSD config.status 4.9.1
+NSD config.status 4.11.0
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 
@@ -13350,6 +13413,149 @@ if test "$no_create" != yes; then
   # Use ||, not &&, to avoid exiting from the if with $? = 1, which
   # would make configure fail if this is the last instruction.
   $ac_cs_success || as_fn_exit 1
+fi
+
+#
+# CONFIG_SUBDIRS section.
+#
+if test "$no_recursion" != yes; then
+
+  # Remove --cache-file, --srcdir, and --disable-option-checking arguments
+  # so they do not pile up.
+  ac_sub_configure_args=
+  ac_prev=
+  eval "set x $ac_configure_args"
+  shift
+  for ac_arg
+  do
+    if test -n "$ac_prev"; then
+      ac_prev=
+      continue
+    fi
+    case $ac_arg in
+    -cache-file | --cache-file | --cache-fil | --cache-fi \
+    | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+      ac_prev=cache_file ;;
+    -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+    | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* \
+    | --c=*)
+      ;;
+    --config-cache | -C)
+      ;;
+    -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+      ac_prev=srcdir ;;
+    -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+      ;;
+    -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+      ac_prev=prefix ;;
+    -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+      ;;
+    --disable-option-checking)
+      ;;
+    *)
+      case $ac_arg in
+      *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+      esac
+      as_fn_append ac_sub_configure_args " '$ac_arg'" ;;
+    esac
+  done
+
+  # Always prepend --prefix to ensure using the same prefix
+  # in subdir configurations.
+  ac_arg="--prefix=$prefix"
+  case $ac_arg in
+  *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+  esac
+  ac_sub_configure_args="'$ac_arg' $ac_sub_configure_args"
+
+  # Pass --silent
+  if test "$silent" = yes; then
+    ac_sub_configure_args="--silent $ac_sub_configure_args"
+  fi
+
+  # Always prepend --disable-option-checking to silence warnings, since
+  # different subdirs can have different --enable and --with options.
+  ac_sub_configure_args="--disable-option-checking $ac_sub_configure_args"
+
+  ac_popdir=`pwd`
+  for ac_dir in : $subdirs; do test "x$ac_dir" = x: && continue
+
+    # Do not complain, so a configure script can configure whichever
+    # parts of a large source tree are present.
+    test -d "$srcdir/$ac_dir" || continue
+
+    ac_msg="=== configuring in $ac_dir (`pwd`/$ac_dir)"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_msg" >&5
+    printf "%s\n" "$ac_msg" >&6
+    as_dir="$ac_dir"; as_fn_mkdir_p
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+    cd "$ac_dir"
+
+    # Check for configure.gnu first; this name is used for a wrapper for
+    # Metaconfig's "Configure" on case-insensitive file systems.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      ac_sub_configure=$ac_srcdir/configure.gnu
+    elif test -f "$ac_srcdir/configure"; then
+      ac_sub_configure=$ac_srcdir/configure
+    else
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: no configuration information is in $ac_dir" >&5
+printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2;}
+      ac_sub_configure=
+    fi
+
+    # The recursion is here.
+    if test -n "$ac_sub_configure"; then
+      # Make the cache file name correct relative to the subdirectory.
+      case $cache_file in
+      [\\/]* | ?:[\\/]* ) ac_sub_cache_file=$cache_file ;;
+      *) # Relative name.
+	ac_sub_cache_file=$ac_top_build_prefix$cache_file ;;
+      esac
+
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: running $SHELL $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&5
+printf "%s\n" "$as_me: running $SHELL $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&6;}
+      # The eval makes quoting arguments work.
+      eval "\$SHELL \"\$ac_sub_configure\" $ac_sub_configure_args \
+	   --cache-file=\"\$ac_sub_cache_file\" --srcdir=\"\$ac_srcdir\"" ||
+	as_fn_error $? "$ac_sub_configure failed for $ac_dir" "$LINENO" 5
+    fi
+
+    cd "$ac_popdir"
+  done
 fi
 if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
Index: configure.ac
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/configure.ac,v
diff -u -p -r1.60 configure.ac
--- configure.ac	15 Apr 2024 12:44:24 -0000	1.60
+++ configure.ac	3 Sep 2025 13:53:31 -0000
@@ -5,7 +5,9 @@ dnl
 sinclude(acx_nlnetlabs.m4)
 sinclude(dnstap/dnstap.m4)
 
-AC_INIT([NSD],[4.9.1],[https://github.com/NLnetLabs/nsd/issues or nsd-bugs@nlnetlabs.nl])
+# autoconf-2.70 is needed for @runstatedir@
+AC_PREREQ([2.70])
+AC_INIT([NSD],[4.11.0],[https://github.com/NLnetLabs/nsd/issues or nsd-bugs@nlnetlabs.nl])
 AC_CONFIG_HEADERS([config.h])
 
 #
@@ -124,6 +126,12 @@ AC_ARG_WITH([zonelistfile], AS_HELP_STRI
 AC_DEFINE_UNQUOTED(ZONELISTFILE, ["`eval echo $zonelistfile`"], [Pathname to the NSD zone list file.])
 AC_SUBST(zonelistfile)
 
+# default cookiesecrets file location.
+cookiesecretsfile=${dbdir}/cookiesecrets.txt
+AC_ARG_WITH([cookiesecretsfile], AS_HELP_STRING([--with-cookiesecretsfile=path],[Pathname to the NSD cookie secrets file]), [cookiesecretsfile=$withval])
+AC_DEFINE_UNQUOTED(COOKIESECRETSFILE, ["`eval echo $cookiesecretsfile`"], [Pathname to the NSD cookies secrets file.])
+AC_SUBST(cookiesecretsfile)
+
 # default xfr dir location.
 xfrdir="/tmp"
 AC_ARG_WITH([xfrdir], AS_HELP_STRING([--with-xfrdir=path],[Pathname to where the NSD transfer dir is created]), [xfrdir=$withval])
@@ -360,11 +368,11 @@ AC_DEFUN([CHECK_SSL], [
         ])
     if test x_$withval != x_no; then
         AC_MSG_CHECKING(for SSL)
-	if test -n "$withval"; then
-        	dnl look for openssl install with different version, eg.
-                dnl in /usr/include/openssl11/openssl/ssl.h
-                dnl and /usr/lib64/openssl11/libssl.so
-                dnl with the --with-ssl=/usr/include/openssl11
+        if test -n "$withval"; then
+            dnl look for openssl install with different version, eg.
+            dnl in /usr/include/openssl11/openssl/ssl.h
+            dnl and /usr/lib64/openssl11/libssl.so
+            dnl with the --with-ssl=/usr/include/openssl11
                 if test ! -f "$withval/include/openssl/ssl.h" -a -f "$withval/openssl/ssl.h"; then
                         ssldir="$withval"
                         found_ssl="yes"
@@ -384,7 +392,7 @@ AC_DEFUN([CHECK_SSL], [
                                 fi
                         fi
                 fi
-	fi
+            fi
         if test x_$withval = x_ -o x_$withval = x_yes; then
             withval="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/sfw /usr/local /usr /usr/local/opt/openssl"
         fi
@@ -395,12 +403,12 @@ AC_DEFUN([CHECK_SSL], [
                 if test x_$ssldir != x_/usr; then
                     CPPFLAGS="$CPPFLAGS -I$ssldir/include";
                 fi
-		ssldir_include="$ssldir/include"
-		if test ! -d "$ssldir/lib" -a -d "$ssldir/lib64"; then
-			ssldir_lib="$ssldir/lib64"
-		else
-			ssldir_lib="$ssldir/lib"
-		fi
+                ssldir_include="$ssldir/include"
+                if test ! -d "$ssldir/lib" -a -d "$ssldir/lib64"; then
+                    ssldir_lib="$ssldir/lib64"
+                else
+                    ssldir_lib="$ssldir/lib"
+                fi
                 break;
             fi
         done
@@ -413,9 +421,9 @@ AC_DEFUN([CHECK_SSL], [
             if test x_$ssldir != x_/usr; then
                 LDFLAGS="$LDFLAGS -L$ssldir_lib";
             fi
-	    if test x_$ssldir = x_/usr/sfw; then
-		LDFLAGS="$LDFLAGS -R$ssldir_lib";
-	    fi
+            if test x_$ssldir = x_/usr/sfw; then
+                LDFLAGS="$LDFLAGS -R$ssldir_lib";
+            fi
         fi
         AC_SUBST(HAVE_SSL)
     fi
@@ -1075,8 +1083,8 @@ if test x$HAVE_SSL = x"yes"; then
 	fi
 	SSL_LIBS="-lssl"
 	AC_SUBST(SSL_LIBS)
-	AC_CHECK_HEADERS([openssl/ssl.h openssl/err.h openssl/rand.h openssl/ocsp.h openssl/core_names.h],,, [AC_INCLUDES_DEFAULT])
-	AC_CHECK_FUNCS([HMAC_CTX_reset HMAC_CTX_new EVP_cleanup ERR_load_crypto_strings OPENSSL_init_crypto CRYPTO_memcmp EC_KEY_new_by_curve_name EVP_MAC_CTX_new EVP_MAC_CTX_set_params EVP_MAC_CTX_get_mac_size SHA1_Init])
+	AC_CHECK_HEADERS([openssl/ssl.h openssl/err.h openssl/rand.h openssl/ocsp.h openssl/core_names.h openssl/x509v3.h],,, [AC_INCLUDES_DEFAULT])
+	AC_CHECK_FUNCS([HMAC_CTX_reset HMAC_CTX_new EVP_cleanup ERR_load_crypto_strings OPENSSL_init_crypto CRYPTO_memcmp EC_KEY_new_by_curve_name EVP_MAC_CTX_new EVP_MAC_CTX_set_params EVP_MAC_CTX_get_mac_size SHA1_Init ASN1_STRING_get0_data])
 	if test "$ac_cv_func_SHA1_Init" = "yes"; then
 		ACX_FUNC_DEPRECATED([SHA1_Init], [(void)SHA1_Init(NULL);], [
 #include <openssl/sha.h>
@@ -1101,6 +1109,9 @@ AC_INCLUDES_DEFAULT
 #endif
 #include <openssl/ssl.h>
 #include <openssl/evp.h>
+#ifdef HAVE_OPENSSL_X509V3_h
+#include <openssl/x509v3.h>
+#endif
 ])
 	AC_CHECK_DECL([TLS1_3_VERSION], 
 		[AC_DEFINE([HAVE_TLS_1_3], [1], [Define if TLS 1.3 is supported by OpenSSL])],
@@ -1427,6 +1438,11 @@ if test "$enable_checking" = "yes"; then
 fi
 
 AC_CONFIG_FILES([Makefile $dnstap_config])
+
+# Arguments introduced specifically for simdzone.
+AC_ARG_ENABLE(westmere, AS_HELP_STRING([--disable-westmere], [Disable Westmere (SSE4.2) parser kernel]))
+AC_ARG_ENABLE(haswell, AS_HELP_STRING([--disable-haswell], [Disable Haswell (AVX2) parser kernel]))
+AC_CONFIG_SUBDIRS([simdzone])
 AC_OUTPUT
 m4_unquote(
   _m4_defn([_m4_wrap_text])_m4_popdef([_m4_wrap_text]))
Index: dbaccess.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/dbaccess.c,v
diff -u -p -r1.12 dbaccess.c
--- dbaccess.c	20 Dec 2023 17:29:01 -0000	1.12
+++ dbaccess.c	3 Sep 2025 13:53:31 -0000
@@ -35,7 +35,6 @@ void
 namedb_close(struct namedb* db)
 {
 	if(db) {
-		zonec_desetup_parser();
 		region_destroy(db->region);
 	}
 }
@@ -76,6 +75,8 @@ namedb_zone_create(namedb_type* db, cons
 	zone->opts = zo;
 	zone->ixfr = NULL;
 	zone->filename = NULL;
+	zone->includes.count = 0;
+	zone->includes.paths = NULL;
 	zone->logstr = NULL;
 	zone->mtime.tv_sec = 0;
 	zone->mtime.tv_nsec = 0;
@@ -91,6 +92,34 @@ namedb_zone_create(namedb_type* db, cons
 }
 
 void
+namedb_zone_free_filenames(namedb_type *db, zone_type* zone)
+{
+	assert(!zone->includes.paths == !zone->includes.count);
+
+	if (zone->filename) {
+		region_recycle(
+			db->region, zone->filename, strlen(zone->filename) + 1);
+		zone->filename = NULL;
+	}
+
+	if (zone->includes.count) {
+		for (size_t i=0; i < zone->includes.count; i++) {
+			region_recycle(
+				db->region,
+				zone->includes.paths[i],
+				strlen(zone->includes.paths[i]) + 1);
+		}
+
+		region_recycle(
+			db->region,
+			zone->includes.paths,
+			zone->includes.count * sizeof(*zone->includes.paths));
+		zone->includes.count = 0;
+		zone->includes.paths = NULL;
+	}
+}
+
+void
 namedb_zone_delete(namedb_type* db, zone_type* zone)
 {
 	/* RRs and UDB and NSEC3 and so on must be already deleted */
@@ -120,9 +149,7 @@ namedb_zone_delete(namedb_type* db, zone
 	hash_tree_delete(db->region, zone->dshashtree);
 #endif
 	zone_ixfr_free(zone->ixfr);
-	if(zone->filename)
-		region_recycle(db->region, zone->filename,
-			strlen(zone->filename)+1);
+	namedb_zone_free_filenames(db, zone);
 	if(zone->logstr)
 		region_recycle(db->region, zone->logstr,
 			strlen(zone->logstr)+1);
@@ -155,7 +182,6 @@ namedb_open (struct nsd_options* opt)
 	db->zonetree = radix_tree_create(db->region);
 	db->diff_skip = 0;
 	db->diff_pos = 0;
-	zonec_setup_parser(db);
 
 	if (gettimeofday(&(db->diff_timestamp), NULL) != 0) {
 		log_msg(LOG_ERR, "unable to load namedb: cannot initialize timestamp");
@@ -238,12 +264,27 @@ namedb_read_zonefile(struct nsd* nsd, st
 		/* if zone_fname, then the file was acquired from reading it,
 		 * and see if filename changed or mtime newer to read it */
 		} else if(zone_fname && strcmp(zone_fname, fname) == 0 &&
-		   timespec_compare(&zone_mtime, &mtime) == 0) {
-			VERBOSITY(3, (LOG_INFO, "zonefile %s is not modified",
-				fname));
-			return;
+			timespec_compare(&zone_mtime, &mtime) == 0) {
+			int changed = 0;
+			struct timespec include_mtime;
+			/* one of the includes may have been deleted, changed, etc */
+			for (size_t i=0; i < zone->includes.count; i++) {
+				if (!file_get_mtime(zone->includes.paths[i], &include_mtime, &nonexist)) {
+					changed = 1;
+				} else if (timespec_compare(&zone_mtime, &include_mtime) < 0) {
+					mtime = include_mtime;
+					changed = 1;
+				}
+			}
+
+			if (!changed) {
+				VERBOSITY(3, (LOG_INFO, "zonefile %s is not modified",
+					fname));
+				return;
+			}
 		}
 	}
+
 	if(ixfr_create_from_difference(zone, fname,
 		&ixfr_create_already_done)) {
 		ixfrcr = ixfr_create_start(zone, fname,
@@ -254,14 +295,18 @@ namedb_read_zonefile(struct nsd* nsd, st
 		}
 	}
 
-	assert(parser);
+	namedb_zone_free_filenames(nsd->db, zone);
+	zone->filename = region_strdup(nsd->db->region, fname);
+
 	/* wipe zone from memory */
 #ifdef NSEC3
 	nsec3_clear_precompile(nsd->db, zone);
 	zone->nsec3_param = NULL;
 #endif
 	delete_zone_rrs(nsd->db, zone);
-	errors = zonec_read(zone->opts->name, fname, zone);
+	VERBOSITY(5, (LOG_INFO, "zone %s zonec_read(%s)",
+		zone->opts->name, fname));
+	errors = zonec_read(nsd->db, nsd->db->domains, zone->opts->name, fname, zone);
 	if(errors > 0) {
 		log_msg(LOG_ERR, "zone %s file %s read with %u errors",
 			zone->opts->name, fname, errors);
@@ -272,10 +317,7 @@ namedb_read_zonefile(struct nsd* nsd, st
 		zone->nsec3_param = NULL;
 #endif
 		delete_zone_rrs(nsd->db, zone);
-		if(zone->filename)
-			region_recycle(nsd->db->region, zone->filename,
-				strlen(zone->filename)+1);
-		zone->filename = NULL;
+		namedb_zone_free_filenames(nsd->db, zone);
 		if(zone->logstr)
 			region_recycle(nsd->db->region, zone->logstr,
 				strlen(zone->logstr)+1);
@@ -287,10 +329,6 @@ namedb_read_zonefile(struct nsd* nsd, st
 		zone->is_changed = 0;
 		/* store zone into udb */
 		zone->mtime = mtime;
-		if(zone->filename)
-			region_recycle(nsd->db->region, zone->filename,
-				strlen(zone->filename)+1);
-		zone->filename = region_strdup(nsd->db->region, fname);
 		if(zone->logstr)
 			region_recycle(nsd->db->region, zone->logstr,
 				strlen(zone->logstr)+1);
Index: dbcreate.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/dbcreate.c,v
diff -u -p -r1.8 dbcreate.c
--- dbcreate.c	20 Dec 2023 17:29:01 -0000	1.8
+++ dbcreate.c	3 Sep 2025 13:53:31 -0000
@@ -256,6 +256,8 @@ namedb_write_zonefile(struct nsd* nsd, s
 			return;
 		}
 		zone->is_changed = 0;
+		VERBOSITY(3, (LOG_INFO, "zone %s written to file %s",
+			zone->opts->name, zfile));
 		/* fetch the mtime of the just created zonefile so we
 		 * do not waste effort reading it back in */
 		if(!file_get_mtime(zfile, &mtime, &notexist)) {
Index: difffile.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/difffile.c,v
diff -u -p -r1.22 difffile.c
--- difffile.c	12 Apr 2024 15:53:34 -0000	1.22
+++ difffile.c	3 Sep 2025 13:53:31 -0000
@@ -1276,8 +1276,7 @@ check_for_bad_serial(namedb_type* db, co
 
 int
 apply_ixfr_for_zone(nsd_type* nsd, zone_type* zone, FILE* in,
-	struct nsd_options* ATTR_UNUSED(opt), udb_base* taskudb, udb_ptr* last_task,
-	uint32_t xfrfilenr)
+	struct nsd_options* ATTR_UNUSED(opt), udb_base* taskudb, uint32_t xfrfilenr)
 {
 	char zone_buf[3072];
 	char log_buf[5120];
@@ -1289,7 +1288,6 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_
 	uint8_t committed;
 	uint32_t i;
 	int num_bytes = 0;
-	(void)last_task;
 	assert(zone);
 
 	/* read zone name and serial */
@@ -1396,10 +1394,7 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_
 			region_recycle(nsd->db->region, zone->logstr,
 				strlen(zone->logstr)+1);
 		zone->logstr = region_strdup(nsd->db->region, log_buf);
-		if(zone->filename)
-			region_recycle(nsd->db->region, zone->filename,
-				strlen(zone->filename)+1);
-		zone->filename = NULL;
+		namedb_zone_free_filenames(nsd->db, zone);
 		if(softfail && taskudb && !is_axfr) {
 			log_msg(LOG_ERR, "Failed to apply IXFR cleanly "
 				"(deletes nonexistent RRs, adds existing RRs). "
@@ -1714,44 +1709,25 @@ void task_new_del_key(udb_base* udb, udb
 	udb_ptr_unlink(&e, udb);
 }
 
-void task_new_add_cookie_secret(udb_base* udb, udb_ptr* last,
-                                 const char* secret) {
+void task_new_cookies(udb_base* udb, udb_ptr* last, int answer_cookie,
+		size_t cookie_count, void* cookie_secrets) {
 	udb_ptr e;
 	char* p;
-	size_t const secret_size = strlen(secret) + 1;
+	size_t const secrets_size = sizeof(cookie_secrets_type);
 
-	DEBUG(DEBUG_IPC, 1, (LOG_INFO, "add task add_cookie_secret"));
+	DEBUG(DEBUG_IPC, 1, (LOG_INFO, "add task cookies"));
 
 	if(!task_create_new_elem(udb, last, &e,
-	                         sizeof(struct task_list_d) + secret_size, NULL)) {
-		log_msg(LOG_ERR, "tasklist: out of space, cannot add add_cookie_secret");
+			sizeof(struct task_list_d) + secrets_size, NULL)) {
+		log_msg(LOG_ERR, "tasklist: out of space, cannot add cookies");
 		return;
 	}
-	TASKLIST(&e)->task_type = task_add_cookie_secret;
+	TASKLIST(&e)->task_type = task_cookies;
+	TASKLIST(&e)->newserial = (uint32_t) answer_cookie;
+	TASKLIST(&e)->yesno = (uint64_t) cookie_count;
 	p = (char*)TASKLIST(&e)->zname;
-	memmove(p, secret, secret_size);
-	udb_ptr_unlink(&e, udb);
-}
+	memmove(p, cookie_secrets, secrets_size);
 
-void task_new_drop_cookie_secret(udb_base* udb, udb_ptr* last) {
-	udb_ptr e;
-	DEBUG(DEBUG_IPC, 1, (LOG_INFO, "add task drop_cookie_secret"));
-	if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d), NULL)) {
-		log_msg(LOG_ERR, "tasklist: out of space, cannot add drop_cookie_secret");
-		return;
-	}
-	TASKLIST(&e)->task_type = task_drop_cookie_secret;
-	udb_ptr_unlink(&e, udb);
-}
-
-void task_new_activate_cookie_secret(udb_base* udb, udb_ptr* last) {
-	udb_ptr e;
-	DEBUG(DEBUG_IPC, 1, (LOG_INFO, "add task activate_cookie_secret"));
-	if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d), NULL)) {
-		log_msg(LOG_ERR, "tasklist: out of space, cannot add activate_cookie_secret");
-		return;
-	}
-	TASKLIST(&e)->task_type = task_activate_cookie_secret;
 	udb_ptr_unlink(&e, udb);
 }
 
@@ -1988,53 +1964,14 @@ task_process_del_key(struct nsd* nsd, st
 }
 
 static void
-task_process_add_cookie_secret(struct nsd* nsd, struct task_list_d* task) {
-	uint8_t secret_tmp[NSD_COOKIE_SECRET_SIZE];
-	ssize_t decoded_len;
-	char* secret = (char*)task->zname;
-
-	DEBUG(DEBUG_IPC, 1, (LOG_INFO, "add_cookie_secret task %s", secret));
-
-	if( strlen(secret) != 32 ) {
-		log_msg(LOG_ERR, "invalid cookie secret: %s", secret);
-		explicit_bzero(secret, strlen(secret));
-		return;
-	}
-
-	decoded_len = hex_pton(secret, secret_tmp, NSD_COOKIE_SECRET_SIZE);
-	if( decoded_len != 16 ) {
-		explicit_bzero(secret_tmp, NSD_COOKIE_SECRET_SIZE);
-		log_msg(LOG_ERR, "unable to parse cookie secret: %s", secret);
-		explicit_bzero(secret, strlen(secret));
-		return;
-	}
-	explicit_bzero(secret, strlen(secret));
-	add_cookie_secret(nsd, secret_tmp);
-	explicit_bzero(secret_tmp, NSD_COOKIE_SECRET_SIZE);
-}
-
-static void
-task_process_drop_cookie_secret(struct nsd* nsd, struct task_list_d* task)
-{
-	(void)task;
-	DEBUG(DEBUG_IPC, 1, (LOG_INFO, "drop_cookie_secret task"));
-	if( nsd->cookie_count <= 1 ) {
-		log_msg(LOG_ERR, "can not drop the only active cookie secret");
-		return;
-	}
-	drop_cookie_secret(nsd);
-}
-
-static void
-task_process_activate_cookie_secret(struct nsd* nsd, struct task_list_d* task)
-{
-	(void)task;
-	DEBUG(DEBUG_IPC, 1, (LOG_INFO, "activate_cookie_secret task"));
-	if( nsd->cookie_count <= 1 ) {
-		log_msg(LOG_ERR, "can not activate the only active cookie secret");
-		return;
-	}
-	activate_cookie_secret(nsd);
+task_process_cookies(struct nsd* nsd, struct task_list_d* task) {
+	DEBUG(DEBUG_IPC, 1, (LOG_INFO, "cookies task answer: %s, count: %d",
+		task->newserial ? "yes" : "no", (int)task->yesno));
+	
+	nsd->do_answer_cookie = (int) task->newserial;
+	nsd->cookie_count = (size_t) task->yesno;
+	memmove(nsd->cookie_secrets, task->zname, sizeof(nsd->cookie_secrets));
+	explicit_bzero(task->zname, sizeof(nsd->cookie_secrets));
 }
 
 static void
@@ -2085,9 +2022,8 @@ task_process_zonestat_inc(struct nsd* ns
 }
 #endif
 
-static void
-task_process_apply_xfr(struct nsd* nsd, udb_base* udb, udb_ptr *last_task,
-	udb_ptr* task)
+void
+task_process_apply_xfr(struct nsd* nsd, udb_base* udb, udb_ptr* task)
 {
 	/* we have to use an udb_ptr task here, because the apply_xfr procedure
 	 * appends soa_info which may remap and change the pointer. */
@@ -2099,6 +2035,7 @@ task_process_apply_xfr(struct nsd* nsd, 
 	if(!zone) {
 		/* assume the zone has been deleted and a zone transfer was
 		 * still waiting to be processed */
+		udb_ptr_free_space(task, udb, TASKLIST(task)->size);
 		return;
 	}
 
@@ -2110,10 +2047,11 @@ task_process_apply_xfr(struct nsd* nsd, 
 		/* soainfo_gone will be communicated from server_reload, unless
 		   preceding updates have been applied */
 		zone->is_skipped = 1;
+		udb_ptr_free_space(task, udb, TASKLIST(task)->size);
 		return;
 	}
 	/* read and apply zone transfer */
-	switch(apply_ixfr_for_zone(nsd, zone, df, nsd->options, udb, last_task,
+	switch(apply_ixfr_for_zone(nsd, zone, df, nsd->options, udb,
 				TASKLIST(task)->yesno)) {
 	case 1: /* Success */
 		break;
@@ -2131,6 +2069,7 @@ task_process_apply_xfr(struct nsd* nsd, 
 	default:break;
 	}
 	fclose(df);
+	udb_ptr_free_space(task, udb, TASKLIST(task)->size);
 }
 
 
@@ -2176,17 +2115,8 @@ void task_process_in_reload(struct nsd* 
 		task_process_zonestat_inc(nsd, udb, last_task, TASKLIST(task));
 		break;
 #endif
-	case task_apply_xfr:
-		task_process_apply_xfr(nsd, udb, last_task, task);
-		break;
-	case task_add_cookie_secret:
-		task_process_add_cookie_secret(nsd, TASKLIST(task));
-		break;
-	case task_drop_cookie_secret:
-		task_process_drop_cookie_secret(nsd, TASKLIST(task));
-		break;
-	case task_activate_cookie_secret:
-		task_process_activate_cookie_secret(nsd, TASKLIST(task));
+	case task_cookies:
+		task_process_cookies(nsd, TASKLIST(task));
 		break;
 	default:
 		log_msg(LOG_WARNING, "unhandled task in reload type %d",
Index: difffile.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/difffile.h,v
diff -u -p -r1.6 difffile.h
--- difffile.h	12 Apr 2024 15:53:34 -0000	1.6
+++ difffile.h	3 Sep 2025 13:53:31 -0000
@@ -68,8 +68,7 @@ int add_RR(namedb_type* db, const dname_
 
 /* apply the xfr file identified by xfrfilenr to zone */
 int apply_ixfr_for_zone(struct nsd* nsd, zone_type* zone, FILE* in,
-        struct nsd_options* opt, udb_base* taskudb, udb_ptr* last_task,
-        uint32_t xfrfilenr);
+        struct nsd_options* opt, udb_base* taskudb, uint32_t xfrfilenr);
 
 enum soainfo_hint {
 	soainfo_ok,
@@ -111,12 +110,8 @@ struct task_list_d {
 		task_opt_change,
 		/** zonestat increment */
 		task_zonestat_inc,
-		/** add a new cookie secret */
-		task_add_cookie_secret,
-		/** drop the oldest cookie secret */
-		task_drop_cookie_secret,
-		/** make staging cookie secret active */
-		task_activate_cookie_secret,
+		/** cookies */
+		task_cookies,
 	} task_type;
 	uint32_t size; /* size of this struct */
 
@@ -152,11 +147,11 @@ void task_new_add_pattern(udb_base* udb,
 void task_new_del_pattern(udb_base* udb, udb_ptr* last, const char* name);
 void task_new_opt_change(udb_base* udb, udb_ptr* last, struct nsd_options* opt);
 void task_new_zonestat_inc(udb_base* udb, udb_ptr* last, unsigned sz);
-void task_new_add_cookie_secret(udb_base* udb, udb_ptr* last, const char* secret);
-void task_new_drop_cookie_secret(udb_base* udb, udb_ptr* last);
-void task_new_activate_cookie_secret(udb_base* udb, udb_ptr* last);
+void task_new_cookies(udb_base* udb, udb_ptr* last, int answer_cookie,
+		size_t cookie_count, void* cookie_secrets);
 int task_new_apply_xfr(udb_base* udb, udb_ptr* last, const dname_type* zone,
 	uint32_t old_serial, uint32_t new_serial, uint64_t filenumber);
+void task_process_apply_xfr(struct nsd* nsd, udb_base* udb, udb_ptr *task);
 void task_process_in_reload(struct nsd* nsd, udb_base* udb, udb_ptr *last_task,
 	udb_ptr* task);
 void task_process_expire(namedb_type* db, struct task_list_d* task);
Index: dname.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/dname.c,v
diff -u -p -r1.15 dname.c
--- dname.c	12 Apr 2024 15:53:34 -0000	1.15
+++ dname.c	3 Sep 2025 13:53:31 -0000
@@ -307,11 +307,15 @@ dname_is_subdomain(const dname_type *lef
 
 
 int
-dname_compare(const dname_type *left, const dname_type *right)
+dname_compare(const void *a, const void *b)
 {
 	int result;
 	uint8_t label_count;
 	uint8_t i;
+	const dname_type *left, *right;
+
+	left = a;
+	right = b;
 
 	assert(left);
 	assert(right);
@@ -584,4 +588,33 @@ int dname_equal_nocase(uint8_t* a, uint8
 		len -= lablen;
 	}
 	return 1;
+}
+
+int
+is_dname_subdomain_of_case(const uint8_t* d, unsigned int len,
+	const uint8_t* d2, unsigned int len2)
+{
+	unsigned int i;
+	if(len < len2)
+		return 0;
+	if(len == len2) {
+		if(memcmp(d, d2, len) == 0)
+			return 1;
+		return 0;
+	}
+	/* so len > len2, for d=a.example.com. and d2=example.com. */
+	/* trailing portion must be exactly name d2. */
+	if(memcmp(d+len-len2, d2, len2) != 0)
+		return 0;
+	/* that must also be a label point */
+	i=0;
+	while(i < len) {
+		if(i == len-len2)
+			return 1;
+		i += d[i];
+		i += 1;
+	}
+
+	/* The trailing portion is not at a label point. */
+	return 0;
 }
Index: dname.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/dname.h,v
diff -u -p -r1.4 dname.h
--- dname.h	12 Apr 2024 15:53:34 -0000	1.4
+++ dname.h	3 Sep 2025 13:53:31 -0000
@@ -181,8 +181,9 @@ dname_label(const dname_type *dname, uin
  * RIGHT.  The comparison is case sensitive.
  *
  * Pre: left != NULL && right != NULL
+ * left and right are dname_type*.
  */
-int dname_compare(const dname_type *left, const dname_type *right);
+int dname_compare(const void *left, const void *right);
 
 
 /*
@@ -394,5 +395,12 @@ char* wiredname2str(const uint8_t* dname
 char* wirelabel2str(const uint8_t* label);
 /** check if two uncompressed dnames of the same total length are equal */
 int dname_equal_nocase(uint8_t* a, uint8_t* b, uint16_t len);
+
+/* Test is the name is a subdomain of the other name. Equal names return true.
+ * Subdomain d of d2 returns true, otherwise false. The names are in
+ * wireformat, uncompressed. Does not perform canonicalization, it is case
+ * sensitive. */
+int is_dname_subdomain_of_case(const uint8_t* d, unsigned int len,
+	const uint8_t* d2, unsigned int len2);
 
 #endif /* DNAME_H */
Index: dns.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/dns.c,v
diff -u -p -r1.21 dns.c
--- dns.c	10 Aug 2021 08:21:30 -0000	1.21
+++ dns.c	3 Sep 2025 13:53:31 -0000
@@ -22,7 +22,6 @@
 
 #include "dns.h"
 #include "zonec.h"
-#include "zparser.h"
 
 /* Taken from RFC 1035, section 3.2.4.  */
 static lookup_table_type dns_rrclasses[] = {
@@ -33,92 +32,93 @@ static lookup_table_type dns_rrclasses[]
 	{ 0, NULL }
 };
 
-static rrtype_descriptor_type rrtype_descriptors[(RRTYPE_DESCRIPTORS_LENGTH+1)] = {
+static rrtype_descriptor_type rrtype_descriptors[(RRTYPE_DESCRIPTORS_LENGTH+2)] = {
 	/* 0 */
-	{ 0, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 0, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 1 */
-	{ TYPE_A, "A", T_A, 1, 1,
+	{ TYPE_A, "A", 1, 1,
 	  { RDATA_WF_A }, { RDATA_ZF_A } },
 	/* 2 */
-	{ TYPE_NS, "NS", T_NS, 1, 1,
+	{ TYPE_NS, "NS", 1, 1,
 	  { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } },
 	/* 3 */
-	{ TYPE_MD, "MD", T_MD, 1, 1,
+	{ TYPE_MD, "MD", 1, 1,
 	  { RDATA_WF_UNCOMPRESSED_DNAME }, { RDATA_ZF_DNAME } },
 	/* 4 */
-	{ TYPE_MF, "MF", T_MF, 1, 1,
+	{ TYPE_MF, "MF", 1, 1,
 	  { RDATA_WF_UNCOMPRESSED_DNAME }, { RDATA_ZF_DNAME } },
 	/* 5 */
-	{ TYPE_CNAME, "CNAME", T_CNAME, 1, 1,
+	{ TYPE_CNAME, "CNAME", 1, 1,
 	  { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } },
 	/* 6 */
-	{ TYPE_SOA, "SOA", T_SOA, 7, 7,
+	{ TYPE_SOA, "SOA", 7, 7,
 	  { RDATA_WF_COMPRESSED_DNAME, RDATA_WF_COMPRESSED_DNAME, RDATA_WF_LONG,
 	    RDATA_WF_LONG, RDATA_WF_LONG, RDATA_WF_LONG, RDATA_WF_LONG },
 	  { RDATA_ZF_DNAME, RDATA_ZF_DNAME, RDATA_ZF_PERIOD, RDATA_ZF_PERIOD,
 	    RDATA_ZF_PERIOD, RDATA_ZF_PERIOD, RDATA_ZF_PERIOD } },
 	/* 7 */
-	{ TYPE_MB, "MB", T_MB, 1, 1,
+	{ TYPE_MB, "MB", 1, 1,
 	  { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } },
 	/* 8 */
-	{ TYPE_MG, "MG", T_MG, 1, 1,
+	{ TYPE_MG, "MG", 1, 1,
 	  { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } },
 	/* 9 */
-	{ TYPE_MR, "MR", T_MR, 1, 1,
+	{ TYPE_MR, "MR", 1, 1,
 	  { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } },
 	/* 10 */
-	{ TYPE_NULL, "NULL", T_UTYPE, 1, 1,
+	{ TYPE_NULL, "NULL", 1, 1,
 	  { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 11 */
-	{ TYPE_WKS, "WKS", T_WKS, 2, 2,
+	{ TYPE_WKS, "WKS", 2, 2,
 	  { RDATA_WF_A, RDATA_WF_BINARY },
 	  { RDATA_ZF_A, RDATA_ZF_SERVICES } },
 	/* 12 */
-	{ TYPE_PTR, "PTR", T_PTR, 1, 1,
+	{ TYPE_PTR, "PTR", 1, 1,
 	  { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } },
 	/* 13 */
-	{ TYPE_HINFO, "HINFO", T_HINFO, 2, 2,
+	{ TYPE_HINFO, "HINFO", 2, 2,
 	  { RDATA_WF_TEXT, RDATA_WF_TEXT }, { RDATA_ZF_TEXT, RDATA_ZF_TEXT } },
 	/* 14 */
-	{ TYPE_MINFO, "MINFO", T_MINFO, 2, 2,
+	{ TYPE_MINFO, "MINFO", 2, 2,
 	  { RDATA_WF_COMPRESSED_DNAME, RDATA_WF_COMPRESSED_DNAME },
 	  { RDATA_ZF_DNAME, RDATA_ZF_DNAME } },
 	/* 15 */
-	{ TYPE_MX, "MX", T_MX, 2, 2,
+	{ TYPE_MX, "MX", 2, 2,
 	  { RDATA_WF_SHORT, RDATA_WF_COMPRESSED_DNAME },
 	  { RDATA_ZF_SHORT, RDATA_ZF_DNAME } },
 	/* 16 */
-	{ TYPE_TXT, "TXT", T_TXT, 1, 1,
+	{ TYPE_TXT, "TXT", 1, 1,
 	  { RDATA_WF_TEXTS },
 	  { RDATA_ZF_TEXTS } },
 	/* 17 */
-	{ TYPE_RP, "RP", T_RP, 2, 2,
+	{ TYPE_RP, "RP", 2, 2,
 	  { RDATA_WF_UNCOMPRESSED_DNAME, RDATA_WF_UNCOMPRESSED_DNAME },
 	  { RDATA_ZF_DNAME, RDATA_ZF_DNAME } },
 	/* 18 */
-	{ TYPE_AFSDB, "AFSDB", T_AFSDB, 2, 2,
+	{ TYPE_AFSDB, "AFSDB", 2, 2,
 	  { RDATA_WF_SHORT, RDATA_WF_UNCOMPRESSED_DNAME },
 	  { RDATA_ZF_SHORT, RDATA_ZF_DNAME } },
 	/* 19 */
-	{ TYPE_X25, "X25", T_X25, 1, 1,
+	{ TYPE_X25, "X25", 1, 1,
 	  { RDATA_WF_TEXT },
 	  { RDATA_ZF_TEXT } },
 	/* 20 */
-	{ TYPE_ISDN, "ISDN", T_ISDN, 1, 2,
+	{ TYPE_ISDN, "ISDN", 1, 2,
 	  { RDATA_WF_TEXT, RDATA_WF_TEXT },
 	  { RDATA_ZF_TEXT, RDATA_ZF_TEXT } },
 	/* 21 */
-	{ TYPE_RT, "RT", T_RT, 2, 2,
+	{ TYPE_RT, "RT", 2, 2,
 	  { RDATA_WF_SHORT, RDATA_WF_UNCOMPRESSED_DNAME },
 	  { RDATA_ZF_SHORT, RDATA_ZF_DNAME } },
 	/* 22 */
-	{ TYPE_NSAP, "NSAP", T_NSAP, 1, 1,
+	{ TYPE_NSAP, "NSAP", 1, 1,
 	  { RDATA_WF_BINARY },
 	  { RDATA_ZF_NSAP } },
 	/* 23 */
-	{ 23, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ TYPE_NSAP_PTR, "NSAP-PTR", 1, 1,
+	  { RDATA_WF_UNCOMPRESSED_DNAME }, { RDATA_ZF_DNAME } },
 	/* 24 */
-	{ TYPE_SIG, "SIG", T_SIG, 9, 9,
+	{ TYPE_SIG, "SIG", 9, 9,
 	  { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_LONG,
 	    RDATA_WF_LONG, RDATA_WF_LONG, RDATA_WF_SHORT,
 	    RDATA_WF_LITERAL_DNAME, RDATA_WF_BINARY },
@@ -126,67 +126,69 @@ static rrtype_descriptor_type rrtype_des
 	    RDATA_ZF_TIME, RDATA_ZF_TIME, RDATA_ZF_SHORT,
 	    RDATA_ZF_LITERAL_DNAME, RDATA_ZF_BASE64 } },
 	/* 25 */
-	{ TYPE_KEY, "KEY", T_KEY, 4, 4,
+	{ TYPE_KEY, "KEY", 4, 4,
 	  { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY },
 	  { RDATA_ZF_SHORT, RDATA_ZF_BYTE, RDATA_ZF_ALGORITHM,
 	    RDATA_ZF_BASE64 } },
 	/* 26 */
-	{ TYPE_PX, "PX", T_PX, 3, 3,
+	{ TYPE_PX, "PX", 3, 3,
 	  { RDATA_WF_SHORT, RDATA_WF_UNCOMPRESSED_DNAME,
 	    RDATA_WF_UNCOMPRESSED_DNAME },
 	  { RDATA_ZF_SHORT, RDATA_ZF_DNAME, RDATA_ZF_DNAME } },
 	/* 27 */
-	{ 27, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ TYPE_GPOS, "GPOS", 3, 3,
+	  { RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT },
+	  { RDATA_ZF_UNQUOTED, RDATA_ZF_UNQUOTED, RDATA_ZF_UNQUOTED} },
 	/* 28 */
-	{ TYPE_AAAA, "AAAA", T_AAAA, 1, 1,
+	{ TYPE_AAAA, "AAAA", 1, 1,
 	  { RDATA_WF_AAAA },
 	  { RDATA_ZF_AAAA } },
 	/* 29 */
-	{ TYPE_LOC, "LOC", T_LOC, 1, 1,
+	{ TYPE_LOC, "LOC", 1, 1,
 	  { RDATA_WF_BINARY },
 	  { RDATA_ZF_LOC } },
 	/* 30 */
-	{ TYPE_NXT, "NXT", T_NXT, 2, 2,
+	{ TYPE_NXT, "NXT", 2, 2,
 	  { RDATA_WF_UNCOMPRESSED_DNAME, RDATA_WF_BINARY },
 	  { RDATA_ZF_DNAME, RDATA_ZF_NXT } },
 	/* 31 */
-	{ 31, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 31, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 32 */
-	{ 32, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 32, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 33 */
-	{ TYPE_SRV, "SRV", T_SRV, 4, 4,
+	{ TYPE_SRV, "SRV", 4, 4,
 	  { RDATA_WF_SHORT, RDATA_WF_SHORT, RDATA_WF_SHORT,
 	    RDATA_WF_UNCOMPRESSED_DNAME },
 	  { RDATA_ZF_SHORT, RDATA_ZF_SHORT, RDATA_ZF_SHORT, RDATA_ZF_DNAME } },
 	/* 34 */
-	{ 34, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 34, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 35 */
-	{ TYPE_NAPTR, "NAPTR", T_NAPTR, 6, 6,
+	{ TYPE_NAPTR, "NAPTR", 6, 6,
 	  { RDATA_WF_SHORT, RDATA_WF_SHORT, RDATA_WF_TEXT, RDATA_WF_TEXT,
 	    RDATA_WF_TEXT, RDATA_WF_UNCOMPRESSED_DNAME },
 	  { RDATA_ZF_SHORT, RDATA_ZF_SHORT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
 	    RDATA_ZF_TEXT, RDATA_ZF_DNAME } },
 	/* 36 */
-	{ TYPE_KX, "KX", T_KX, 2, 2,
+	{ TYPE_KX, "KX", 2, 2,
 	  { RDATA_WF_SHORT, RDATA_WF_UNCOMPRESSED_DNAME },
 	  { RDATA_ZF_SHORT, RDATA_ZF_DNAME } },
 	/* 37 */
-	{ TYPE_CERT, "CERT", T_CERT, 4, 4,
+	{ TYPE_CERT, "CERT", 4, 4,
 	  { RDATA_WF_SHORT, RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BINARY },
 	  { RDATA_ZF_CERTIFICATE_TYPE, RDATA_ZF_SHORT, RDATA_ZF_ALGORITHM,
 	    RDATA_ZF_BASE64 } },
 	/* 38 */
-	{ TYPE_A6, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ TYPE_A6, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 39 */
-	{ TYPE_DNAME, "DNAME", T_DNAME, 1, 1,
+	{ TYPE_DNAME, "DNAME", 1, 1,
 	  { RDATA_WF_UNCOMPRESSED_DNAME }, { RDATA_ZF_DNAME } },
 	/* 40 */
-	{ 40, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 40, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 41 */
-	{ TYPE_OPT, "OPT", T_UTYPE, 1, 1,
+	{ TYPE_OPT, "OPT", 1, 1,
 	  { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 42 */
-	{ TYPE_APL, "APL", T_APL, 0, MAXRDATALEN,
+	{ TYPE_APL, "APL", 0, MAXRDATALEN,
 	  { RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL,
 	    RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL,
 	    RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL,
@@ -220,21 +222,21 @@ static rrtype_descriptor_type rrtype_des
 	    RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL,
 	    RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL } },
 	/* 43 */
-	{ TYPE_DS, "DS", T_DS, 4, 4,
+	{ TYPE_DS, "DS", 4, 4,
 	  { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY },
 	  { RDATA_ZF_SHORT, RDATA_ZF_ALGORITHM, RDATA_ZF_BYTE, RDATA_ZF_HEX } },
 	/* 44 */
-	{ TYPE_SSHFP, "SSHFP", T_SSHFP, 3, 3,
+	{ TYPE_SSHFP, "SSHFP", 3, 3,
 	  { RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY },
 	  { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_HEX } },
 	/* 45 */
-	{ TYPE_IPSECKEY, "IPSECKEY", T_IPSECKEY, 4, 5,
+	{ TYPE_IPSECKEY, "IPSECKEY", 4, 5,
 	  { RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_IPSECGATEWAY,
 	    RDATA_WF_BINARY },
 	  { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_IPSECGATEWAY,
 	    RDATA_ZF_BASE64 } },
 	/* 46 */
-	{ TYPE_RRSIG, "RRSIG", T_RRSIG, 9, 9,
+	{ TYPE_RRSIG, "RRSIG", 9, 9,
 	  { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_LONG,
 	    RDATA_WF_LONG, RDATA_WF_LONG, RDATA_WF_SHORT,
 	    RDATA_WF_LITERAL_DNAME, RDATA_WF_BINARY },
@@ -242,18 +244,18 @@ static rrtype_descriptor_type rrtype_des
 	    RDATA_ZF_TIME, RDATA_ZF_TIME, RDATA_ZF_SHORT,
 		RDATA_ZF_LITERAL_DNAME, RDATA_ZF_BASE64 } },
 	/* 47 */
-	{ TYPE_NSEC, "NSEC", T_NSEC, 2, 2,
+	{ TYPE_NSEC, "NSEC", 2, 2,
 	  { RDATA_WF_LITERAL_DNAME, RDATA_WF_BINARY },
 	  { RDATA_ZF_LITERAL_DNAME, RDATA_ZF_NSEC } },
 	/* 48 */
-	{ TYPE_DNSKEY, "DNSKEY", T_DNSKEY, 4, 4,
+	{ TYPE_DNSKEY, "DNSKEY", 4, 4,
 	  { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY },
 	  { RDATA_ZF_SHORT, RDATA_ZF_BYTE, RDATA_ZF_ALGORITHM,
 	    RDATA_ZF_BASE64 } },
 	/* 49 */
-	{ TYPE_DHCID, "DHCID", T_DHCID, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_BASE64 } },
+	{ TYPE_DHCID, "DHCID", 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_BASE64 } },
 	/* 50 */
-	{ TYPE_NSEC3, "NSEC3", T_NSEC3, 6, 6,
+	{ TYPE_NSEC3, "NSEC3", 6, 6,
 	  { RDATA_WF_BYTE, /* hash type */
 	    RDATA_WF_BYTE, /* flags */
 	    RDATA_WF_SHORT, /* iterations */
@@ -263,60 +265,129 @@ static rrtype_descriptor_type rrtype_des
 	  { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_SHORT, RDATA_ZF_HEX_LEN,
 	    RDATA_ZF_BASE32, RDATA_ZF_NSEC } },
 	/* 51 */
-	{ TYPE_NSEC3PARAM, "NSEC3PARAM", T_NSEC3PARAM, 4, 4,
+	{ TYPE_NSEC3PARAM, "NSEC3PARAM", 4, 4,
 	  { RDATA_WF_BYTE, /* hash type */
 	    RDATA_WF_BYTE, /* flags */
 	    RDATA_WF_SHORT, /* iterations */
 	    RDATA_WF_BINARYWITHLENGTH /* salt */ },
 	  { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_SHORT, RDATA_ZF_HEX_LEN } },
 	/* 52 */
-	{ TYPE_TLSA, "TLSA", T_TLSA, 4, 4,
+	{ TYPE_TLSA, "TLSA", 4, 4,
 	  { RDATA_WF_BYTE, /* usage */
 	    RDATA_WF_BYTE, /* selector */
 	    RDATA_WF_BYTE, /* matching type */
 	    RDATA_WF_BINARY }, /* certificate association data */
 	  { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_HEX } },
 	/* 53 */
-	{ TYPE_SMIMEA, "SMIMEA", T_SMIMEA, 4, 4,
+	{ TYPE_SMIMEA, "SMIMEA", 4, 4,
 	  { RDATA_WF_BYTE, /* usage */
 	    RDATA_WF_BYTE, /* selector */
 	    RDATA_WF_BYTE, /* matching type */
 	    RDATA_WF_BINARY }, /* certificate association data */
 	  { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_HEX } },
 	/* 54 */
-	{ 54, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 54, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 55 - HIP [RFC 5205] */
-	{ 55, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ TYPE_HIP, "HIP", 1, MAXRDATALEN,
+	  { RDATA_WF_HIP, RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME
+	                , RDATA_WF_LITERAL_DNAME                         },
+	  { RDATA_ZF_HIP, RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME
+	                , RDATA_ZF_LITERAL_DNAME                         } },
 	/* 56 - NINFO */
-	{ 56, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ TYPE_NINFO, "NINFO", 1, 1,
+	  { RDATA_WF_TEXTS },
+	  { RDATA_ZF_TEXTS } },
 	/* 57 - RKEY */
-	{ 57, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ TYPE_RKEY, "RKEY", 4, 4,
+	  { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY },
+	  { RDATA_ZF_SHORT, RDATA_ZF_BYTE, RDATA_ZF_ALGORITHM,
+	    RDATA_ZF_BASE64 } },
 	/* 58 - TALINK */
-	{ 58, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 58, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 59 - CDS */
-	{ TYPE_CDS, "CDS", T_CDS, 4, 4,
+	{ TYPE_CDS, "CDS", 4, 4,
 	  { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY },
 	  { RDATA_ZF_SHORT, RDATA_ZF_ALGORITHM, RDATA_ZF_BYTE, RDATA_ZF_HEX } },
 	/* 60 - CDNSKEY */
-	{ TYPE_CDNSKEY, "CDNSKEY", T_CDNSKEY, 4, 4,
+	{ TYPE_CDNSKEY, "CDNSKEY", 4, 4,
 	  { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY },
 	  { RDATA_ZF_SHORT, RDATA_ZF_BYTE, RDATA_ZF_ALGORITHM,
 	    RDATA_ZF_BASE64 } },
 	/* 61 - OPENPGPKEY */
-	{ TYPE_OPENPGPKEY, "OPENPGPKEY", T_OPENPGPKEY, 1, 1,
+	{ TYPE_OPENPGPKEY, "OPENPGPKEY", 1, 1,
 	  { RDATA_WF_BINARY }, { RDATA_ZF_BASE64 } },
 	/* 62 - CSYNC */
-	{ TYPE_CSYNC, "CSYNC", T_CSYNC, 3, 3, { RDATA_WF_LONG, RDATA_WF_SHORT,
+	{ TYPE_CSYNC, "CSYNC", 3, 3, { RDATA_WF_LONG, RDATA_WF_SHORT,
 	 RDATA_WF_BINARY }, { RDATA_ZF_LONG, RDATA_ZF_SHORT, RDATA_ZF_NSEC } },
 	/* 63 - ZONEMD */
-	{ TYPE_ZONEMD, "ZONEMD", T_ZONEMD, 4, 4,
+	{ TYPE_ZONEMD, "ZONEMD", 4, 4,
 	  { RDATA_WF_LONG, /* serial */
 	    RDATA_WF_BYTE, /* scheme */
 	    RDATA_WF_BYTE, /* hash Algorithm */
 	    RDATA_WF_BINARY }, /* digest */
 	  { RDATA_ZF_PERIOD, RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_HEX } },
 	/* 64 - SVCB */
-	{ TYPE_SVCB, "SVCB", T_SVCB, 2, MAXRDATALEN,
+	{ TYPE_SVCB, "SVCB", 2, MAXRDATALEN,
 	  { RDATA_WF_SHORT                        /* SvcFieldPriority */
 	  , RDATA_WF_UNCOMPRESSED_DNAME           /* SvcDomainName */
 	  , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM  /* SvcFieldValue */
@@ -365,7 +436,7 @@ static rrtype_descriptor_type rrtype_des
 	  , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM
 	  } },
 	/* 65 - HTTPS */
-	{ TYPE_HTTPS, "HTTPS", T_HTTPS, 2, MAXRDATALEN,
+	{ TYPE_HTTPS, "HTTPS", 2, MAXRDATALEN,
 	  { RDATA_WF_SHORT                        /* SvcFieldPriority */
 	  , RDATA_WF_UNCOMPRESSED_DNAME           /* SvcDomainName */
 	  , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM  /* SvcFieldValue */
@@ -414,443 +485,425 @@ static rrtype_descriptor_type rrtype_des
 	  , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM
 	  } },
 	/* 66 */
-	{ 66, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 66, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 67 */
-	{ 67, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 67, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 68 */
-	{ 68, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 68, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 69 */
-	{ 69, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 69, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 70 */
-	{ 70, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 70, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 71 */
-	{ 71, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 71, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 72 */
-	{ 72, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 72, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 73 */
-	{ 73, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 73, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 74 */
-	{ 74, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 74, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 75 */
-	{ 75, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 75, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 76 */
-	{ 76, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 76, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 77 */
-	{ 77, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 77, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 78 */
-	{ 78, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 78, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 79 */
-	{ 79, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 79, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 80 */
-	{ 80, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 80, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 81 */
-	{ 81, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 81, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 82 */
-	{ 82, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 82, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 83 */
-	{ 83, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 83, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 84 */
-	{ 84, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 84, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 85 */
-	{ 85, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 85, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 86 */
-	{ 86, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 86, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 87 */
-	{ 87, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 87, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 88 */
-	{ 88, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 88, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 89 */
-	{ 89, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 89, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 90 */
-	{ 90, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 90, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 91 */
-	{ 91, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 91, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 92 */
-	{ 92, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 92, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 93 */
-	{ 93, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 93, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 94 */
-	{ 94, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 94, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 95 */
-	{ 95, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 95, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 96 */
-	{ 96, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 96, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 97 */
-	{ 97, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 97, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 98 */
-	{ 98, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 98, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 99 */
-	{ TYPE_SPF, "SPF", T_SPF, 1, MAXRDATALEN,
-	  { RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT,
-	    RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT },
-	  { RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT,
-	    RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT, RDATA_ZF_TEXT } },
+	{ TYPE_SPF, "SPF", 1, MAXRDATALEN,
+	  { RDATA_WF_TEXTS }, { RDATA_ZF_TEXTS } },
 	/* 100 - UINFO */
-	{ 100, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 100, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 101 - UID */
-	{ 101, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 101, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 102 - GID */
-	{ 102, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 102, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 103 - UNSPEC */
-	{ 103, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 103, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 104 */
-	{ TYPE_NID, "NID", T_NID, 2, 2,
+	{ TYPE_NID, "NID", 2, 2,
 	  { RDATA_WF_SHORT, RDATA_WF_ILNP64 },
 	  { RDATA_ZF_SHORT, RDATA_ZF_ILNP64 } },
 	/* 105 */
-	{ TYPE_L32, "L32", T_L32, 2, 2,
+	{ TYPE_L32, "L32", 2, 2,
 	  { RDATA_WF_SHORT, RDATA_WF_A },
 	  { RDATA_ZF_SHORT, RDATA_ZF_A } },
 	/* 106 */
-	{ TYPE_L64, "L64", T_L64, 2, 2,
+	{ TYPE_L64, "L64", 2, 2,
 	  { RDATA_WF_SHORT, RDATA_WF_ILNP64 },
 	  { RDATA_ZF_SHORT, RDATA_ZF_ILNP64 } },
 	/* 107 */
-	{ TYPE_LP, "LP", T_LP, 2, 2,
+	{ TYPE_LP, "LP", 2, 2,
 	  { RDATA_WF_SHORT, RDATA_WF_UNCOMPRESSED_DNAME },
 	  { RDATA_ZF_SHORT, RDATA_ZF_DNAME } },
 	/* 108 */
-	{ TYPE_EUI48, "EUI48", T_EUI48, 1, 1,
+	{ TYPE_EUI48, "EUI48", 1, 1,
 	  { RDATA_WF_EUI48 }, { RDATA_ZF_EUI48 } },
 	/* 109 */
-	{ TYPE_EUI64, "EUI64", T_EUI64, 1, 1,
+	{ TYPE_EUI64, "EUI64", 1, 1,
 	  { RDATA_WF_EUI64 }, { RDATA_ZF_EUI64 } },
 	/* 110 */
-	{ 110, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 110, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 111 */
-	{ 111, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 111, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 112 */
-	{ 112, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 112, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 113 */
-	{ 113, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 113, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 114 */
-	{ 114, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 114, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 115 */
-	{ 115, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 115, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 116 */
-	{ 116, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 116, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 117 */
-	{ 117, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 117, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 118 */
-	{ 118, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 118, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 119 */
-	{ 119, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 119, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 120 */
-	{ 120, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 120, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 121 */
-	{ 121, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 121, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 122 */
-	{ 122, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 122, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 123 */
-	{ 123, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 123, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 124 */
-	{ 124, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 124, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 125 */
-	{ 125, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 125, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 126 */
-	{ 126, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 126, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 127 */
-	{ 127, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 127, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 128 */
-	{ 128, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 128, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 129 */
-	{ 129, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 129, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 130 */
-	{ 130, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 130, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 131 */
-	{ 131, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 131, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 132 */
-	{ 132, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 132, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 133 */
-	{ 133, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 133, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 134 */
-	{ 134, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 134, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 135 */
-	{ 135, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 135, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 136 */
-	{ 136, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 136, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 137 */
-	{ 137, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 137, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 138 */
-	{ 138, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 138, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 139 */
-	{ 139, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 139, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 140 */
-	{ 140, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 140, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 141 */
-	{ 141, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 141, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 142 */
-	{ 142, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 142, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 143 */
-	{ 143, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 143, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 144 */
-	{ 144, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 144, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 145 */
-	{ 145, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 145, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 146 */
-	{ 146, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 146, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 147 */
-	{ 147, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 147, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 148 */
-	{ 148, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 148, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 149 */
-	{ 149, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 149, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 150 */
-	{ 150, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 150, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 151 */
-	{ 151, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 151, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 152 */
-	{ 152, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 152, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 153 */
-	{ 153, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 153, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 154 */
-	{ 154, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 154, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 155 */
-	{ 155, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 155, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 156 */
-	{ 156, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 156, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 157 */
-	{ 157, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 157, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 158 */
-	{ 158, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 158, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 159 */
-	{ 159, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 159, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 160 */
-	{ 160, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 160, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 161 */
-	{ 161, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 161, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 162 */
-	{ 162, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 162, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 163 */
-	{ 163, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 163, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 164 */
-	{ 164, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 164, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 165 */
-	{ 165, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 165, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 166 */
-	{ 166, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 166, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 167 */
-	{ 167, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 167, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 168 */
-	{ 168, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 168, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 169 */
-	{ 169, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 169, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 170 */
-	{ 170, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 170, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 171 */
-	{ 171, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 171, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 172 */
-	{ 172, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 172, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 173 */
-	{ 173, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 173, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 174 */
-	{ 174, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 174, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 175 */
-	{ 175, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 175, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 176 */
-	{ 176, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 176, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 177 */
-	{ 177, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 177, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 178 */
-	{ 178, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 178, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 179 */
-	{ 179, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 179, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 180 */
-	{ 180, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 180, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 181 */
-	{ 181, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 181, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 182 */
-	{ 182, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 182, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 183 */
-	{ 183, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 183, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 184 */
-	{ 184, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 184, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 185 */
-	{ 185, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 185, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 186 */
-	{ 186, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 186, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 187 */
-	{ 187, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 187, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 188 */
-	{ 188, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 188, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 189 */
-	{ 189, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 189, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 190 */
-	{ 190, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 190, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 191 */
-	{ 191, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 191, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 192 */
-	{ 192, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 192, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 193 */
-	{ 193, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 193, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 194 */
-	{ 194, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 194, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 195 */
-	{ 195, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 195, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 196 */
-	{ 196, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 196, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 197 */
-	{ 197, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 197, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 198 */
-	{ 198, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 198, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 199 */
-	{ 199, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 199, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 200 */
-	{ 200, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 200, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 201 */
-	{ 201, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 201, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 202 */
-	{ 202, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 202, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 203 */
-	{ 203, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 203, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 204 */
-	{ 204, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 204, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 205 */
-	{ 205, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 205, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 206 */
-	{ 206, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 206, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 207 */
-	{ 207, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 207, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 208 */
-	{ 208, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 208, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 209 */
-	{ 209, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 209, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 210 */
-	{ 210, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 210, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 211 */
-	{ 211, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 211, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 212 */
-	{ 212, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 212, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 213 */
-	{ 213, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 213, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 214 */
-	{ 214, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 214, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 215 */
-	{ 215, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 215, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 216 */
-	{ 216, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 216, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 217 */
-	{ 217, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 217, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 218 */
-	{ 218, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 218, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 219 */
-	{ 219, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 219, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 220 */
-	{ 220, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 220, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 221 */
-	{ 221, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 221, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 222 */
-	{ 222, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 222, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 223 */
-	{ 223, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 223, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 224 */
-	{ 224, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 224, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 225 */
-	{ 225, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 225, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 226 */
-	{ 226, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 226, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 227 */
-	{ 227, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 227, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 228 */
-	{ 228, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 228, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 229 */
-	{ 229, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 229, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 230 */
-	{ 230, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 230, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 231 */
-	{ 231, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 231, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 232 */
-	{ 232, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 232, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 233 */
-	{ 233, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 233, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 234 */
-	{ 234, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 234, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 235 */
-	{ 235, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 235, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 236 */
-	{ 236, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 236, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 237 */
-	{ 237, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 237, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 238 */
-	{ 238, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 238, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 239 */
-	{ 239, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 239, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 240 */
-	{ 240, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 240, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 241 */
-	{ 241, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 241, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 242 */
-	{ 242, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 242, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 243 */
-	{ 243, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 243, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 244 */
-	{ 244, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 244, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 245 */
-	{ 245, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 245, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 246 */
-	{ 246, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 246, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 247 */
-	{ 247, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 247, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 248 */
-	{ 248, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 248, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 249 - TKEY [RFC 2930] */
-	{ 249, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 249, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 250 - TSIG [RFC 2845] */
-	{ 250, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 250, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 251 - IXFR [RFC 1995] */
-	{ 251, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 251, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 252 - AXFR [RFC 1035, RFC 5936] */
-	{ 252, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 252, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 253 - MAILB [RFC 1035] */
-	{ 253, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 253, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 254 - MAILA [RFC 1035] */
-	{ 254, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 254, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 255 - * [RFC 1035, RFC 6895] */
-	{ 255, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	{ 255, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
 	/* 256 - URI */
-	{ TYPE_URI, "URI", T_URI, 3, 3,
+	{ TYPE_URI, "URI", 3, 3,
 	  { RDATA_WF_SHORT, RDATA_WF_SHORT, RDATA_WF_LONG_TEXT },
 	  { RDATA_ZF_SHORT, RDATA_ZF_SHORT, RDATA_ZF_LONG_TEXT } },
 	/* 257 - CAA [RFC 6844] */
-	{ TYPE_CAA, "CAA", T_CAA, 3, 3,
+	{ TYPE_CAA, "CAA", 3, 3,
 	  { RDATA_WF_BYTE, RDATA_WF_TEXT, RDATA_WF_LONG_TEXT },
 	  { RDATA_ZF_BYTE, RDATA_ZF_TAG, RDATA_ZF_LONG_TEXT } },
 	/* 258 - AVC */
-	{ TYPE_AVC, "AVC", T_AVC, 1, 1,
+	{ TYPE_AVC, "AVC", 1, 1,
 	  { RDATA_WF_TEXTS },
 	  { RDATA_ZF_TEXTS } },
+	/* 259 - DOA */
+	{ 259, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	/* 260 - AMTRELAY */
+	{ 260, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } },
+	/* 261 - RESINFO */
+	{ TYPE_RESINFO, "RESINFO", 1, 1, { RDATA_WF_TEXTS }, { RDATA_ZF_UNQUOTEDS } },
+	/* 262 - WALLET */
+	{ TYPE_WALLET, "WALLET", 1, 1, { RDATA_WF_TEXTS }, { RDATA_ZF_TEXTS } },
+	/* 263 - CLA */
+	{ TYPE_CLA, "CLA", 1, 1, { RDATA_WF_TEXTS }, { RDATA_ZF_TEXTS } },
 
 	/* 32768 - TA */
+	{ TYPE_TA, "TA", 4, 4,
+	  { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY },
+	  { RDATA_ZF_SHORT, RDATA_ZF_ALGORITHM, RDATA_ZF_BYTE, RDATA_ZF_HEX } },
 	/* 32769 */
-	{ TYPE_DLV, "DLV", T_DLV, 4, 4,
+	{ TYPE_DLV, "DLV", 4, 4,
 	  { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY },
 	  { RDATA_ZF_SHORT, RDATA_ZF_ALGORITHM, RDATA_ZF_BYTE, RDATA_ZF_HEX } },
 };
@@ -862,6 +915,8 @@ rrtype_descriptor_by_type(uint16_t type)
 		return &rrtype_descriptors[type];
 	else if (type == TYPE_DLV)
 		return &rrtype_descriptors[PSEUDO_TYPE_DLV];
+	else if (type == TYPE_TA)
+		return &rrtype_descriptors[PSEUDO_TYPE_TA];
 	return &rrtype_descriptors[0];
 }
 
Index: dns.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/dns.h,v
diff -u -p -r1.20 dns.h
--- dns.h	29 Jun 2023 19:38:49 -0000	1.20
+++ dns.h	3 Sep 2025 13:53:31 -0000
@@ -103,12 +103,12 @@ typedef enum nsd_rc nsd_rc_type;
 #define TYPE_X25	19	/* RFC1183 */
 #define TYPE_ISDN	20	/* RFC1183 */
 #define TYPE_RT		21	/* RFC1183 */
-#define TYPE_NSAP	22	/* RFC1706 */
-
+#define TYPE_NSAP	22	/* RFC1706 (deprecated by RFC9121) */
+#define TYPE_NSAP_PTR	23	/* RFC1348  (deprecated by RFC9121)*/
 #define TYPE_SIG	24	/* 2535typecode */
 #define TYPE_KEY	25	/* 2535typecode */
 #define TYPE_PX		26	/* RFC2163 */
-
+#define TYPE_GPOS	27	/* RFC1712 */
 #define TYPE_AAAA	28	/* ipv6 address */
 #define TYPE_LOC	29	/* LOC record  RFC1876 */
 #define TYPE_NXT	30	/* 2535typecode */
@@ -118,9 +118,7 @@ typedef enum nsd_rc nsd_rc_type;
 #define TYPE_NAPTR	35	/* RFC2915 */
 #define TYPE_KX		36	/* RFC2230 Key Exchange Delegation Record */
 #define TYPE_CERT	37	/* RFC2538 */
-
 #define TYPE_A6		38	/* RFC2874 */
-
 #define TYPE_DNAME	39	/* RFC2672 */
 
 #define TYPE_OPT	41	/* Pseudo OPT record... */
@@ -128,7 +126,6 @@ typedef enum nsd_rc nsd_rc_type;
 #define TYPE_DS		43	/* RFC 4033, 4034, and 4035 */
 #define TYPE_SSHFP	44	/* SSH Key Fingerprint */
 #define TYPE_IPSECKEY	45	/* public key for ipsec use. RFC 4025 */
-
 #define TYPE_RRSIG	46	/* RFC 4033, 4034, and 4035 */
 #define TYPE_NSEC	47	/* RFC 4033, 4034, and 4035 */
 #define TYPE_DNSKEY	48	/* RFC 4033, 4034, and 4035 */
@@ -137,13 +134,17 @@ typedef enum nsd_rc nsd_rc_type;
 #define TYPE_NSEC3PARAM 51	/* NSEC3PARAM at zone apex nsec3 parameters */
 #define TYPE_TLSA	52	/* RFC 6698 */
 #define TYPE_SMIMEA	53	/* RFC 8162 */
+#define TYPE_HIP	55	/* RFC 8005 */
+#define TYPE_NINFO	56	/* NINFO/ninfo-completed-template */
+#define TYPE_RKEY	57	/* RKEY/rkey-completed-template */
+
 #define TYPE_CDS	59	/* RFC 7344 */
 #define TYPE_CDNSKEY	60	/* RFC 7344 */
 #define TYPE_OPENPGPKEY 61	/* RFC 7929 */
 #define TYPE_CSYNC	62	/* RFC 7477 */
-#define TYPE_ZONEMD	63	/* draft-ietf-dnsop-dns-zone-digest */
-#define TYPE_SVCB	64	/* draft-ietf-dnsop-svcb-https-03 */
-#define TYPE_HTTPS	65	/* draft-ietf-dnsop-svcb-https-03 */
+#define TYPE_ZONEMD	63	/* RFC 8976 */
+#define TYPE_SVCB	64	/* RFC 9460 */
+#define TYPE_HTTPS	65	/* RFC 9460 */
 
 #define TYPE_SPF        99      /* RFC 4408 */
 
@@ -162,20 +163,28 @@ typedef enum nsd_rc nsd_rc_type;
 #define TYPE_ANY	255	/* any type (wildcard) */
 #define TYPE_URI	256	/* RFC 7553 */
 #define TYPE_CAA	257	/* RFC 6844 */
-#define TYPE_AVC	258
+#define TYPE_AVC	258	/* AVC/avc-completed-template */
+
+#define TYPE_RESINFO	261	/* RFC 9606 */
+#define TYPE_WALLET	262	/* WALLET/wallet-completed-template */
+#define TYPE_CLA	263	/* CLA/cla-completed-template */
 
+#define TYPE_TA		32768	/* http://www.watson.org/~weiler/INI1999-19.pdf */
 #define TYPE_DLV	32769	/* RFC 4431 */
-#define PSEUDO_TYPE_DLV	RRTYPE_DESCRIPTORS_LENGTH
+#define PSEUDO_TYPE_TA	RRTYPE_DESCRIPTORS_LENGTH
+#define PSEUDO_TYPE_DLV	(RRTYPE_DESCRIPTORS_LENGTH + 1)
 
 #define SVCB_KEY_MANDATORY		0
 #define SVCB_KEY_ALPN			1
 #define SVCB_KEY_NO_DEFAULT_ALPN	2
 #define SVCB_KEY_PORT			3
 #define SVCB_KEY_IPV4HINT		4
-#define SVCB_KEY_ECH		5
+#define SVCB_KEY_ECH			5
 #define SVCB_KEY_IPV6HINT		6
 #define SVCB_KEY_DOHPATH		7
-#define SVCPARAMKEY_COUNT 8
+#define SVCB_KEY_OHTTP			8
+#define SVCB_KEY_TLS_SUPPORTED_GROUPS	9
+#define SVCPARAMKEY_COUNT 10
 
 #define MAXLABELLEN	63
 #define MAXDOMAINLEN	255
@@ -214,10 +223,11 @@ enum rdata_wireformat
 	RDATA_WF_APL,                /* APL data.  */
 	RDATA_WF_IPSECGATEWAY,       /* IPSECKEY gateway ip4, ip6 or dname. */
 	RDATA_WF_ILNP64,             /* 64-bit uncompressed IPv6 address.  */
-	RDATA_WF_EUI48,	             /* 48-bit address.  */
+	RDATA_WF_EUI48,              /* 48-bit address.  */
 	RDATA_WF_EUI64,              /* 64-bit address.  */
 	RDATA_WF_LONG_TEXT,          /* Long (>255) text string. */
-	RDATA_WF_SVCPARAM            /* SvcParam <key>[=<value>] */
+	RDATA_WF_SVCPARAM,           /* SvcParam <key>[=<value>] */
+	RDATA_WF_HIP                 /* HIP rdata up to the Rendezvous Servers */
 };
 typedef enum rdata_wireformat rdata_wireformat_type;
 
@@ -255,8 +265,11 @@ enum rdata_zoneformat
 	RDATA_ZF_EUI48,		/* EUI48 address.  */
 	RDATA_ZF_EUI64,		/* EUI64 address.  */
 	RDATA_ZF_LONG_TEXT,	/* Long (>255) text string. */
-	RDATA_ZF_TAG,		/* Text string without quotes. */
+	RDATA_ZF_UNQUOTED,	/* Unquoted text string. */
+	RDATA_ZF_UNQUOTEDS,	/* A sequence of unquoted text strings. */
+	RDATA_ZF_TAG,		/* A sequence of letters and numbers. */
 	RDATA_ZF_SVCPARAM,	/* SvcParam <key>[=<value>] */
+	RDATA_ZF_HIP,		/* HIP rdata up to the Rendezvous Servers */
 	RDATA_ZF_UNKNOWN	/* Unknown data.  */
 };
 typedef enum rdata_zoneformat rdata_zoneformat_type;
@@ -265,7 +278,6 @@ struct rrtype_descriptor
 {
 	uint16_t    type;	/* RR type */
 	const char *name;	/* Textual name.  */
-	int         token;	/* Parser token.  */
 	uint32_t    minimum;	/* Minimum number of RDATAs.  */
 	uint32_t    maximum;	/* Maximum number of RDATAs.  */
 	uint8_t     wireformat[MAXRDATALEN]; /* rdata_wireformat_type */
@@ -277,9 +289,9 @@ typedef struct rrtype_descriptor rrtype_
  * Indexed by type.  The special type "0" can be used to get a
  * descriptor for unknown types (with one binary rdata).
  *
- * AVC + 1
+ * CLA + 1
  */
-#define RRTYPE_DESCRIPTORS_LENGTH  (TYPE_AVC + 1)
+#define RRTYPE_DESCRIPTORS_LENGTH  (TYPE_CLA + 1)
 rrtype_descriptor_type *rrtype_descriptor_by_name(const char *name);
 rrtype_descriptor_type *rrtype_descriptor_by_type(uint16_t type);
 
@@ -295,14 +307,5 @@ uint16_t rrtype_from_string(const char *
 
 const char *rrclass_to_string(uint16_t rrclass);
 uint16_t rrclass_from_string(const char *name);
-
-#ifdef __cplusplus
-inline rr_section_type
-operator++(rr_section_type &lhs)
-{
-	lhs = (rr_section_type) ((int) lhs + 1);
-	return lhs;
-}
-#endif /* __cplusplus */
 
 #endif /* DNS_H */
Index: edns.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/edns.c,v
diff -u -p -r1.8 edns.c
--- edns.c	20 Dec 2023 17:29:01 -0000	1.8
+++ edns.c	3 Sep 2025 13:53:31 -0000
@@ -70,6 +70,7 @@ edns_init_record(edns_record_type *edns)
 	edns->opt_reserved_space = 0;
 	edns->dnssec_ok = 0;
 	edns->nsid = 0;
+	edns->zoneversion = 0;
 	edns->cookie_status = COOKIE_NOT_PRESENT;
 	edns->cookie_len = 0;
 	edns->ede = -1; /* -1 means no Extended DNS Error */
@@ -115,6 +116,11 @@ edns_handle_option(uint16_t optcode, uin
 		} else {
 			buffer_skip(packet, optlen);
 		}
+		break;
+	case ZONEVERSION_CODE:
+		edns->zoneversion = 1;
+		if(optlen > 0)
+			return 0;
 		break;
 	default:
 		buffer_skip(packet, optlen);
Index: edns.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/edns.h,v
diff -u -p -r1.5 edns.h
--- edns.h	29 Jun 2023 19:38:49 -0000	1.5
+++ edns.h	3 Sep 2025 13:53:31 -0000
@@ -20,8 +20,12 @@ struct query;
 #define NSID_CODE       3               /* nsid option code */
 #define COOKIE_CODE    10               /* COOKIE option code */
 #define EDE_CODE       15               /* Extended DNS Errors option code */
+#define ZONEVERSION_CODE 19             /* ZONEVERSION option code */
 #define DNSSEC_OK_MASK  0x8000U         /* do bit mask */
 
+/* https://iana.org/assignments/dns-parameters/#zoneversion-type-values */
+#define ZONEVERSION_SOA_SERIAL 0
+
 struct edns_data
 {
 	char ok[OPT_LEN];
@@ -59,6 +63,7 @@ struct edns_record
 	size_t		   opt_reserved_space;
 	int                dnssec_ok;
 	int                nsid;
+	int                zoneversion;
 	cookie_status_type cookie_status;
 	size_t             cookie_len;
 	uint8_t            cookie[40];
Index: ipc.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/ipc.c,v
diff -u -p -r1.12 ipc.c
--- ipc.c	20 Dec 2023 17:29:01 -0000	1.12
+++ ipc.c	3 Sep 2025 13:53:31 -0000
@@ -166,31 +166,6 @@ send_stat_to_child(struct main_ipc_handl
 	data->child->need_to_send_STATS = 0;
 }
 
-#ifndef NDEBUG
-int packet_read_query_section(buffer_type *packet, uint8_t* dest, uint16_t* qtype, uint16_t* qclass);
-static void
-debug_print_fwd_name(int ATTR_UNUSED(len), buffer_type* packet, int acl_num)
-{
-	uint8_t qnamebuf[MAXDOMAINLEN];
-	uint16_t qtype, qclass;
-	const dname_type* dname;
-	region_type* tempregion = region_create(xalloc, free);
-
-	size_t bufpos = buffer_position(packet);
-	buffer_rewind(packet);
-	buffer_skip(packet, 12);
-	if(packet_read_query_section(packet, qnamebuf, &qtype, &qclass)) {
-		dname = dname_make(tempregion, qnamebuf, 1);
-		log_msg(LOG_INFO, "main: fwd packet for %s, acl %d",
-			dname_to_string(dname,0), acl_num);
-	} else {
-		log_msg(LOG_INFO, "main: fwd packet badqname, acl %d", acl_num);
-	}
-	buffer_set_position(packet, bufpos);
-	region_destroy(tempregion);
-}
-#endif
-
 static void
 send_quit_to_child(struct main_ipc_handler_data* data, int fd)
 {
@@ -332,95 +307,6 @@ parent_handle_child_command(netio_type *
 		return;
 	}
 
-	if (data->forward_mode) {
-		int got_acl;
-		/* forward the data to xfrd */
-		DEBUG(DEBUG_IPC,2, (LOG_INFO,
-			"main passed packet readup %d", (int)data->got_bytes));
-		if(data->got_bytes < sizeof(data->total_bytes))
-		{
-			if ((len = read(handler->fd,
-				(char*)&data->total_bytes+data->got_bytes,
-				sizeof(data->total_bytes)-data->got_bytes)) == -1) {
-				log_msg(LOG_ERR, "handle_child_command: read: %s",
-					strerror(errno));
-				return;
-			}
-			if(len == 0) {
-				/* EOF */
-				data->forward_mode = 0;
-				return;
-			}
-			data->got_bytes += len;
-			if(data->got_bytes < sizeof(data->total_bytes))
-				return;
-			data->total_bytes = ntohs(data->total_bytes);
-			buffer_clear(data->packet);
-			if(data->total_bytes > buffer_capacity(data->packet)) {
-				log_msg(LOG_ERR, "internal error: ipc too large");
-				exit(1);
-			}
-			return;
-		}
-		/* read the packet */
-		if(data->got_bytes-sizeof(data->total_bytes) < data->total_bytes) {
-			if((len = read(handler->fd, buffer_current(data->packet),
-				data->total_bytes - (data->got_bytes-sizeof(data->total_bytes))
-				)) == -1 ) {
-				log_msg(LOG_ERR, "handle_child_command: read: %s",
-					strerror(errno));
-				return;
-			}
-			if(len == 0) {
-				/* EOF */
-				data->forward_mode = 0;
-				return;
-			}
-			data->got_bytes += len;
-			buffer_skip(data->packet, len);
-			/* read rest later */
-			return;
-		}
-		/* read the acl numbers */
-		got_acl = data->got_bytes - sizeof(data->total_bytes) - data->total_bytes;
-		if((len = read(handler->fd, (char*)&data->acl_num+got_acl,
-			sizeof(data->acl_num)+sizeof(data->acl_xfr)-got_acl)) == -1 ) {
-			log_msg(LOG_ERR, "handle_child_command: read: %s",
-				strerror(errno));
-			return;
-		}
-		if(len == 0) {
-			/* EOF */
-			data->forward_mode = 0;
-			return;
-		}
-		got_acl += len;
-		data->got_bytes += len;
-		if(got_acl >= (int)(sizeof(data->acl_num)+sizeof(data->acl_xfr))) {
-			uint16_t len = htons(data->total_bytes);
-			DEBUG(DEBUG_IPC,2, (LOG_INFO,
-				"main fwd passed packet write %d", (int)data->got_bytes));
-#ifndef NDEBUG
-			if(nsd_debug_level >= 2)
-				debug_print_fwd_name(len, data->packet, data->acl_num);
-#endif
-			data->forward_mode = 0;
-			mode = NSD_PASS_TO_XFRD;
-			if(!write_socket(*data->xfrd_sock, &mode, sizeof(mode)) ||
-			   !write_socket(*data->xfrd_sock, &len, sizeof(len)) ||
-			   !write_socket(*data->xfrd_sock, buffer_begin(data->packet),
-				data->total_bytes) ||
-			   !write_socket(*data->xfrd_sock, &data->acl_num,
-			   	sizeof(data->acl_num)) ||
-			   !write_socket(*data->xfrd_sock, &data->acl_xfr,
-			   	sizeof(data->acl_xfr))) {
-				log_msg(LOG_ERR, "error in ipc fwd main2xfrd: %s",
-					strerror(errno));
-			}
-		}
-		return;
-	}
-
 	/* read command from ipc */
 	if ((len = read(handler->fd, &mode, sizeof(mode))) == -1) {
 		log_msg(LOG_ERR, "handle_child_command: read: %s",
@@ -443,12 +329,6 @@ parent_handle_child_command(netio_type *
 	case NSD_REAP_CHILDREN:
 		data->nsd->signal_hint_child = 1;
 		break;
-	case NSD_PASS_TO_XFRD:
-		/* set mode for handle_child_command; echo to xfrd. */
-		data->forward_mode = 1;
-		data->got_bytes = 0;
-		data->total_bytes = 0;
-		break;
 	default:
 		log_msg(LOG_ERR, "handle_child_command: bad mode %d",
 			(int) mode);
@@ -651,52 +531,6 @@ xfrd_handle_ipc_read(struct event* handl
 	sig_atomic_t cmd;
 	int len;
 
-	if(xfrd->ipc_conn->is_reading==2) {
-		buffer_type* tmp = xfrd->ipc_pass;
-		uint32_t acl_num;
-		int32_t acl_xfr;
-		/* read acl_num */
-		int ret = conn_read(xfrd->ipc_conn);
-		if(ret == -1) {
-			log_msg(LOG_ERR, "xfrd: error in read ipc: %s", strerror(errno));
-			xfrd->ipc_conn->is_reading = 0;
-			return;
-		}
-		if(ret == 0)
-			return;
-		buffer_flip(xfrd->ipc_conn->packet);
-		xfrd->ipc_pass = xfrd->ipc_conn->packet;
-		xfrd->ipc_conn->packet = tmp;
-		xfrd->ipc_conn->is_reading = 0;
-		acl_num = buffer_read_u32(xfrd->ipc_pass);
-		acl_xfr = (int32_t)buffer_read_u32(xfrd->ipc_pass);
-		xfrd_handle_passed_packet(xfrd->ipc_conn->packet, acl_num, acl_xfr);
-		return;
-	}
-	if(xfrd->ipc_conn->is_reading) {
-		/* reading an IPC message */
-		buffer_type* tmp;
-		int ret = conn_read(xfrd->ipc_conn);
-		if(ret == -1) {
-			log_msg(LOG_ERR, "xfrd: error in read ipc: %s", strerror(errno));
-			xfrd->ipc_conn->is_reading = 0;
-			return;
-		}
-		if(ret == 0)
-			return;
-		buffer_flip(xfrd->ipc_conn->packet);
-		/* use ipc_conn to read remaining data as well */
-		tmp = xfrd->ipc_pass;
-		xfrd->ipc_conn->is_reading=2;
-		xfrd->ipc_pass = xfrd->ipc_conn->packet;
-		xfrd->ipc_conn->packet = tmp;
-		xfrd->ipc_conn->total_bytes = sizeof(xfrd->ipc_conn->msglen);
-		xfrd->ipc_conn->msglen = 2*sizeof(uint32_t);
-		buffer_clear(xfrd->ipc_conn->packet);
-		buffer_set_limit(xfrd->ipc_conn->packet, xfrd->ipc_conn->msglen);
-		return;
-	}
-
 	if((len = read(handler->ev_fd, &cmd, sizeof(cmd))) == -1) {
 		if(errno != EINTR && errno != EAGAIN)
 			log_msg(LOG_ERR, "xfrd_handle_ipc: read: %s",
@@ -747,10 +581,6 @@ xfrd_handle_ipc_read(struct event* handl
 		xfrd_prepare_zones_for_reload();
 		xfrd->reload_failed = 0;
 		break;
-	case NSD_PASS_TO_XFRD:
-		DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv PASS_TO_XFRD"));
-		xfrd->ipc_conn->is_reading = 1;
-		break;
 	case NSD_RELOAD_REQ:
 		DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv RELOAD_REQ"));
 		/* make reload happen, right away, and schedule file check */
@@ -769,11 +599,38 @@ xfrd_handle_ipc_read(struct event* handl
 			(int)ntohl(cmd));
 		break;
 	}
+}
 
-	if(xfrd->ipc_conn->is_reading) {
-		/* setup read of info */
-		xfrd->ipc_conn->total_bytes = 0;
-		xfrd->ipc_conn->msglen = 0;
-		buffer_clear(xfrd->ipc_conn->packet);
-	}
+void
+xfrd_handle_notify(int ATTR_UNUSED(fd), short event, void* arg)
+{
+	struct xfrd_tcp* notify_pipe = (struct xfrd_tcp*)arg;
+	uint32_t acl_num;
+	int32_t acl_xfr;
+
+	if(!(event & EV_READ))
+		return;
+
+	switch(conn_read(notify_pipe)){
+	case -1: /* TODO: What to do here? */
+		 return;
+	case  0: return; /* call back later */
+	default: break;
+	}
+	if(buffer_limit(notify_pipe->packet) < sizeof(acl_xfr)+sizeof(acl_num))
+		log_msg(LOG_ERR, "xfrd_handle_notify invalid message size");
+	else {
+		size_t eop = buffer_position(notify_pipe->packet)
+		           - sizeof(acl_xfr) - sizeof(acl_num);
+
+		buffer_set_position(notify_pipe->packet, eop);
+		acl_num = buffer_read_u32(notify_pipe->packet);
+		acl_xfr = (int32_t)buffer_read_u32(notify_pipe->packet);
+		buffer_set_position(notify_pipe->packet, eop);
+		buffer_flip(notify_pipe->packet);
+		xfrd_handle_passed_packet(notify_pipe->packet,acl_num,acl_xfr);
+	}
+	notify_pipe->total_bytes = 0;
+	notify_pipe->msglen = 0;
+	buffer_clear(notify_pipe->packet);
 }
Index: ipc.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/ipc.h,v
diff -u -p -r1.1.1.6 ipc.h
--- ipc.h	3 Feb 2015 10:24:32 -0000	1.1.1.6
+++ ipc.h	3 Sep 2025 13:53:31 -0000
@@ -27,16 +27,6 @@ struct main_ipc_handler_data
 {
 	struct nsd	*nsd;
 	struct nsd_child *child;
-	int		child_num;
-
-	/* pointer to the socket, as it may change if it is restarted */
-	int		*xfrd_sock;
-	struct buffer	*packet;
-	int		forward_mode;
-	size_t		got_bytes;
-	uint16_t	total_bytes;
-	uint32_t	acl_num;
-	int32_t		acl_xfr;
 };
 
 /*
@@ -83,6 +73,9 @@ void child_handle_parent_command(int fd,
  * Handle interprocess communication with parent process, read and write.
  */
 void xfrd_handle_ipc(int fd, short event, void* arg);
+
+/* receive incoming notifies received by and from the serve processes */
+void xfrd_handle_notify(int fd, short event, void* arg);
 
 /* check if all children have exited in an orderly fashion and set mode */
 void parent_check_all_children_exited(struct nsd* nsd);
Index: ixfr.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/ixfr.c,v
diff -u -p -r1.3 ixfr.c
--- ixfr.c	20 Dec 2023 17:29:01 -0000	1.3
+++ ixfr.c	3 Sep 2025 13:53:31 -0000
@@ -26,6 +26,7 @@
 #include "axfr.h"
 #include "options.h"
 #include "zonec.h"
+#include "zone.h"
 
 /*
  * For optimal compression IXFR response packets are limited in size
@@ -2238,64 +2239,6 @@ void ixfr_write_to_file(struct zone* zon
 	ixfr_write_files(zone, zfile);
 }
 
-/* skip whitespace */
-static char* skipwhite(char* str)
-{
-	while(isspace((unsigned char)*str))
-		str++;
-	return str;
-}
-
-/* read one RR from file */
-static int ixfr_data_readrr(struct zone* zone, FILE* in, const char* ixfrfile,
-	struct region* tempregion, struct domain_table* temptable,
-	struct zone* tempzone, struct rr** rr)
-{
-	char line[65536];
-	char* str;
-	struct domain* domain_parsed = NULL;
-	int num_rrs = 0;
-	line[sizeof(line)-1]=0;
-	while(!feof(in)) {
-		if(!fgets(line, sizeof(line), in)) {
-			if(errno == 0) {
-				log_msg(LOG_ERR, "zone %s IXFR data %s: "
-					"unexpected end of file", zone->opts->name, ixfrfile);
-				return 0;
-			}
-			log_msg(LOG_ERR, "zone %s IXFR data %s: "
-				"cannot read: %s", zone->opts->name, ixfrfile,
-				strerror(errno));
-			return 0;
-		}
-		str = skipwhite(line);
-		if(str[0] == 0) {
-			/* empty line */
-			continue;
-		}
-		if(str[0] == ';') {
-			/* comment line */
-			continue;
-		}
-		if(zonec_parse_string(tempregion, temptable, tempzone,
-			line, &domain_parsed, &num_rrs)) {
-			log_msg(LOG_ERR, "zone %s IXFR data %s: parse error",
-				zone->opts->name, ixfrfile);
-			return 0;
-		}
-		if(num_rrs != 1) {
-			log_msg(LOG_ERR, "zone %s IXFR data %s: parse error",
-				zone->opts->name, ixfrfile);
-			return 0;
-		}
-		*rr = &domain_parsed->rrsets->rrs[0];
-		return 1;
-	}
-	log_msg(LOG_ERR, "zone %s IXFR data %s: file too short, no newsoa",
-		zone->opts->name, ixfrfile);
-	return 0;
-}
-
 /* delete from domain table */
 static void domain_table_delete(struct domain_table* table,
 	struct domain* domain)
@@ -2419,45 +2362,41 @@ static void clear_temp_table_of_rr(struc
 
 /* read ixfr data new SOA */
 static int ixfr_data_readnewsoa(struct ixfr_data* data, struct zone* zone,
-	FILE* in, const char* ixfrfile, struct region* tempregion,
+	struct rr *rr, zone_parser_t *parser, struct region* tempregion,
 	struct domain_table* temptable, struct zone* tempzone,
 	uint32_t dest_serial)
 {
-	struct rr* rr;
 	size_t capacity = 0;
-	if(!ixfr_data_readrr(zone, in, ixfrfile, tempregion, temptable,
-		tempzone, &rr))
-		return 0;
 	if(rr->type != TYPE_SOA) {
-		log_msg(LOG_ERR, "zone %s ixfr data %s: IXFR data does not start with SOA",
-			zone->opts->name, ixfrfile);
+		zone_error(parser, "zone %s ixfr data: IXFR data does not start with SOA",
+			zone->opts->name);
 		return 0;
 	}
 	if(rr->klass != CLASS_IN) {
-		log_msg(LOG_ERR, "zone %s ixfr data %s: IXFR data is not class IN",
-			zone->opts->name, ixfrfile);
+		zone_error(parser, "zone %s ixfr data: IXFR data is not class IN",
+			zone->opts->name);
 		return 0;
 	}
 	if(!zone->apex) {
-		log_msg(LOG_ERR, "zone %s ixfr data %s: zone has no apex, no zone data",
-			zone->opts->name, ixfrfile);
+		zone_error(parser, "zone %s ixfr data: zone has no apex, no zone data",
+			zone->opts->name);
 		return 0;
 	}
 	if(dname_compare(domain_dname(zone->apex), domain_dname(rr->owner)) != 0) {
-		log_msg(LOG_ERR, "zone %s ixfr data %s: IXFR data wrong SOA for zone %s",
-			zone->opts->name, ixfrfile, domain_to_string(rr->owner));
+		zone_error(parser, "zone %s ixfr data: IXFR data wrong SOA for zone %s",
+			zone->opts->name, domain_to_string(rr->owner));
 		return 0;
 	}
 	data->newserial = soa_rr_get_serial(rr);
 	if(data->newserial != dest_serial) {
-		log_msg(LOG_ERR, "zone %s ixfr data %s: IXFR data contains the wrong version, serial %u but want destination serial %u",
-			zone->opts->name, ixfrfile, data->newserial,
+		zone_error(parser, "zone %s ixfr data: IXFR data contains the wrong version, serial %u but want destination serial %u",
+			zone->opts->name, data->newserial,
 			dest_serial);
 		return 0;
 	}
 	if(!ixfr_putrr(domain_dname(rr->owner), rr->type, rr->klass, rr->ttl, rr->rdatas, rr->rdata_count, &data->newsoa, &data->newsoa_len, &capacity)) {
-		log_msg(LOG_ERR, "zone %s ixfr data %s: cannot allocate space",
-			zone->opts->name, ixfrfile);
+		zone_error(parser, "zone %s ixfr data: cannot allocate space",
+			zone->opts->name);
 		return 0;
 	}
 	clear_temp_table_of_rr(temptable, tempzone, rr);
@@ -2468,39 +2407,35 @@ static int ixfr_data_readnewsoa(struct i
 
 /* read ixfr data old SOA */
 static int ixfr_data_readoldsoa(struct ixfr_data* data, struct zone* zone,
-	FILE* in, const char* ixfrfile, struct region* tempregion,
+	struct rr *rr, zone_parser_t *parser, struct region* tempregion,
 	struct domain_table* temptable, struct zone* tempzone,
 	uint32_t* dest_serial)
 {
-	struct rr* rr;
 	size_t capacity = 0;
-	if(!ixfr_data_readrr(zone, in, ixfrfile, tempregion, temptable,
-		tempzone, &rr))
-		return 0;
 	if(rr->type != TYPE_SOA) {
-		log_msg(LOG_ERR, "zone %s ixfr data %s: IXFR data 2nd RR is not SOA",
-			zone->opts->name, ixfrfile);
+		zone_error(parser, "zone %s ixfr data: IXFR data 2nd RR is not SOA",
+			zone->opts->name);
 		return 0;
 	}
 	if(rr->klass != CLASS_IN) {
-		log_msg(LOG_ERR, "zone %s ixfr data %s: IXFR data 2ndSOA is not class IN",
-			zone->opts->name, ixfrfile);
+		zone_error(parser, "zone %s ixfr data: IXFR data 2ndSOA is not class IN",
+			zone->opts->name);
 		return 0;
 	}
 	if(!zone->apex) {
-		log_msg(LOG_ERR, "zone %s ixfr data %s: zone has no apex, no zone data",
-			zone->opts->name, ixfrfile);
+		zone_error(parser, "zone %s ixfr data: zone has no apex, no zone data",
+			zone->opts->name);
 		return 0;
 	}
 	if(dname_compare(domain_dname(zone->apex), domain_dname(rr->owner)) != 0) {
-		log_msg(LOG_ERR, "zone %s ixfr data %s: IXFR data wrong 2nd SOA for zone %s",
-			zone->opts->name, ixfrfile, domain_to_string(rr->owner));
+		zone_error(parser, "zone %s ixfr data: IXFR data wrong 2nd SOA for zone %s",
+			zone->opts->name, domain_to_string(rr->owner));
 		return 0;
 	}
 	data->oldserial = soa_rr_get_serial(rr);
 	if(!ixfr_putrr(domain_dname(rr->owner), rr->type, rr->klass, rr->ttl, rr->rdatas, rr->rdata_count, &data->oldsoa, &data->oldsoa_len, &capacity)) {
-		log_msg(LOG_ERR, "zone %s ixfr data %s: cannot allocate space",
-			zone->opts->name, ixfrfile);
+		zone_error(parser, "zone %s ixfr data: cannot allocate space",
+			zone->opts->name);
 		return 0;
 	}
 	clear_temp_table_of_rr(temptable, tempzone, rr);
@@ -2512,76 +2447,166 @@ static int ixfr_data_readoldsoa(struct i
 
 /* read ixfr data del section */
 static int ixfr_data_readdel(struct ixfr_data* data, struct zone* zone,
-	FILE* in, const char* ixfrfile, struct region* tempregion,
+	struct rr *rr, zone_parser_t *parser, struct region* tempregion,
 	struct domain_table* temptable, struct zone* tempzone)
 {
-	struct rr* rr;
 	size_t capacity = 0;
-	while(1) {
-		if(!ixfr_data_readrr(zone, in, ixfrfile, tempregion, temptable,
-			tempzone, &rr))
-			return 0;
-		if(!ixfr_putrr(domain_dname(rr->owner), rr->type, rr->klass, rr->ttl, rr->rdatas, rr->rdata_count, &data->del, &data->del_len, &capacity)) {
-			log_msg(LOG_ERR, "zone %s ixfr data %s: cannot allocate space",
-				zone->opts->name, ixfrfile);
-			return 0;
-		}
-		/* check SOA and also serial, because there could be other
-		 * add and del sections from older versions collated, we can
-		 * see this del section end when it has the serial */
-		if(rr->type == TYPE_SOA &&
-			soa_rr_get_serial(rr) == data->newserial) {
-			/* end of del section. */
-			clear_temp_table_of_rr(temptable, tempzone, rr);
-			region_free_all(tempregion);
-			break;
-		}
-		clear_temp_table_of_rr(temptable, tempzone, rr);
-		region_free_all(tempregion);
+	if(!ixfr_putrr(domain_dname(rr->owner), rr->type, rr->klass, rr->ttl, rr->rdatas, rr->rdata_count, &data->del, &data->del_len, &capacity)) {
+		zone_error(parser, "zone %s ixdr data: cannot allocate space",
+			zone->opts->name);
+		return 0;
+	}
+	clear_temp_table_of_rr(temptable, tempzone, rr);
+	region_free_all(tempregion);
+	/* check SOA and also serial, because there could be other
+	 * add and del sections from older versions collated, we can
+	 * see this del section end when it has the serial */
+	if(rr->type != TYPE_SOA && soa_rr_get_serial(rr) != data->newserial) {
+		return 1;
 	}
 	ixfr_trim_capacity(&data->del, &data->del_len, &capacity);
-	return 1;
+	return 2;
 }
 
 /* read ixfr data add section */
 static int ixfr_data_readadd(struct ixfr_data* data, struct zone* zone,
-	FILE* in, const char* ixfrfile, struct region* tempregion,
+	struct rr *rr, zone_parser_t *parser, struct region* tempregion,
 	struct domain_table* temptable, struct zone* tempzone)
 {
-	struct rr* rr;
 	size_t capacity = 0;
-	while(1) {
-		if(!ixfr_data_readrr(zone, in, ixfrfile, tempregion, temptable,
-			tempzone, &rr))
-			return 0;
-		if(!ixfr_putrr(domain_dname(rr->owner), rr->type, rr->klass, rr->ttl, rr->rdatas, rr->rdata_count, &data->add, &data->add_len, &capacity)) {
-			log_msg(LOG_ERR, "zone %s ixfr data %s: cannot allocate space",
-				zone->opts->name, ixfrfile);
-			return 0;
-		}
-		if(rr->type == TYPE_SOA &&
-			soa_rr_get_serial(rr) == data->newserial) {
-			/* end of add section. */
-			clear_temp_table_of_rr(temptable, tempzone, rr);
-			region_free_all(tempregion);
-			break;
-		}
-		clear_temp_table_of_rr(temptable, tempzone, rr);
-		region_free_all(tempregion);
+	if(!ixfr_putrr(domain_dname(rr->owner), rr->type, rr->klass, rr->ttl, rr->rdatas, rr->rdata_count, &data->add, &data->add_len, &capacity)) {
+		zone_error(parser, "zone %s ixfr data: cannot allocate space",
+			zone->opts->name);
+		return 0;
+	}
+	clear_temp_table_of_rr(temptable, tempzone, rr);
+	region_free_all(tempregion);
+	if(rr->type != TYPE_SOA || soa_rr_get_serial(rr) != data->newserial) {
+		return 1;
 	}
 	ixfr_trim_capacity(&data->add, &data->add_len, &capacity);
-	return 1;
+	return 2;
+}
+
+struct ixfr_data_state {
+	struct zone *zone;
+	struct ixfr_data *data;
+	struct region *tempregion, *stayregion;
+	struct domain_table *temptable;
+	struct zone *tempzone;
+	uint32_t *dest_serial;
+	size_t rr_count, soa_rr_count;
+};
+
+/* read one RR from file */
+static int32_t ixfr_data_accept(
+	zone_parser_t *parser,
+	const zone_name_t *name,
+	uint16_t type,
+	uint16_t class,
+	uint32_t ttl,
+	uint16_t rdlength,
+	const uint8_t *rdata,
+	void *user_data)
+{
+	struct rr *rr;
+	const struct dname *dname;
+	struct domain *domain;
+	struct buffer buffer;
+	union rdata_atom *rdatas;
+	ssize_t rdata_count;
+	struct ixfr_data_state *state = (struct ixfr_data_state *)user_data;
+
+	assert(parser);
+
+	buffer_create_from(&buffer, rdata, rdlength);
+
+	dname = dname_make(state->tempregion, name->octets, 1);
+	assert(dname);
+	domain = domain_table_insert(state->temptable, dname);
+	assert(domain);
+
+	rdata_count = rdata_wireformat_to_rdata_atoms(
+		state->tempregion, state->temptable, type, rdlength, &buffer, &rdatas);
+	assert(rdata_count > 0);
+	rr = region_alloc(state->tempregion, sizeof(*rr));
+	assert(rr);
+	rr->owner = domain;
+	rr->rdatas = rdatas;
+	rr->ttl = ttl;
+	rr->type = type;
+	rr->klass = class;
+	rr->rdata_count = rdata_count;
+
+	if (state->rr_count == 0) {
+		if (!ixfr_data_readnewsoa(state->data, state->zone, rr, parser,
+		                          state->tempregion, state->temptable,
+		                          state->tempzone, *state->dest_serial))
+			return ZONE_SEMANTIC_ERROR;
+	} else if (state->rr_count == 1) {
+		if(!ixfr_data_readoldsoa(state->data, state->zone, rr, parser,
+		                         state->tempregion, state->temptable,
+		                         state->tempzone, state->dest_serial))
+			return ZONE_SEMANTIC_ERROR;
+	} else if (state->soa_rr_count == 0) {
+		switch (ixfr_data_readdel(state->data, state->zone, rr, parser,
+		                          state->tempregion, state->temptable,
+		                          state->tempzone))
+		{
+			case 0:
+				return ZONE_SEMANTIC_ERROR;
+			case 1:
+				break;
+			case 2:
+				state->soa_rr_count++;
+				break;
+		}
+	} else if (state->soa_rr_count == 1) {
+		switch (ixfr_data_readadd(state->data, state->zone, rr, parser,
+		                          state->tempregion, state->temptable,
+		                          state->tempzone))
+		{
+			case 0:
+				return ZONE_SEMANTIC_ERROR;
+			case 1:
+				break;
+			case 2:
+				state->soa_rr_count++;
+				break;
+		}
+	}
+
+	state->rr_count++;
+	return 0;
+}
+
+static void ixfr_data_log(
+	zone_parser_t *parser,
+	uint32_t category,
+	const char *file,
+	size_t line,
+	const char *message,
+	void *user_data)
+{
+	int priority = LOG_ERR;
+	(void)parser;
+	(void)file;
+	(void)line;
+	(void)user_data;
+	if (category == ZONE_WARNING)
+		priority = LOG_WARNING;
+	log_msg(priority, "%s", message);
 }
 
 /* read ixfr data from file */
-static int ixfr_data_read(struct nsd* nsd, struct zone* zone, FILE* in,
+static int ixfr_data_read(struct nsd* nsd, struct zone* zone,
 	const char* ixfrfile, uint32_t* dest_serial, int file_num)
 {
-	struct ixfr_data* data = NULL;
-	struct region* tempregion, *stayregion;
-	struct domain_table* temptable;
-	struct zone* tempzone;
+	struct ixfr_data_state state = { 0 };
 
+	if(!zone->apex) {
+		return 0;
+	}
 	if(zone->ixfr &&
 		zone->ixfr->data->count == zone->opts->pattern->ixfr_number) {
 		VERBOSITY(3, (LOG_INFO, "zone %s skip %s IXFR data because only %d ixfr-number configured",
@@ -2592,74 +2617,74 @@ static int ixfr_data_read(struct nsd* ns
 	/* the file has header comments, new soa, old soa, delsection,
 	 * addsection. The delsection and addsection end in a SOA of oldver
 	 * and newver respectively. */
-	data = xalloc_zero(sizeof(*data));
-	data->file_num = file_num;
+	state.zone = zone;
+	state.data = xalloc_zero(sizeof(*state.data));
+	state.data->file_num = file_num;
 
+	state.dest_serial = dest_serial;
 	/* the temp region is cleared after every RR */
-	tempregion = region_create(xalloc, free);
+	state.tempregion = region_create(xalloc, free);
 	/* the stay region holds the temporary data that stays between RRs */
-	stayregion = region_create(xalloc, free);
-	temptable = domain_table_create(stayregion);
-	tempzone = region_alloc_zero(stayregion, sizeof(zone_type));
+	state.stayregion = region_create(xalloc, free);
+	state.temptable = domain_table_create(state.stayregion);
+	state.tempzone = region_alloc_zero(state.stayregion, sizeof(*state.tempzone));
 	if(!zone->apex) {
-		ixfr_data_free(data);
-		region_destroy(tempregion);
-		region_destroy(stayregion);
+		ixfr_data_free(state.data);
+		region_destroy(state.tempregion);
+		region_destroy(state.stayregion);
 		return 0;
 	}
-	tempzone->apex = domain_table_insert(temptable,
+	state.tempzone->apex = domain_table_insert(state.temptable,
 		domain_dname(zone->apex));
-	temptable->root->usage++;
-	tempzone->apex->usage++;
-	tempzone->opts = zone->opts;
+	state.temptable->root->usage++;
+	state.tempzone->apex->usage++;
+	state.tempzone->opts = zone->opts;
 	/* switch to per RR region for new allocations in temp domain table */
-	temptable->region = tempregion;
+	state.temptable->region = state.tempregion;
 
-	if(!ixfr_data_readnewsoa(data, zone, in, ixfrfile, tempregion,
-		temptable, tempzone, *dest_serial)) {
-		ixfr_data_free(data);
-		region_destroy(tempregion);
-		region_destroy(stayregion);
-		return 0;
-	}
-	if(!ixfr_data_readoldsoa(data, zone, in, ixfrfile, tempregion,
-		temptable, tempzone, dest_serial)) {
-		ixfr_data_free(data);
-		region_destroy(tempregion);
-		region_destroy(stayregion);
-		return 0;
-	}
-	if(!ixfr_data_readdel(data, zone, in, ixfrfile, tempregion, temptable,
-		tempzone)) {
-		ixfr_data_free(data);
-		region_destroy(tempregion);
-		region_destroy(stayregion);
-		return 0;
-	}
-	if(!ixfr_data_readadd(data, zone, in, ixfrfile, tempregion, temptable,
-		tempzone)) {
-		ixfr_data_free(data);
-		region_destroy(tempregion);
-		region_destroy(stayregion);
-		return 0;
+  {
+		const struct dname *origin;
+		zone_parser_t parser;
+		zone_options_t options;
+		zone_name_buffer_t name_buffer;
+		zone_rdata_buffer_t rdata_buffer;
+		zone_buffers_t buffers = { 1, &name_buffer, &rdata_buffer };
+		memset(&options, 0, sizeof(options));
+
+		origin = domain_dname(zone->apex);
+		options.origin.octets = dname_name(origin);
+		options.origin.length = origin->name_size;
+		options.no_includes = true;
+		options.pretty_ttls = false;
+		options.default_ttl = DEFAULT_TTL;
+		options.default_class = CLASS_IN;
+		options.log.callback = &ixfr_data_log;
+		options.accept.callback = &ixfr_data_accept;
+
+		if(zone_parse(&parser, &options, &buffers, ixfrfile, &state) != 0) {
+			ixfr_data_free(state.data);
+			region_destroy(state.tempregion);
+			region_destroy(state.stayregion);
+			return 0;
+		}
 	}
 
-	region_destroy(tempregion);
-	region_destroy(stayregion);
+	region_destroy(state.tempregion);
+	region_destroy(state.stayregion);
 
 	if(!zone->ixfr)
 		zone->ixfr = zone_ixfr_create(nsd);
 	if(zone->opts->pattern->ixfr_size != 0 &&
-		zone->ixfr->total_size + ixfr_data_size(data) >
+		zone->ixfr->total_size + ixfr_data_size(state.data) >
 		zone->opts->pattern->ixfr_size) {
 		VERBOSITY(3, (LOG_INFO, "zone %s skip %s IXFR data because only ixfr-size: %u configured, and it is %u size",
-			zone->opts->name, ixfrfile, (unsigned)zone->opts->pattern->ixfr_size, (unsigned)ixfr_data_size(data)));
-		ixfr_data_free(data);
+			zone->opts->name, ixfrfile, (unsigned)zone->opts->pattern->ixfr_size, (unsigned)ixfr_data_size(state.data)));
+		ixfr_data_free(state.data);
 		return 0;
 	}
-	zone_ixfr_add(zone->ixfr, data, 0);
+	zone_ixfr_add(zone->ixfr, state.data, 0);
 	VERBOSITY(3, (LOG_INFO, "zone %s read %s IXFR data of %u bytes",
-		zone->opts->name, ixfrfile, (unsigned)ixfr_data_size(data)));
+		zone->opts->name, ixfrfile, (unsigned)ixfr_data_size(state.data)));
 	return 1;
 }
 
@@ -2669,27 +2694,13 @@ static int ixfr_read_one_more_file(struc
 	const char* zfile, int num_files, uint32_t *dest_serial)
 {
 	char ixfrfile[1024+24];
-	FILE* in;
+	struct stat statbuf;
 	int file_num = num_files+1;
 	make_ixfr_name(ixfrfile, sizeof(ixfrfile), zfile, file_num);
-	in = fopen(ixfrfile, "r");
-	if(!in) {
-		if(errno == ENOENT) {
-			/* the file does not exist, we reached the end
-			 * of the list of IXFR files */
-			return 0;
-		}
-		log_msg(LOG_ERR, "could not read zone %s IXFR file %s: %s",
-			zone->opts->name, ixfrfile, strerror(errno));
+	/* if the file does not exist, all transfers have been read */
+	if (stat(ixfrfile, &statbuf) != 0 && errno == ENOENT)
 		return 0;
-	}
-	warn_if_directory("IXFR data", in, ixfrfile);
-	if(!ixfr_data_read(nsd, zone, in, ixfrfile, dest_serial, file_num)) {
-		fclose(in);
-		return 0;
-	}
-	fclose(in);
-	return 1;
+	return ixfr_data_read(nsd, zone, ixfrfile, dest_serial, file_num);
 }
 
 void ixfr_read_from_file(struct nsd* nsd, struct zone* zone, const char* zfile)
Index: namedb.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/namedb.h,v
diff -u -p -r1.17 namedb.h
--- namedb.h	12 Apr 2024 15:53:34 -0000	1.17
+++ namedb.h	3 Sep 2025 13:53:31 -0000
@@ -139,7 +139,12 @@ struct zone
 #endif
 	struct zone_options* opts;
 	struct zone_ixfr* ixfr;
-	char*        filename; /* set if read from file, which file */
+	char *filename; /* set if read from file, which files */
+	/* list of include files to monitor for changes */
+	struct {
+		size_t count;
+		char **paths;
+	} includes;
 	char*        logstr; /* set for zone xfer, the log string */
 	struct timespec mtime; /* time of last modification */
 	unsigned     zonestatid; /* array index for zone stats */
@@ -400,6 +405,7 @@ namedb_find_or_create_zone(namedb_type *
 	       	struct zone_options* zopt)
 { zone_type* zone = namedb_find_zone(db, dname);
   return zone ? zone : namedb_zone_create(db, dname, zopt); }
+void namedb_zone_free_filenames(namedb_type* db, zone_type* zone);
 void namedb_zone_delete(namedb_type* db, zone_type* zone);
 void namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt);
 void namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options);
Index: netio.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/netio.h,v
diff -u -p -r1.3 netio.h
--- netio.h	29 Jun 2023 19:38:49 -0000	1.3
+++ netio.h	3 Sep 2025 13:53:31 -0000
@@ -174,17 +174,4 @@ int netio_dispatch(netio_type *netio,
 		   const struct timespec *timeout,
 		   const sigset_t *sigmask);
 
-
-#ifdef __cplusplus
-inline netio_event_types_type
-operator | (netio_event_types_type lhs, netio_event_types_type rhs) {
-	return (netio_event_types_type) (lhs | rhs);
-}
-inline netio_event_types_type
-operator |= (netio_event_types_type &lhs, netio_event_types_type rhs) {
-	lhs = (netio_event_types_type) (lhs | rhs);
-	return lhs;
-}
-#endif /* __cplusplus */
-
 #endif /* NETIO_H */
Index: nsd-checkconf.8.in
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd-checkconf.8.in,v
diff -u -p -r1.43 nsd-checkconf.8.in
--- nsd-checkconf.8.in	12 Apr 2024 15:53:34 -0000	1.43
+++ nsd-checkconf.8.in	3 Sep 2025 13:53:31 -0000
@@ -1,5 +1,5 @@
-.TH "nsd\-checkconf" "8" "Apr  4, 2024" "NLnet Labs" "nsd 4.9.1"
-.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
+.TH "nsd\-checkconf" "8" "dec 12, 2024" "NLnet Labs" "nsd 4.11.0"
+.\" Copyright (c) 2001\-2024, NLnet Labs. All rights reserved.
 .\" See LICENSE for the license.
 .SH "NAME"
 .B nsd\-checkconf
@@ -57,14 +57,12 @@ prints out a list of configured zones.
 The special value
 .I patterns
 prints out a list of configured patterns.
-.P
-.RS
+.sp
 This option can be used to parse the config file from the shell. If the
 .B \-z
 option is given, but the 
 .B \-o 
 option is not given, nothing is printed. 
-.RE
 .TP
 .B \-s\fI keyname
 Prints the key secret (base64 blob) configured for this key in the 
@@ -83,15 +81,11 @@ for the given pattern name.
 Return the option specified with 
 .B \-o
 for zone 'zonename'.
-.P
-.RS
+.sp
 If this option is not given, the server section of the config file
 is used.
-.RE
-.P
-.RS
+.sp
 The \-o, \-s and \-z option print configfile options to standard output. 
-.RE
 .SH "FILES"
 .TP
 @nsdconfigfile@
Index: nsd-checkconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd-checkconf.c,v
diff -u -p -r1.37 nsd-checkconf.c
--- nsd-checkconf.c	12 Apr 2024 15:53:34 -0000	1.37
+++ nsd-checkconf.c	3 Sep 2025 13:53:31 -0000
@@ -411,8 +411,10 @@ config_print_zone(nsd_options_type* opt,
 		SERV_GET_BIN(hide_version, o);
 		SERV_GET_BIN(hide_identity, o);
 		SERV_GET_BIN(drop_updates, o);
+		SERV_GET_BIN(reload_config, o);
 		SERV_GET_BIN(zonefiles_check, o);
 		SERV_GET_BIN(log_time_ascii, o);
+		SERV_GET_BIN(log_time_iso, o);
 		SERV_GET_BIN(round_robin, o);
 		SERV_GET_BIN(minimal_responses, o);
 		SERV_GET_BIN(confine_to_zone, o);
@@ -438,6 +440,7 @@ config_print_zone(nsd_options_type* opt,
 		SERV_GET_STR(tls_port, o);
 		SERV_GET_STR(tls_cert_bundle, o);
 		SERV_GET_STR(cookie_secret, o);
+		SERV_GET_STR(cookie_staging_secret, o);
 		SERV_GET_STR(cookie_secret_file, o);
 		SERV_GET_BIN(answer_cookie, o);
 		/* int */
@@ -671,6 +674,7 @@ config_test_print_server(nsd_options_typ
 	print_string_var("xfrdir:", opt->xfrdir);
 	printf("\txfrd-reload-timeout: %d\n", opt->xfrd_reload_timeout);
 	printf("\tlog-time-ascii: %s\n", opt->log_time_ascii?"yes":"no");
+	printf("\tlog-time-iso: %s\n", opt->log_time_iso?"yes":"no");
 	printf("\tround-robin: %s\n", opt->round_robin?"yes":"no");
 	printf("\tminimal-responses: %s\n", opt->minimal_responses?"yes":"no");
 	printf("\tconfine-to-zone: %s\n",
@@ -706,6 +710,7 @@ config_test_print_server(nsd_options_typ
 	printf("\trrl-ipv6-prefix-length: %d\n", (int)opt->rrl_ipv6_prefix_length);
 	printf("\trrl-whitelist-ratelimit: %d\n", (int)opt->rrl_whitelist_ratelimit);
 #endif
+	printf("\treload-config: %s\n", opt->reload_config?"yes":"no");
 	printf("\tzonefiles-check: %s\n", opt->zonefiles_check?"yes":"no");
 	printf("\tzonefiles-write: %d\n", opt->zonefiles_write);
 	print_string_var("tls-service-key:", opt->tls_service_key);
@@ -714,10 +719,15 @@ config_test_print_server(nsd_options_typ
 	print_string_var("tls-port:", opt->tls_port);
 	print_string_var("tls-cert-bundle:", opt->tls_cert_bundle);
 	printf("\tanswer-cookie: %s\n", opt->answer_cookie?"yes":"no");
-	if (opt->cookie_secret)
-		print_string_var("cookie-secret:", opt->cookie_secret);
-	if (opt->cookie_secret_file)
+	print_string_var("cookie-secret:", opt->cookie_secret);
+	print_string_var("cookie-staging-secret:", opt->cookie_staging_secret);
+	if(opt->cookie_secret_file_is_default) {
+		print_string_var("#cookie-secret-file:", opt->cookie_secret_file);
+	} else if(opt->cookie_secret_file) {
 		print_string_var("cookie-secret-file:", opt->cookie_secret_file);
+	} else {
+		print_string_var("cookie-secret-file:", "");
+	}
 	if(opt->proxy_protocol_port) {
 		struct proxy_protocol_port_list* p;
 		for(p = opt->proxy_protocol_port; p; p = p->next)
Index: nsd-checkzone.8.in
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd-checkzone.8.in,v
diff -u -p -r1.27 nsd-checkzone.8.in
--- nsd-checkzone.8.in	12 Apr 2024 15:53:34 -0000	1.27
+++ nsd-checkzone.8.in	3 Sep 2025 13:53:31 -0000
@@ -1,5 +1,5 @@
-.TH "nsd\-checkzone" "8" "Apr  4, 2024" "NLnet Labs" "nsd 4.9.1"
-.\" Copyright (c) 2014, NLnet Labs. All rights reserved.
+.TH "nsd\-checkzone" "8" "dec 12, 2024" "NLnet Labs" "nsd 4.11.0"
+.\" Copyright (c) 2014-2024, NLnet Labs. All rights reserved.
 .\" See LICENSE for the license.
 .SH "NAME"
 .B nsd\-checkzone
@@ -24,7 +24,8 @@ Print usage help information and exit.
 The name of the zone to check, eg. "example.com".
 .TP
 .I zonefile
-The file to read, eg. "zones/example.com.zone.signed".
+The file to read, eg. "zones/example.com.zone.signed". Use "-" to read
+from stdin.
 .TP
 .B \-p
 Print the zone contents to stdout if the zone is ok. This prints the
@@ -36,14 +37,14 @@ command write does.
 Create an IXFR from the differences between the old zone file and the
 new zone file. The argument to the \-i option is the old zone file,
 the other zonefile argument passed is the new zonefile.
-.IP
+.sp
 The difference is computed between the two zonefiles by keeping one
 version of the zone in memory, and another version in a temporary
 file. The temporary file is located at the zonefile directory. This is
 also where the result is written, to a file with the zonefile name,
 ending with '.ixfr'. This is also where NSD reads it when IXFRs are
 configured for the zone.
-.IP
+.sp
 The other existing ixfr files are renamed to become older IXFR contents
 for the zone, if any such files exist.  If the output file already exists
 with the correct contents, no new file is created. The contents of the
Index: nsd-checkzone.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd-checkzone.c,v
diff -u -p -r1.7 nsd-checkzone.c
--- nsd-checkzone.c	20 Dec 2023 17:29:01 -0000	1.7
+++ nsd-checkzone.c	3 Sep 2025 13:53:31 -0000
@@ -67,7 +67,7 @@ check_zone(struct nsd* nsd, const char* 
 	zone = namedb_zone_create(nsd->db, dname, zo);
 
 	if(oldzone) {
-		errors = zonec_read(name, oldzone, zone);
+		errors = zonec_read(nsd->db, nsd->db->domains, name, oldzone, zone);
 		if(errors > 0) {
 			printf("zone %s file %s has %u errors\n", name, oldzone, errors);
 			exit(1);
@@ -80,7 +80,7 @@ check_zone(struct nsd* nsd, const char* 
 	}
 
 	/* read the zone */
-	errors = zonec_read(name, fname, zone);
+	errors = zonec_read(nsd->db, nsd->db->domains, name, fname, zone);
 	if(errors > 0) {
 		printf("zone %s file %s has %u errors\n", name, fname, errors);
 		ixfr_create_cancel(ixfrcr);
@@ -116,7 +116,7 @@ int writepid(struct nsd * ATTR_UNUSED(ns
 {
 	        return 0;
 }
-void unlinkpid(const char * ATTR_UNUSED(file))
+void unlinkpid(const char * ATTR_UNUSED(file), const char* ATTR_UNUSED(username))
 {
 }
 void bind8_stats(struct nsd * ATTR_UNUSED(nsd))
Index: nsd-control.8.in
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd-control.8.in,v
diff -u -p -r1.31 nsd-control.8.in
--- nsd-control.8.in	12 Apr 2024 15:53:34 -0000	1.31
+++ nsd-control.8.in	3 Sep 2025 13:53:31 -0000
@@ -1,5 +1,5 @@
-.TH "nsd\-control" "8" "Apr  4, 2024" "NLnet Labs" "nsd 4.9.1"
-.\" Copyright (c) 2011, NLnet Labs. All rights reserved.
+.TH "nsd\-control" "8" "dec 12, 2024" "NLnet Labs" "nsd 4.11.0"
+.\" Copyright (c) 2011-2024, NLnet Labs. All rights reserved.
 .\" See LICENSE for the license.
 .SH "NAME"
 .B nsd\-control,
@@ -190,27 +190,27 @@ restart, for lasting changes edit the ns
 .B add_cookie_secret <secret>
 Add or replace a cookie secret persistently. <secret> needs to be an 128 bit
 hex string.
-
+.sp
 Cookie secrets can be either \fIactive\fR or \fIstaging\fR. \fIActive\fR cookie
 secrets are used to create DNS Cookies, but verification of a DNS Cookie
 succeeds with any of the \fIactive\fR or \fIstaging\fR cookie secrets. The
 state of the current cookie secrets can be printed with the
 \fBprint_cookie_secrets\fR command.
-
+.sp
 When there are no cookie secrets configured yet, the <secret> is added as
 \fIactive\fR. If there is already an \fIactive\fR cookie secret, the <secret>
 is added as \fIstaging\fR or replacing an existing \fIstaging\fR secret.
-
+.sp
 To "roll" a cookie secret used in an anycast set. The new secret has to be
 added as staging secret to \fBall\fR nodes in the anycast set. When \fBall\fR
 nodes can verify DNS Cookies with the new secret, the new secret can be
 activated with the \fBactivate_cookie_secret\fR command. After \fBall\fR nodes
 have the new secret \fIactive\fR for at least one hour, the previous secret can
 be dropped with the \fBdrop_cookie_secret\fR command.
-
+.sp
 Persistence is accomplished by writing to a file which if configured with the
 \fBcookie\-secret\-file\fR option in the server section of the config file.
-The default value for that is: @configdir@/nsd_cookiesecrets.txt .
+The default value for that is: @cookiesecretsfile@ .
 .TP
 .B drop_cookie_secret
 Drop the \fIstaging\fR cookie secret.
Index: nsd-control.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd-control.c,v
diff -u -p -r1.19 nsd-control.c
--- nsd-control.c	12 Apr 2024 15:53:34 -0000	1.19
+++ nsd-control.c	3 Sep 2025 13:53:31 -0000
@@ -399,7 +399,11 @@ setup_ssl(SSL_CTX* ctx, int fd)
 	/* check authenticity of server */
 	if(SSL_get_verify_result(ssl) != X509_V_OK)
 		ssl_err("SSL verification failed");
+#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
+	x = SSL_get1_peer_certificate(ssl);
+#else
 	x = SSL_get_peer_certificate(ssl);
+#endif
 	if(!x)
 		ssl_err("Server presented no peer certificate");
 	X509_free(x);
Index: nsd-mem.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd-mem.c,v
diff -u -p -r1.7 nsd-mem.c
--- nsd-mem.c	12 Apr 2024 15:53:34 -0000	1.7
+++ nsd-mem.c	3 Sep 2025 13:53:31 -0000
@@ -219,7 +219,7 @@ int writepid(struct nsd * ATTR_UNUSED(ns
 {
 	        return 0;
 }
-void unlinkpid(const char * ATTR_UNUSED(file))
+void unlinkpid(const char * ATTR_UNUSED(file), const char* ATTR_UNUSED(username))
 {
 }
 void bind8_stats(struct nsd * ATTR_UNUSED(nsd))
Index: nsd.8.in
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd.8.in,v
diff -u -p -r1.45 nsd.8.in
--- nsd.8.in	17 Aug 2024 09:07:33 -0000	1.45
+++ nsd.8.in	3 Sep 2025 13:53:32 -0000
@@ -1,9 +1,9 @@
-.TH "NSD" "8" "Apr  4, 2024" "NLnet Labs" "NSD 4.9.1"
-.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
+.TH "NSD" "8" "dec 12, 2024" "NLnet Labs" "NSD 4.11.0"
+.\" Copyright (c) 2001\-2024, NLnet Labs. All rights reserved.
 .\" See LICENSE for the license.
 .SH "NAME"
 .B nsd
-\- Name Server Daemon (NSD) version 4.9.1.
+\- Name Server Daemon (NSD) version 4.11.0.
 .SH "SYNOPSIS"
 .B nsd
 .RB [ \-4 ] 
@@ -13,8 +13,6 @@
 .RB [ \-c
 .IR configfile ]
 .RB [ \-d ] 
-.RB [ \-f
-.IR database ]
 .RB [ \-h ] 
 .RB [ \-i
 .IR identity ]
@@ -44,28 +42,22 @@
 is a complete implementation of an authoritative DNS nameserver. 
 Upon startup,
 .B NSD
-will read the database specified with 
-.B \-f
-.I database
-argument and put itself into background and answers queries on port 
-53 or a different port specified with 
+will read the configuration file and put itself into the background and
+answers queries on port 53 or a different port specified with
 .B \-p
 .I port
-option. The
-.I database
-is created if it does not exist. By default,
+option. By default,
 .B NSD 
 will bind to all local interfaces available. Use the 
 .B \-a
 .I ip\-address[@port]
-option to specify a single particular interface address to be
-bound. If this option is given more than once,
+option to specify a single particular interface address to be bound. If this
+option is given more than once,
 .B NSD
 will bind its UDP and TCP sockets to all the specified ip\-addresses
 separately. If IPv6 is enabled when 
 .B NSD 
 is compiled an IPv6 address can also be specified.
-.P
 .SH "OPTIONS"
 All the options can be specified in the configfile (
 .B \-c 
@@ -203,13 +195,15 @@ to standard error and exit.
 reacts to the following signals:
 .TP
 SIGTERM
+.br
 Stop answering queries, shutdown, and exit normally.
 .TP 
 SIGHUP
-Reload.  Scans zone files and if changed (mtime) reads
-them in.  Also reopens the logfile (assists logrotation).
+.br
+Reopen logfile (assists rotation) and optionally update TSIG keys and zones.
 .TP
 SIGUSR1
+.br
 Dump BIND8\-style statistics into the log. Ignored otherwise.
 .SH "FILES"
 .TP
Index: nsd.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd.c,v
diff -u -p -r1.47 nsd.c
--- nsd.c	12 Apr 2024 15:53:34 -0000	1.47
+++ nsd.c	3 Sep 2025 13:53:32 -0000
@@ -1,7 +1,7 @@
 /*
  * nsd.c -- nsd(8)
  *
- * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
+ * Copyright (c) 2001-2024, NLnet Labs. All rights reserved.
  *
  * See LICENSE for the license.
  *
@@ -145,7 +145,7 @@ version(void)
 		);
 #endif
 	fprintf(stderr,
-		"Copyright (C) 2001-2020 NLnet Labs.  This is free software.\n"
+		"Copyright (C) 2001-2024 NLnet Labs.  This is free software.\n"
 		"There is NO warranty; not even for MERCHANTABILITY or FITNESS\n"
 		"FOR A PARTICULAR PURPOSE.\n");
 	exit(0);
@@ -659,7 +659,9 @@ readpid(const char *file)
 	}
 
 	if (((l = read(fd, pidbuf, sizeof(pidbuf)))) == -1) {
+		int errno_bak = errno;
 		close(fd);
+		errno = errno_bak;
 		return -1;
 	}
 
@@ -724,27 +726,32 @@ writepid(struct nsd *nsd)
 	}
 	close(fd);
 
-	if (chown(nsd->pidfile, nsd->uid, nsd->gid) == -1) {
-		log_msg(LOG_ERR, "cannot chown %u.%u %s: %s",
-			(unsigned) nsd->uid, (unsigned) nsd->gid,
-			nsd->pidfile, strerror(errno));
-		return -1;
-	}
-
 	return 0;
 }
 
 void
-unlinkpid(const char* file)
+unlinkpid(const char* file, const char* username)
 {
 	int fd = -1;
 
 	if (file && file[0]) {
 		/* truncate pidfile */
-		fd = open(file, O_WRONLY | O_TRUNC, 0644);
+		fd = open(file, O_WRONLY | O_TRUNC
+#ifdef O_NOFOLLOW
+			| O_NOFOLLOW
+#endif
+			, 0644);
 		if (fd == -1) {
 			/* Truncate the pid file.  */
-			log_msg(LOG_ERR, "can not truncate the pid file %s: %s", file, strerror(errno));
+			/* If there is a username configured, we assume that
+			 * due to privilege drops, NSD cannot truncate or
+			 * unlink the pid file. NSD does not chown the file
+			 * because that creates a privilege escape. */
+			if(username && username[0]) {
+				VERBOSITY(5, (LOG_INFO, "can not truncate the pid file %s: %s", file, strerror(errno)));
+			} else {
+				log_msg(LOG_ERR, "can not truncate the pid file %s: %s", file, strerror(errno));
+			}
 		} else {
 			close(fd);
 		}
@@ -754,9 +761,15 @@ unlinkpid(const char* file)
 			/* this unlink may not work if the pidfile is located
 			 * outside of the chroot/workdir or we no longer
 			 * have permissions */
-			VERBOSITY(3, (LOG_WARNING,
-				"failed to unlink pidfile %s: %s",
-				file, strerror(errno)));
+			if(username && username[0]) {
+				VERBOSITY(5, (LOG_INFO,
+					"failed to unlink pidfile %s: %s",
+					file, strerror(errno)));
+			} else {
+				VERBOSITY(3, (LOG_WARNING,
+					"failed to unlink pidfile %s: %s",
+					file, strerror(errno)));
+			}
 		}
 	}
 }
@@ -891,40 +904,6 @@ bind8_stats (struct nsd *nsd)
 }
 #endif /* BIND8_STATS */
 
-static
-int cookie_secret_file_read(nsd_type* nsd) {
-	char secret[NSD_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/];
-	char const* file = nsd->options->cookie_secret_file;
-	FILE* f;
-	int corrupt = 0;
-	size_t count;
-
-	assert( nsd->options->cookie_secret_file != NULL );
-	f = fopen(file, "r");
-	/* a non-existing cookie file is not an error */
-	if( f == NULL ) { return errno != EPERM; }
-	/* cookie secret file exists and is readable */
-	nsd->cookie_count = 0;
-	for( count = 0; count < NSD_COOKIE_HISTORY_SIZE; count++ ) {
-		size_t secret_len = 0;
-		ssize_t decoded_len = 0;
-		if( fgets(secret, sizeof(secret), f) == NULL ) { break; }
-		secret_len = strlen(secret);
-		if( secret_len == 0 ) { break; }
-		assert( secret_len <= sizeof(secret) );
-		secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len;
-		if( secret_len != NSD_COOKIE_SECRET_SIZE * 2 ) { corrupt++; break; }
-		/* needed for `hex_pton`; stripping potential `\n` */
-		secret[secret_len] = '\0';
-		decoded_len = hex_pton(secret, nsd->cookie_secrets[count].cookie_secret,
-		                       NSD_COOKIE_SECRET_SIZE);
-		if( decoded_len != NSD_COOKIE_SECRET_SIZE ) { corrupt++; break; }
-		nsd->cookie_count++;
-	}
-	fclose(f);
-	return corrupt == 0;
-}
-
 extern char *optarg;
 extern int optind;
 
@@ -966,14 +945,16 @@ main(int argc, char *argv[])
 	nsd.chrootdir	= 0;
 	nsd.nsid 	= NULL;
 	nsd.nsid_len 	= 0;
+	nsd.do_answer_cookie = 0;
 	nsd.cookie_count = 0;
+	nsd.cookie_secrets_source = COOKIE_SECRETS_NONE;
+	nsd.cookie_secrets_filename = NULL;
 
 	nsd.child_count = 0;
 	nsd.maximum_tcp_count = 0;
 	nsd.current_tcp_count = 0;
 	nsd.file_rotation_ok = 0;
 
-	nsd.do_answer_cookie = 1;
 
 	/* Set up our default identity to gethostname(2) */
 	if (gethostname(hostname, MAXHOSTNAMELEN) == 0) {
@@ -1194,6 +1175,7 @@ main(int argc, char *argv[])
 	nsd.ipv6_edns_size = nsd.options->ipv6_edns_size;
 #ifdef HAVE_SSL
 	nsd.tls_ctx = NULL;
+	nsd.tls_auth_ctx = NULL;
 #endif
 
 	if(udp_port == 0)
@@ -1249,35 +1231,7 @@ main(int argc, char *argv[])
 #endif /* IPV6 MTU) */
 #endif /* defined(INET6) */
 
-	nsd.do_answer_cookie = nsd.options->answer_cookie;
-	if (nsd.cookie_count > 0)
-		; /* pass */
-
-	else if (nsd.options->cookie_secret) {
-		ssize_t len = hex_pton(nsd.options->cookie_secret,
-			nsd.cookie_secrets[0].cookie_secret, NSD_COOKIE_SECRET_SIZE);
-		if (len != NSD_COOKIE_SECRET_SIZE ) {
-			error("A cookie secret must be a "
-			      "128 bit hex string");
-		}
-		nsd.cookie_count = 1;
-	} else {
-		size_t j;
-		size_t const cookie_secret_len = NSD_COOKIE_SECRET_SIZE;
-		/* Calculate a new random secret */
-		srandom(getpid() ^ time(NULL));
-
-		for( j = 0; j < NSD_COOKIE_HISTORY_SIZE; j++) {
-#if defined(HAVE_SSL)
-			if (!RAND_status()
-			    || !RAND_bytes(nsd.cookie_secrets[j].cookie_secret, cookie_secret_len))
-#endif
-			for (i = 0; i < cookie_secret_len; i++)
-				nsd.cookie_secrets[j].cookie_secret[i] = random_generate(256);
-		}
-		// XXX: all we have is a random cookie, still pretend we have one
-		nsd.cookie_count = 1;
-	}
+	reconfig_cookies(&nsd, nsd.options);
 
 	if (nsd.nsid_len == 0 && nsd.options->nsid) {
 		if (strlen(nsd.options->nsid) % 2 != 0) {
@@ -1486,7 +1440,14 @@ main(int argc, char *argv[])
 	log_msg(LOG_NOTICE, "%s starting (%s)", argv0, PACKAGE_STRING);
 
 	/* Do we have a running nsd? */
-	if(nsd.pidfile && nsd.pidfile[0]) {
+	/* When there is a username configured, we assume that due to
+	 * privilege drops, the pidfile could not be removed by NSD and
+	 * as such could be lingering around. We could not remove it,
+	 * and also not chown it as that creates privilege escape problems.
+	 * The init system could remove the pidfile after use for us, but
+	 * it is not sure if it is configured to do so. */
+	if(nsd.pidfile && nsd.pidfile[0] &&
+		!(nsd.username && nsd.username[0])) {
 		if ((oldpid = readpid(nsd.pidfile)) == -1) {
 			if (errno != ENOENT) {
 				log_msg(LOG_ERR, "can't read pidfile %s: %s",
@@ -1559,17 +1520,20 @@ main(int argc, char *argv[])
 #if defined(HAVE_SSL)
 	if(nsd.options->tls_service_key && nsd.options->tls_service_key[0]
 	   && nsd.options->tls_service_pem && nsd.options->tls_service_pem[0]) {
+		/* normal tls port with no client authentication */
 		if(!(nsd.tls_ctx = server_tls_ctx_create(&nsd, NULL,
-			nsd.options->tls_service_ocsp)))
+		     nsd.options->tls_service_ocsp)))
 			error("could not set up tls SSL_CTX");
+		/* tls-auth port with required client authentication */
+		if(nsd.options->tls_auth_port) {
+			if(!(nsd.tls_auth_ctx = server_tls_ctx_create(&nsd,
+			     (char*)nsd.options->tls_cert_bundle,
+			     nsd.options->tls_service_ocsp)))
+				error("could not set up tls SSL_CTX");
+		}
 	}
 #endif /* HAVE_SSL */
 
-	if(nsd.options->cookie_secret_file && nsd.options->cookie_secret_file[0]
-	   && !cookie_secret_file_read(&nsd) ) {
-		log_msg(LOG_ERR, "cookie secret file corrupt or not readable");
-	}
-
 	/* Unless we're debugging, fork... */
 	if (!nsd.debug) {
 		int fd;
@@ -1744,7 +1708,7 @@ main(int argc, char *argv[])
 #endif /* USE_DNSTAP */
 	}
 	if (server_prepare(&nsd) != 0) {
-		unlinkpid(nsd.pidfile);
+		unlinkpid(nsd.pidfile, nsd.username);
 		error("server preparation failed, %s could "
 			"not be started", argv0);
 	}
Index: nsd.conf.5.in
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd.conf.5.in,v
diff -u -p -r1.51 nsd.conf.5.in
--- nsd.conf.5.in	17 Aug 2024 09:07:33 -0000	1.51
+++ nsd.conf.5.in	3 Sep 2025 13:53:32 -0000
@@ -1,5 +1,5 @@
-.TH "nsd.conf" "5" "Apr  4, 2024" "NLnet Labs" "nsd 4.9.1"
-.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
+.TH "nsd.conf" "5" "dec 12, 2024" "NLnet Labs" "nsd 4.11.0"
+.\" Copyright (c) 2001\-2024, NLnet Labs. All rights reserved.
 .\" See LICENSE for the license.
 .SH "NAME"
 .B nsd.conf
@@ -9,89 +9,55 @@
 .SH "DESCRIPTION"
 This file is used to configure nsd(8). It specifies options for the nsd
 server, zone files, primaries and secondaries.
-.PP
+.sp
 The file format has attributes and values. Some attributes have attributes
 inside them. The notation is:
-.PP
+.sp
 attribute: value
-.PP
+.sp
 Comments start with # and last to the end of line. Empty lines are
 ignored, as is whitespace at the beginning of a line. Quotes must be used
 for values with spaces in them, eg. "file name.zone".
 .SH "EXAMPLE"
 An example of a short nsd.conf file is below.
-.LP
-# Example.com nsd.conf file
-.RS 0
+.sp
+.nf
+# Example nsd.conf file for example.com.
 # This is a comment.
-.RE
-.TP
+.sp
 server:
-.RS 5
-server-count: 1 # use this number of cpu cores
-.RE
-.RS 5
-username: @user@
-.RE
-.RS 5
-zonelistfile: @zonelistfile@
-.RE
-.RS 5
-logfile: @logfile@
-.RE
-.RS 5
-xfrdfile: @xfrdfile@
-.RE
-.TP
+	server-count: 1 # use this number of cpu cores
+	username: @user@
+	zonelistfile: @zonelistfile@
+	logfile: @logfile@
+	xfrdfile: @xfrdfile@
+.sp
 zone:
-.RS 5
-name: example.com
-.RE
-.RS 5
-zonefile: @configdir@/example.com.zone
-.RE
-.TP
+	name: example.com
+	zonefile: @configdir@/example.com.zone
+.sp
 zone:
-.RS 5
-# this server is the primary and 192.0.2.1 is the secondary.
-.RE
-.RS 5
-name: primaryzone.com
-.RE
-.RS 5
-zonefile: @configdir@/primaryzone.com.zone
-.RE
-.RS 5
-notify: 192.0.2.1 NOKEY
-.RE
-.RS 5
-provide-xfr: 192.0.2.1 NOKEY
-.RE
-.TP
+	# this server is the primary and 192.0.2.1 is the secondary.
+	name: primaryzone.com
+	zonefile: @configdir@/primaryzone.com.zone
+	notify: 192.0.2.1 NOKEY
+	provide-xfr: 192.0.2.1 NOKEY
+.sp
 zone:
-.RS 5
-# this server is the secondary and 192.0.2.2 is the primary.
-.RE
-.RS 5
-name: secondaryzone.com
-.RE
-.RS 5
-zonefile: @configdir@/secondaryzone.com.zone
-.RE
-.RS 5
-allow-notify: 192.0.2.2 NOKEY
-.RE
-.RS 5
-request-xfr: 192.0.2.2 NOKEY
-.RE
-.LP
+	# this server is the secondary and 192.0.2.2 is the primary.
+	name: secondaryzone.com
+	zonefile: @configdir@/secondaryzone.com.zone
+	allow-notify: 192.0.2.2 NOKEY
+	request-xfr: 192.0.2.2 NOKEY
+.fi
+.sp
 Then, use kill \-HUP to reload changes from primary zone files.
 And use kill \-TERM to stop the server.
 .SH "FILE FORMAT"
 There must be whitespace between keywords. Attribute keywords end
 with a colon ':'. An attribute is followed by its containing
 attributes, or a value.
-.P
+.sp
 At the top level, only
 .BR server: ,
 .BR verify: ,
@@ -116,8 +82,9 @@ attribute is used to define keys for aut
 attribute is followed by the zone options for zones that use the pattern.
 A
 .B tls-auth:
-attribute is used to define credentials for authenticating an outgoing TLS connection used for XFR-over-TLS.
-.P
+attribute is used to define authentication attributes for TLS connections used
+for XFR-over-TLS.
+.sp
 Files can be included using the
 .B include:
 directive. It can appear anywhere, and takes a single filename as an
@@ -130,7 +97,6 @@ wildcard match of files, eg. "foo/nsd.d/
 and '~' work, see \fBglob\fR(7).  If no files match the pattern, this
 is not an error.
 .SS "Server Options"
-.LP
 The global options (if not overridden from the NSD command-line) are
 taken from the
 .B server:
@@ -143,15 +109,15 @@ NSD will bind to the listed ip\-address.
 to bind multiple ip\-addresses. Optionally, a port number can be given.
 If none are given NSD listens to the wildcard interface. Same as command-line option
 .BR \-a.
-.IP
+.sp
 To limit which NSD server(s) listen on the given interface, specify one or
 more servers separated by whitespace after <ip>[@port]. Ranges can be used as
 a shorthand to specify multiple consecutive servers. By default every server
 will listen.
-.IP
+.sp
 If an interface name is used instead of ip4 or ip6, the list of IP addresses
 associated with that interface is picked up and used at server start.
-.IP
+.sp
 For servers with multiple IP addresses that can be used to send traffic
 to the internet, list them one by one, or the source address of replies
 could be wrong.  This is because if the udp socket associates a source
@@ -247,19 +213,16 @@ option
 .TP
 .B cpu\-affinity:\fR <number> <number> ...
 Overall CPU affinity for NSD server(s). Default is no affinity.
-.BR \-n .
 .TP
 .B server\-N\-cpu\-affinity:\fR <number>
 Bind NSD server specified by N to a specific core. Default is to have affinity
 set to every core specified in cpu\-affinity. This setting only takes effect
 if cpu\-affinity is enabled.
-.BR \-n
 .TP
 .B xfrd\-cpu\-affinity:\fR <number>
 Bind xfrd to a specific core. Default is to have affinity set to every core
 specified in cpu\-affinity. This setting only takes effect if cpu\-affinity is
 enabled.
-.BR \-n
 .TP
 .B tcp\-count:\fR <number>
 The maximum number of concurrent, active TCP connections by each server.
@@ -301,7 +264,7 @@ Increase it to allow more zone transfer 
 To save memory, this can be lowered, set it lower together with some other
 settings to have reduced memory footprint for NSD. xfrd\-tcp\-max: 32
 and xfrd\-tcp\-pipeline: 128 and rrl\-size: 1000
-.IP
+.sp
 This reduces memory footprint, other memory usage is caused mainly by
 the server\-count setting, the number of server processes, and the
 tcp\-count setting, which keeps buffers per server process, and by the
@@ -323,6 +286,14 @@ Same as command-line option
 .BR \-P .
 With "" there is no pidfile, for some startup management setups,
 where a pidfile is not useful to have.
+The default can be set at compile time, sometimes to "". Then the config
+option and commandline option can be used to specify that a pidfile is
+used, different from its compile time default value.
+The file is not chowned to the user from the \fBusername:\fR option, for
+permission safety reasons. It remains owned to the user by which the
+server was started. The file may not be removed after the server is
+finished and quit, since permissions for the username may not make
+this possible.
 .TP
 .B port:\fR <number>
 Answer queries on the specified port. Default is 53. Same as
@@ -386,15 +357,16 @@ once per the number of seconds. The defa
 This value specifies the verbosity level for (non\-debug) logging.
 Default is 0. 1 gives more information about incoming notifies and
 zone transfers. 2 lists soft warnings that are encountered. 3 prints
-more information.
-.IP
+more information. Same as command-line option
+.BR \-V .
+.sp
 Verbosity 0 will print warnings and errors, and other events that are
 important to keep NSD running.
-.IP
+.sp
 Verbosity 1 prints additionally messages of interest.  Successful notifies,
 successful incoming zone transfer (the zone is updated), failed incoming
 zone transfers or the inability to process zone updates.
-.IP
+.sp
 Verbosity 2 prints additionally soft errors, like connection resets over TCP.
 And notify refusal, and axfr request refusals.
 .TP
@@ -418,6 +390,10 @@ Log time in ascii, if "no" then in secon
 This chooses the format when logging to file.  The printout via syslog
 has a timestamp formatted by syslog.
 .TP
+.B log\-time\-iso:\fR <yes or no>
+Log time in ISO8601 format, if \fBlog\-time\-ascii:\fR yes is also set.
+Default is no.
+.TP
 .B round\-robin:\fR <yes or no>
 Enable round robin rotation of records in the answer.  This changes the
 order of records in the answer and this may balance load across them.
@@ -442,6 +418,14 @@ to get large responses.  Note that rrl r
 a ratelimiting type.  It sends truncation in response to UDP type ANY queries,
 and it allows TCP type ANY queries like normal.
 The default is yes.
+With the option turned off, NSD behaves according to RFC 8482 4.1. It minimizes
+the response with one RRset. Popular and not large types, like A, AAAA and
+MX are preferred, and large types, like DNSKEY and RRSIG are picked with a
+lower preference than other types. This makes the response smaller.
+.TP
+.B reload\-config:\fR <yes or no>
+Reload configuration file and update TSIG keys and zones on SIGHUP.
+Default is no.
 .TP
 .B zonefiles\-check:\fR <yes or no>
 Make NSD check the mtime of zone files on start and sighup.  If you
@@ -495,25 +479,56 @@ With the value 0 the rate is unlimited.
 .B answer\-cookie:\fR <yes or no>
 Enable to answer to requests containing DNS Cookies as specified in RFC7873.
 Default is yes.
+.sp
+DNS Cookies increase transaction security and provide limited protection 
+against denial-off-service amplification attacks. Server cookies will be
+created and included in responses. Server cookies are created based on the
+client cookie in the request, the current time, the client's IP address and
+a secret. When a client includes a valid server cookie in successive requests,
+the client will not be subjected to Request Rate Limiting (see \fBrrl\-ratelimit\fR).
+.sp
+Servers in an anycast deployment need to be able to verify each other's server
+cookies. For this they need to share the secret used to construct and verify
+the cookies. These cookie secrets can be specified in the configuration files
+with the \fBcookie\-secret\fR and \fBcookie\-staging\-secret\fR options.
+.sp
+If no cookie secrets are provided via configuration file, server cookie secrets
+can be added, dropped and activated with the \fInsd\-control\fR(8) tool.
+These secrets will be stored persistently in the cookie secret file for which
+the location can be specified with the \fBcookie\-secret\-file\fR option.
+.sp
+If no cookie secrets are provided via configuration file, and there is no or an
+empty cookie secret file, a random cookie secret is generated.
 .TP
 .B cookie\-secret:\fR <128 bit hex string>
-Servers in an anycast deployment need to be able to  verify  each other's DNS
-Server Cookies.  For  this they need to share the secret used to construct and
-verify the DNS Cookies.  Default is a 128 bits random secret generated at
-startup time.  This option is ignored if a \fBcookie\-secret\-file\fR is
-present.  In that case the secrets from that file are used in DNS Cookie
-calculations.
+The cookie secret with which server cookies are created and can be verified.
+If a \fBcookie\-secret\fR is specified via configuration file, cookie secrets
+from the cookie secret file will be ignored. 
+.TP
+.B cookie\-staging\-secret:\fR <128 bit hex string>
+A cookie secret with which server cookies can be verified, but will not be
+created. This is helpful in rolling cookie secrets in anycast setups.
+.sp
+A \fBcookie\-staging\-secret\fR can only be configured when there is also a
+\fBcookie\-secret\fR configured.
 .TP
 .B cookie\-secret\-file:\fR <filename>
-File from which the secrets are read used in DNS Cookie calculations. When this
-file exists, the secrets in this file are used and the secret specified by the
-\fBcookie-secret\fR option is ignored.
-Default is @configdir@/nsd_cookiesecrets.txt
-
-The content of this file must be manipulated with the \fBadd_cookie_secret\fR,
-\fBdrop_cookie_secret\fR and \fBactivate_cookie_secret\fR commands to the
-\fInsd\-control\fR(8) tool. Please see that manpage how to perform a safe
-cookie secret rollover.
+File from which the secrets are read used in DNS Cookie calculations. Secrets
+will only be read from this file if no cookie secrets are given in the
+configuration file via the \fBcookie\-secret\fR and
+\fBcookie\-staging\-secret\fR options.
+Default is "@cookiesecretsfile@"
+.sp
+In NSD version 4.10.1 and earlier, the default location of the cookie secret
+file was "@configdir@/nsd_cookiesecrets.txt". For migration purposes, cookie
+secrets will be read from that location if no value is given for the
+\fBcookie\-secret\-file\fR option and when the current default location
+("@cookiesecretsfile@") does not exist.
+.sp
+The content of the cookie secret file must be manipulated with the
+\fBadd_cookie_secret\fR, \fBdrop_cookie_secret\fR and
+\fBactivate_cookie_secret\fR commands to the \fInsd\-control\fR(8) tool.
+Please see that manpage how to perform a safe cookie secret rollover.
 .TP
 .B tls\-service\-key:\fR <filename>
 If enabled, the server provides TLS service on TCP sockets with the TLS
@@ -521,7 +536,7 @@ service port number.  The port number (8
 To turn it on, create an interface: option line in config with @port
 appended to the IP-address.  This creates the extra socket on which the
 DNS over TLS service is provided.
-.IP
+.sp
 The file is the private key for the TLS session. The public certificate is
 in the tls-service-pem file. Default is "", turned off. Requires a
 restart (a reload is not enough) if changed, because the private key is
@@ -534,19 +549,48 @@ The public key certificate pem file for 
 The ocsp pem file for the tls service, for OCSP stapling.  Default is "",
 turned off.  An external process prepares and updates the OCSP stapling data.
 Like this,
-.RS 9
+.sp
+.nf
 openssl ocsp -no_nonce \\
-   -respout /path/to/ocsp.pem \\
-   -CAfile /path/to/ca_and_any_intermediate.pem \\
-   -issuer /path/to/direct_issuer.pem \\
-   -cert /path/to/cert.pem \\
-   -url "$( openssl x509 -noout -text -in /path/to/cert.pem | grep 'OCSP - URI:' | cut -d: -f2,3 )"
-.RE
+	-respout /path/to/ocsp.pem \\
+	-CAfile /path/to/ca_and_any_intermediate.pem \\
+	-issuer /path/to/direct_issuer.pem \\
+	-cert /path/to/cert.pem \\
+	-url "$( openssl x509 -noout -ocsp_uri -in /path/to/cert.pem )"
+.fi
 .TP
 .B tls\-port:\fR <number>
 The port number on which to provide TCP TLS service, default is 853, only
 interfaces configured with that port number as @number get DNS over TLS service.
 .TP
+.B tls\-auth\-port:\fR <number>
+The port number on which to provide TCP TLS service to authenticated clients only.
+If you want to use mutual TLS authentication in Transfer over TLS (XoT) connections,
+this is where the primary server enables a dedicated port for this purpose. Certificates in
+.BR tls-cert-bundle
+are used for verifying the authenticity of a client or a secondary server.
+.sp
+Client (secondary) must enable
+.BR tls-auth ,
+configure
+.BR client-cert
+and
+.BR client-key
+and enable
+.BR tls-auth
+in zone configuration in order to authenticate to a remote (primary) server.
+.TP
+.B tls\-auth\-xfr\-only:\fR <yes or no>
+Allow zone transfers only on the
+.BR tls-auth-port
+port and only to authenticated clients. This works globally for all zones.
+A
+.BR provide-xfr
+access control list with
+.BR tls-auth
+is also required to allow and verify a connection.
+Requests for zone transfers on other ports are refused.
+.TP
 .B tls\-cert\-bundle:\fR <filename>
 If null or "", the default verify locations are used. Set it to the certificate
 bundle file, for example "/etc/pki/tls/certs/ca-bundle.crt". These certificates
@@ -578,10 +622,10 @@ NSD will bind to the listed addresses to
 Use 0.0.0.0 and ::0 to service the wildcard interface.  If none are given
 NSD listens to the localhost 127.0.0.1 and ::1 interfaces for control,
 if control is enabled with control\-enable.
-.IP
+
 If an interface name is used instead of ip4 or ip6, the list of IP addresses
 associated with that interface is picked up and used at server start.
-.IP
+
 With an absolute path, a unix local named pipe is used for control.  The
 file is created with user and group that is configured and access bits
 are set to allow members of the group access.  Further access can be
@@ -643,47 +687,42 @@ run to assess the zone with the update. 
 code of 0, the zone is considered good and will be served. Any other status
 code will designate the zone bad and the received update will be discarded.
 The zone will continue to be served but without the update.
-.P
-.RS
+.sp
 The following environment variables are available to verifiers:
-.P
-.RS
+.sp
 .B VERIFY_ZONE
-.RS
+.br
 The domain name of the zone to be verified.
-.RE
-.B VERIZFY_ZONE_ON_STDIN
-.RS
+.sp
+.B VERIFY_ZONE_ON_STDIN
+.br
 When the zone can be read from standard input (stdin), this variable is set
 to "yes", otherwise it is set to "no".
-.RE
+.sp
 .B VERIFY_IP_ADDRESSES
-.RS
+.br
 The first address on which the zones to be assessed will be served.
 If IPv6 is available an IPv6 address will be preferred over IPv4.
-.RE
+.sp
 .B VERIFY_PORT
-.RS
+.br
 The port number for \fBVERIFY_IP_ADDRESS\fR.
-.RE
+.sp
 .B VERIFY_IPV6_ADDRESS
-.RS
+.br
 The first IPv6 address on which the zones to be assessed will be served.
-.RE
+.sp
 .B VERIFY_IPV6_PORT
-.RS
+.br
 The port number for \fBVERIFY_IPV6_ADDRESS\fR.
-.RE
+.sp
 .B VERIFY_IPV4_ADDRESS
-.RS
-The first IPv4 address on which the zones to be assessed will be served.
-.RE
+.br
+The first IPv4 address of which the zones to be assessed will be served.
+.sp
 .B VERIFY_IPV4_PORT
-.RS
+.br
 The port number for \fBVERIFY_IPV4_ADDRESS\fR.
-.RE
-.RE
-.RE
 .TP
 .B verifier\-count:\fR <number>
 Maximum number of verifiers to run concurrently. Default is 1.
@@ -759,53 +798,50 @@ each zone.
 The file containing the zone information. If this attribute is present
 it is used to read and write the zone contents. If the attribute is
 absent it prevents writing out of the zone.
-.IP
+.sp
 The string is processed so that one string can be used (in a pattern)
 for a lot of different zones.  If the label or character does not exist the
 percent-character is replaced with a period for output (i.e. for the
 third character in a two letter domain name).
-.IP
+.sp
 .B %s\fR is replaced with the zone name.
-.IP
+.sp
 .B %1\fR is replaced with the first character of the zone name.
-.IP
+.sp
 .B %2\fR is replaced with the second character of the zone name.
-.IP
+.sp
 .B %3\fR is replaced with the third character of the zone name.
-.IP
+.sp
 .B %z\fR is replaced with the toplevel domain name of the zone.
-.IP
+.sp
 .B %y\fR is replaced with the next label under the toplevel domain.
-.IP
+.sp
 .B %x\fR is replaced with the next-next label under the toplevel domain.
 .TP
 .B allow\-query:\fR <ip\-spec> <key\-name | NOKEY | BLOCKED>
 Access control list.  When at least one \fBallow\-query\fR option is
-specified, then the in the \fBallow\-query\fR options specified addresses
-are are allowed to query the server for the zone.  Queries from unlisted or
+specified, then the specified addresses in the \fBallow\-query\fR options
+are allowed to query the server for the zone.  Queries from unlisted or
 specifically BLOCKED addresses are discarded. If NOKEY is given no TSIG
 signature is required.  BLOCKED supersedes other entries, other entries are
 scanned for a match in the order of the statements.  Without
 \fBallow\-query\fR options, queries are allowed from any IP address
 without TSIG key (which is the default).
-.P
-.RS
+.sp
 The ip\-spec is either a plain IP address (IPv4 or IPv6), or can be
 a subnet of the form 1.2.3.4/24, or masked like
 1.2.3.4&255.255.255.0 or a range of the form 1.2.3.4\-1.2.3.25.
 Note the ip\-spec ranges do not use spaces around the /, &, @ and \-
 symbols.
-.RE
 .TP
 .B allow\-notify:\fR <ip\-spec> <key\-name | NOKEY | BLOCKED>
 Access control list. The listed (primary) address is allowed to
-send notifies to this (secondary) server. Notifies from unlisted or
+send notifies to this (secondary) server via UDP or TCP. Notifies from unlisted or
 specifically BLOCKED addresses are discarded. If NOKEY is given no
 TSIG signature is required.
 BLOCKED supersedes other entries, other entries are scanned for a match
 in the order of the statements.
-.P
-.RS
+.sp
 The ip\-spec is either a plain IP address (IPv4 or IPv6), or can be
 a subnet of the form 1.2.3.4/24, or masked like
 1.2.3.4&255.255.255.0 or a range of the form 1.2.3.4\-1.2.3.25.
@@ -821,25 +857,23 @@ AXFR/IXFR on update. A port number can b
 for example 1.2.3.4@5300. The specified key is used during AXFR/IXFR. If
 tls-auth-name is included, the specified tls-auth clause will be used to
 perform authenticated XFR-over-TLS.
-.P
-.RS
+.sp
 If the AXFR option is given, the server will not be contacted with
 IXFR queries but only AXFR requests will be made to the server. This
 allows an NSD secondary to have a primary server that runs NSD. If
 the AXFR option is left out then both IXFR and AXFR requests are
 made to the primary server.
-.P
+.sp
 If the UDP option is given, the secondary will use UDP to transmit the IXFR
 requests. You should deploy TSIG when allowing UDP transport, to authenticate
 notifies and zone transfers. Otherwise, NSD is more vulnerable for
 Kaminsky\-style attacks. If the UDP option is left out then IXFR will be
 transmitted using TCP.
-.P
+.sp
 If a tls-auth-name is given then TLS (by default on port 853) will be used
-for all zone transfers for the zone. If authentication of the primary based on
-the specified tls-auth authentication information fails, the XFR request will
+for all zone transfers for the zone. If authentication of the primary, based on
+the specified tls-auth authentication information, fails the XFR request will
 not be sent. Support for TLS 1.3 is required for XFR-over-TLS.
-.RE
 .TP
 .B allow\-axfr\-fallback:\fR <yes or no>
 This option should be accompanied by request\-xfr. It (dis)allows NSD (as secondary)
@@ -851,7 +885,7 @@ If this option is 0, unlimited. Default 
 .TP
 .B notify:\fR <ip\-address> <key\-name | NOKEY>
 Access control list. The listed address (a secondary) is notified
-of updates to this zone. A port number can be added using a suffix of @number,
+of updates to this zone via UDP. A port number can be added using a suffix of @number,
 for example 1.2.3.4@5300. The specified key is used to sign the
 notify. Only on secondary configurations will NSD be able to detect
 zone updates (as it gets notified itself, or refreshes after a
@@ -861,32 +895,47 @@ time).
 This option should be accompanied by notify. It sets the number of retries
 when sending notifies.
 .TP
-.B provide\-xfr:\fR <ip\-spec> <key\-name | NOKEY | BLOCKED>
+.B provide\-xfr:\fR <ip\-spec> <key\-name | NOKEY | BLOCKED> [tls\-auth\-name]
 Access control list. The listed address (a secondary) is allowed to
 request XFR from this server. Zone data will be provided to the
 address. The specified key is used during XFR. For unlisted or
 BLOCKED addresses no data is provided and requests are discarded.
 BLOCKED supersedes other entries and other entries are scanned for a match
 in the order of the statements.
-.P
-.RS
+.sp
 The ip\-spec is either a plain IP address (IPv4 or IPv6), or can be
 a subnet of the form 1.2.3.4/24, or masked like
 1.2.3.4&255.255.255.0 or a range of the form 1.2.3.4\-1.2.3.25.
 A port number can be added using a suffix of @number, for example
 1.2.3.4@5300 or 1.2.3.4/24@5300 for port 5300. Note the ip\-spec
 ranges do not use spaces around the /, &, @ and \- symbols.
-.RE
+.sp
+If a tls-auth-name is given then TLS authentication of the secondary will be performed
+for zone transfer requests for the zone. The remote end must connect to the
+.BR tls-auth-port
+and must present a certificate with a
+SAN (Subject Alternative Name) DNS entry or CN (Common Name) entry equal to
+.BR auth-domain-name
+of the defined
+.BR tls-auth .
+The certificate validify is also verified with
+.BR tls-cert-bundle .
+If authentication of the secondary, based on the specified tls-auth authentication
+information, fails the XFR zone transfer will be refused. If the connection is performed
+on the
+.BR tls-port
+then no authentication will be performed and the transfer will not be refused.
+To enforce only authenticated zone transfers,
+.BR tls-auth-xfr-only
+should also be enabled. Support for TLS 1.3 is required for XFR-over-TLS.
 .TP
 .B outgoing\-interface:\fR <ip\-address>
 Access control list. The listed address is used to request AXFR|IXFR (in case of
 a secondary) or used to send notifies (in case of a primary).
-.P
-.RS
+.sp
 The ip\-address is a plain IP address (IPv4 or IPv6).
 A port number can be added using a suffix of @number, for example
 1.2.3.4@5300.
-.RE
 .TP
 .B store\-ixfr:\fR <yes or no>
 If enabled, IXFR contents are stored and provided to the set of clients
@@ -968,9 +1017,6 @@ Default no.  If enabled, checks all prim
 the higher version of all the configured primaries.  Useful if you have multiple
 primaries that have different version numbers served.
 .TP
-.B multi\-master\-check:\fR <yes or no>
-It is the same as multi\-primary\-check.
-.TP
 .B verify\-zone:\fR <yes or no>
 Enable or disable verification for this zone. Default is value\-zones
 configured in
@@ -999,7 +1045,7 @@ property, or if a group property is miss
 by the \fBcatalog\-member\-pattern\fR option is used. Group properties are valid
 if there is only a single value matching the name of a for member zones valid
 pattern.
-.IP
+.sp
 A zone with the option set to \fIproducer\fR, can be used to produce a
 catalog zone.  Member zones for catalog producer zones can be added with
 "\fInsd\-control addzone <zone> <pattern>\fR", where <pattern> has a
@@ -1009,17 +1055,17 @@ Catalog producer zones must be primary z
 \fBrequest\-xfr\fR option. Catalog producer zones will \fInot\fR read content
 from zone files, but will reconstruct the zone on startup from the member zone
 entries in @zonelistfile@, specified with the \fBzonelistfile\fR option.
-.IP
+.sp
 The status of both catalog consumer and producer zones can be verified with
 \fInsd\-control zonestatus\fR. It will show the number of member zones and, if
 the catalog zone is invalid, the reason for it to be invalid is shown.
 \fInsd\-control zonestatus\fR will also show the entry of a catalog member zone
 in the catalog (consumer or producer) zone as \fBcatalog-member-id:\fR.
-.IP
+.sp
 A catalog zone can either be catalog consumer zone or a catalog producer zone
 but not both. Likewise, catalog member zones can be either a member of catalog
 consumer zone or a catalog producer zone but not both.
-.IP
+.sp
 Catalog zones contain a list of zones that are served. Use \fBallow\-query:
 0.0.0.0/0 BLOCKED\fR and \fBallow\-query: ::0/0 BLOCKED\fR in a catalog zone 
 zone or pattern clause to prevent revealing the catalog. Also consider using
@@ -1032,7 +1078,7 @@ that have a missing or an invalid group 
 .TP
 .B catalog\-producer\-zone:\fR <zone\-name>
 This option can only be used in a pattern. Adding a zone using
-"\fInsd\-control addzone <zone> <pattern>\fR with a <pattern> containing this
+"\fInsd\-control addzone <zone> <pattern>\fR" with a <pattern> containing this
 option, will cause a catalog member entry to be created in the catalog producer
 zone <zone\-name>.  <zone\-name> must exist and must be a valid catalog
 producer zone.
@@ -1067,16 +1113,21 @@ dev-random output through a base64 encod
 .SS "TLS Auth Declarations"
 The
 .B tls-auth:
-clause establishes authentication attributes to use when authenticating
-the far end of an outgoing TLS connection used in access control lists for XFR-over-TLS.
-It has the following attributes.
+clause establishes attributes to use when authenticating the far end of a TLS connection
+as well as to define credentials to authenticate to a remote server. It is used in
+access control lists for XFR-over-TLS. It has the following attributes.
 .TP
 .B name:\fR <string>
 The tls-auth name. Used to refer to this TLS authentication information in the
 access control list.
 .TP
 .B auth\-domain\-name:\fR <string>
-The authentication domain name as defined in RFC8310.
+The authentication domain name as defined in RFC8310. Used to verify the certificate
+of the remote connecting server. When used by a primary server in
+.BR provide-xfr
+it verifies the secondary. When used by a secondary server in
+.BR request-xfr
+it verifies the primary.
 .TP
 .B client\-cert: <file name of clientcert.pem>
 If you want to use mutual TLS authentication, this is where the client
@@ -1149,145 +1200,98 @@ Enable to log auth response messages.  D
 These are responses from NSD to clients.
 .SH "NSD CONFIGURATION FOR BIND9 HACKERS"
 BIND9 is a name server implementation with its own configuration
-file format, named.conf(5). BIND9 types zones as 'Master' or 'Slave'.
-.SS "Slave zones"
+file format, named.conf(5). BIND9 types zones as 'Primary' or 'Secondary'.
+.SS "Secondary zones"
 For a secondary zone, the primary servers are listed. The primary servers are
 queried for zone data, and are listened to for update notifications.
 In NSD these two properties need to be configured separately, by listing
 the primary address in allow\-notify and request\-xfr statements.
-.P
+.sp
 In BIND9 you only need to provide allow\-notify elements for
 any extra sources of notifications (i.e. the operators), NSD needs to have
 allow\-notify for both primaries and operators. BIND9 allows
 additional transfer sources, in NSD you list those as request\-xfr.
-.P
+.sp
 Here is an example of a secondary zone in BIND9 syntax.
-.P
+.sp
+.nf
 # Config file for example.org
 options {
-.RS 5
-dnssec\-enable yes;
-.RE
-.RS 0
+	dnssec\-enable yes;
 };
-.RE
-.LP
+.sp
 key tsig.example.org. {
-.RS 5
-algorithm hmac\-md5;
-.RE
-.RS 5
-secret "aaaaaabbbbbbccccccdddddd";
-.RE
+	algorithm hmac\-md5;
+	secret "aaaaaabbbbbbccccccdddddd";
 };
-.LP
+.sp
 server 162.0.4.49 {
-.RS 5
-keys { tsig.example.org. ; };
-.RE
+	keys { tsig.example.org. ; };
 };
-.LP
+.sp
 zone "example.org" {
-.RS 5
-type secondary;
-.RE
-.RS 5
-file "secondary/example.org.signed";
-.RE
-.RS 5
-primaries { 162.0.4.49; };
-.RE
+	type secondary;
+	file "secondary/example.org.signed";
+	primaries { 162.0.4.49; };
 };
-.P
+.fi
 For NSD, DNSSEC is enabled automatically for zones that are signed. The
 dnssec\-enable statement in the options clause is not needed. In NSD
 keys are associated with an IP address in the access control list
 statement, therefore the server{} statement is not needed. Below is
 the same example in an NSD config file.
-.LP
+.sp
+.nf
 # Config file for example.org
-.RS 0
 key:
-.RE
-.RS 5
-name: tsig.example.org.
-.RE
-.RS 5
-algorithm: hmac\-md5
-.RE
-.RS 5
-secret: "aaaaaabbbbbbccccccdddddd"
-.RE
-.LP
+	name: tsig.example.org.
+	algorithm: hmac\-md5
+	secret: "aaaaaabbbbbbccccccdddddd"
+.sp
 zone:
-.RS 5
-name: "example.org"
-.RE
-.RS 5
-zonefile: "secondary/example.org.signed"
-.RE
-.RS 5
-# the primary is allowed to notify and will provide zone data.
-.RE
-.RS 5
-allow\-notify: 162.0.4.49 NOKEY
-.RE
-.RS 5
-request\-xfr: 162.0.4.49 tsig.example.org.
-.RE
-.P
+	name: "example.org"
+	zonefile: "secondary/example.org.signed"
+	# the primary is allowed to notify and will provide zone data.
+	allow\-notify: 162.0.4.49 NOKEY
+	request\-xfr: 162.0.4.49 tsig.example.org.
+.fi
+.sp
 Notice that the primary is listed twice, once to allow it to send notifies
 to this secondary server and once to tell the secondary server where to look for
 updates zone data. More allow\-notify and request\-xfr lines can be
 added to specify more primaries.
-.P
+.sp
 It is possible to specify extra allow\-notify lines for addresses
 that are also allowed to send notifications to this secondary server.
-.SS "Master zones"
+.SS "Primary zones"
 For a primary zone in BIND9, the secondary servers are listed. These secondary
 servers are sent notifications of updated and are allowed to request
 transfer of the zone data. In NSD these two properties need to be
 configured separately.
-.P
+.sp
 Here is an example of a primary zone in BIND9 syntax.
-.LP
+.sp
+.nf
 zone "example.nl" {
-.RS 5
-type primary;
-.RE
-.RS 5
-file "example.nl";
-.RE
+	type primary;
+	file "example.nl";
 };
-.LP
+.fi
+.sp
 In NSD syntax this becomes:
-.LP
+.sp
+.nf
 zone:
-.RS 5
-name: "example.nl"
-.RE
-.RS 5
-zonefile: "example.nl"
-.RE
-.RS 5
-# allow anybody to request xfr.
-.RE
-.RS 5
-provide\-xfr: 0.0.0.0/0 NOKEY
-.RE
-.RS 5
-provide\-xfr: ::0/0 NOKEY
-.RE
-.P
-.RS 5
+	name: "example.nl"
+	zonefile: "example.nl"
+	# allow anybody to request xfr.
+	provide\-xfr: 0.0.0.0/0 NOKEY
+	provide\-xfr: ::0/0 NOKEY
+.sp
 # to list a secondary server you would in general give
-.RE
-.RS 5
 # provide\-xfr: 1.2.3.4 tsig\-key.name.
-.RE
-.RS 5
 # notify: 1.2.3.4 NOKEY
-.RE
+.fi
 .SS "Other"
 NSD is an authoritative only DNS server. This means that it is
 meant as a primary or secondary server for zones, providing DNS
@@ -1303,7 +1307,7 @@ default
 .B NSD
 configuration file
 .SH "SEE ALSO"
-\fInsd\fR(8), \fInsd\-checkconf\fR(8), \fInsd\-control\fR(8)
+\fInsd\fR(8), \fInsd\-checkconf\fR(8), \fInsd\-checkzone\fR(8), \fInsd\-control\fR(8)
 .SH "AUTHORS"
 .B NSD
 was written by a combined team from NLnet Labs and RIPE NCC. Please see the
Index: nsd.conf.sample.in
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd.conf.sample.in,v
diff -u -p -r1.24 nsd.conf.sample.in
--- nsd.conf.sample.in	12 Apr 2024 15:53:34 -0000	1.24
+++ nsd.conf.sample.in	3 Sep 2025 13:53:32 -0000
@@ -195,6 +195,10 @@ server:
 	# log timestamp in ascii (y-m-d h:m:s.msec), yes is default.
 	# log-time-ascii: yes
 
+	# log timestamp in ISO8601 format if also log-time-ascii is enabled.
+	# (y-m-dTh:m:s.msec[+-]tzhours:tzminutes)
+	# log-time-iso: no
+
 	# round robin rotation of records in the answer.
 	# round-robin: no
 
@@ -216,6 +220,9 @@ server:
 	# default is 3600.
 	# zonefiles-write: 3600
 
+	# Reload nsd.conf and update TSIG keys and zones on SIGHUP.
+	# reload-config: no
+
 	# RRLconfig
 	# Response Rate Limiting, size of the hashtable. Default 1000000.
 	# rrl-size: 1000000
@@ -256,6 +263,18 @@ server:
 	# tls-service-ocsp: "path/to/ocsp.pem"
 	# tls-port: 853
 	
+	# Provides a dedidated TLS port where only authenticated clients can
+	# connect. Used for zone transfers to secondary servers. It uses
+	# tls-service-key and tls-service-pem and verifies client certificates
+	# using tls-cert-bundle.
+	# Default is "" (disabled). Requires restart to take effect.
+	# tls-auth-port: ""
+
+	# Allow zone transfers only on the tls-auth-port port and only to
+	# authenticated clients. Requests for zone transfers on other ports
+	# are refused. Default is no. Requires restart to change it.
+	# tls-auth-xfr-only: no
+
 	# Certificates used to authenticate connections made upstream for
 	# Transfers over TLS (XoT). Default is "" (default verify locations).
 	# tls-cert-bundle: "path/to/ca-bundle.pem"
@@ -325,12 +344,14 @@ remote-control:
 	# interfaces can be specified by IP address or interface name.
 	# with an interface name, all IP addresses associated with that
 	# interface are used.
-	# with an absolute path, a unix local named pipe is used for control
-	# (and key and cert files are not needed, use directory permissions).
 	# control-interface: 127.0.0.1
 	# control-interface: ::1
 	# control-interface: lo
 
+	# with an absolute path, a unix local named pipe is used for control
+	# (and key and cert files are not needed, use directory permissions).
+	# control-interface: @runstatedir@/nsd/nsd.sock
+
 	# port number for remote control operations (uses TLS over TCP).
 	# control-port: 8952
 
@@ -429,13 +450,13 @@ remote-control:
 	#allow-notify: 2001:db8::0/64 my_tsig_key_name
 	# By default, a secondary will request a zone transfer with IXFR/TCP.
 	# If you want to make use of IXFR/UDP use: UDP addr tsigkey
-	# for a master that only speaks AXFR (like NSD) use AXFR addr tsigkey
+	# for a primary that only speaks AXFR use AXFR addr tsigkey
 	# If you want to require use of XFR-over-TLS use: addr tsigkey tlsauthname
 	#request-xfr: 192.0.2.2 the_tsig_key_name
 	#request-xfr: 192.0.2.2 the_tsig_key_name the_tls_auth_name
 	# Attention: You cannot use UDP and AXFR together. AXFR is always over
 	# TCP. If you use UDP, we highly recommend you to deploy TSIG.
-	# Allow AXFR fallback if the master does not support IXFR. Default
+	# Allow AXFR fallback if the primary does not support IXFR. Default
 	# is yes.
 	#allow-axfr-fallback: yes
 	# set local interface for sending zone transfer requests.
@@ -467,8 +488,8 @@ remote-control:
 	# from that pattern are inserted into this one (as if it were a
 	# macro).  The statement can be given in between other statements,
 	# because the order of access control elements can make a difference
-	# (which master to request from first, which secondary to notify first).
-	#include-pattern: "common-masters"
+	# (which primary to request from first, which secondary to notify first).
+	#include-pattern: "common-primaries"
 
 	# Verify zone before publishing.
 	# Default is value of verify-zones in verify.
@@ -513,7 +534,7 @@ remote-control:
 	# name: "example.com"
 	# you can give a pattern here, all the settings from that pattern
 	# are then inserted at this point
-	# include-pattern: "master"
+	# include-pattern: "primary"
 	# You can also specify (additional) options directly for this zone.
 	# zonefile: "example.com.zone"
 	# request-xfr: 192.0.2.1 example.com.key
Index: nsd.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsd.h,v
diff -u -p -r1.15 nsd.h
--- nsd.h	20 Dec 2023 17:29:01 -0000	1.15
+++ nsd.h	3 Sep 2025 13:53:32 -0000
@@ -42,13 +42,6 @@ struct dt_collector;
 #define	NSD_REAP_CHILDREN 4
 #define	NSD_QUIT 5
 /*
- * PASS_TO_XFRD is followed by the u16(len in network order) and
- * then network packet contents.  packet is a notify(acl checked), or
- * xfr reply from a master(acl checked).
- * followed by u32(acl number that matched from notify/xfr acl).
- */
-#define NSD_PASS_TO_XFRD 6
-/*
  * RELOAD_REQ is sent when parent receives a SIGHUP and tells
  * xfrd that it wants to initiate a reload (and thus task swap).
  */
@@ -206,11 +199,20 @@ struct nsd_child
 #define NSD_COOKIE_HISTORY_SIZE 2
 #define NSD_COOKIE_SECRET_SIZE 16
 
-typedef struct cookie_secret cookie_secret_type;
 struct cookie_secret {
 	/** cookie secret */
 	uint8_t cookie_secret[NSD_COOKIE_SECRET_SIZE];
 };
+typedef struct cookie_secret cookie_secret_type;
+typedef cookie_secret_type cookie_secrets_type[NSD_COOKIE_HISTORY_SIZE];
+
+enum cookie_secrets_source {
+	COOKIE_SECRETS_NONE        = 0,
+	COOKIE_SECRETS_GENERATED   = 1,
+	COOKIE_SECRETS_FROM_FILE   = 2,
+	COOKIE_SECRETS_FROM_CONFIG = 3
+};
+typedef enum cookie_secrets_source cookie_secrets_source_type;
 
 /* NSD configuration and run-time variables */
 typedef struct nsd nsd_type;
@@ -354,26 +356,43 @@ struct	nsd
 	 * simultaneous with new serve childs. */
 	int *dt_collector_fd_swap;
 #endif /* USE_DNSTAP */
+	/* the pipes from the serve processes to xfrd, for passing through
+	 * NOTIFY messages, arrays of size child_count * 2.
+	 * Kept open for (re-)forks. */
+	int *serve2xfrd_fd_send, *serve2xfrd_fd_recv;
+	/* the pipes from the serve processes to the xfrd. Initially
+	 * these point halfway into serve2xfrd_fd_send, but during reload
+	 * the pointer is swapped with serve2xfrd_fd_send so that only one
+	 * serve child will write to the same fd simultaneously. */
+	int *serve2xfrd_fd_swap;
 	/* ratelimit for errors, time value */
 	time_t err_limit_time;
 	/* ratelimit for errors, packet count */
 	unsigned int err_limit_count;
 
-	/** do answer with server cookie when request contained cookie option */
+	/* do answer with server cookie when request contained cookie option */
 	int do_answer_cookie;
 
-	/** how many cookies are there in the cookies array */
+	/* how many cookies are there in the cookies array */
 	size_t cookie_count;
 
 	/* keep track of the last `NSD_COOKIE_HISTORY_SIZE`
 	 * cookies as per rfc requirement .*/
-	cookie_secret_type cookie_secrets[NSD_COOKIE_HISTORY_SIZE];
+	cookie_secrets_type cookie_secrets;
+
+	/* From where came the configured cookies */
+	cookie_secrets_source_type cookie_secrets_source;
+
+	/* The cookie secrets filename when they came from file; when
+	 * cookie_secrets_source == COOKIE_SECRETS_FROM_FILE */
+	char* cookie_secrets_filename;
 
 	struct nsd_options* options;
 
 #ifdef HAVE_SSL
 	/* TLS specific configuration */
 	SSL_CTX *tls_ctx;
+	SSL_CTX *tls_auth_ctx;
 #endif
 };
 
@@ -382,7 +401,7 @@ extern struct nsd nsd;
 /* nsd.c */
 pid_t readpid(const char *file);
 int writepid(struct nsd *nsd);
-void unlinkpid(const char* file);
+void unlinkpid(const char* file, const char* username);
 void sig_handler(int sig);
 void bind8_stats(struct nsd *nsd);
 
Index: nsec3.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/nsec3.c,v
diff -u -p -r1.27 nsec3.c
--- nsec3.c	20 Dec 2023 17:29:01 -0000	1.27
+++ nsec3.c	3 Sep 2025 13:53:32 -0000
@@ -477,7 +477,22 @@ nsec3_tree_dszone(namedb_type* db, domai
 	/* the DStree does not contain nodes with d==z->apex */
 	if(d->is_apex)
 		d = d->parent;
-	return nsec3_tree_zone(db, d);
+	while (d) {
+		if (d->is_apex) {
+			zone_type *zone = NULL;
+			for (rrset_type *rrset = d->rrsets; rrset; rrset = rrset->next)
+				if (rrset_rrtype(rrset) == TYPE_SOA ||
+				    rrset_rrtype(rrset) == TYPE_DNSKEY ||
+				    rrset_rrtype(rrset) == TYPE_NSEC3PARAM)
+					zone = rrset->zone;
+			if (!zone)
+				zone = namedb_find_zone(db, domain_dname(d));
+			if (zone && zone->dshashtree)
+				return zone;
+		}
+		d = d->parent;
+	}
+	return NULL;
 }
 
 int
@@ -560,9 +575,7 @@ nsec3_precompile_domain_ds(struct namedb
 	/* lookup in tree cover ptr (or exact) */
 	exact = nsec3_find_cover(zone, domain->nsec3->ds_parent_hash->hash,
 		sizeof(domain->nsec3->ds_parent_hash->hash), &result);
-	if(exact)
-		domain->nsec3->nsec3_ds_parent_is_exact = 1;
-	else 	domain->nsec3->nsec3_ds_parent_is_exact = 0;
+	domain->nsec3->nsec3_ds_parent_is_exact = exact != 0;
 	domain->nsec3->nsec3_ds_parent_cover = result;
 	/* add into tree */
 	zone_add_domain_in_hash_tree(db->region, &zone->dshashtree,
Index: options.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/options.c,v
diff -u -p -r1.30 options.c
--- options.c	12 Apr 2024 15:53:34 -0000	1.30
+++ options.c	3 Sep 2025 13:53:32 -0000
@@ -14,6 +14,13 @@
 #ifdef HAVE_IFADDRS_H
 #include <ifaddrs.h>
 #endif
+#ifdef HAVE_SSL
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
 #include "options.h"
 #include "query.h"
 #include "tsig.h"
@@ -73,6 +80,7 @@ nsd_options_create(region_type* region)
 	opt->logfile = 0;
 	opt->log_only_syslog = 0;
 	opt->log_time_ascii = 1;
+	opt->log_time_iso = 0;
 	opt->round_robin = 0; /* also packet.h::round_robin */
 	opt->minimal_responses = 1; /* also packet.h::minimal_responses */
 	opt->confine_to_zone = 0;
@@ -130,6 +138,7 @@ nsd_options_create(region_type* region)
 	opt->dnstap_log_auth_query_messages = 0;
 	opt->dnstap_log_auth_response_messages = 0;
 #endif
+	opt->reload_config = 0;
 	opt->zonefiles_check = 1;
 	opt->zonefiles_write = ZONEFILES_WRITE_INTERVAL;
 	opt->xfrd_reload_timeout = 1;
@@ -137,11 +146,15 @@ nsd_options_create(region_type* region)
 	opt->tls_service_ocsp = NULL;
 	opt->tls_service_pem = NULL;
 	opt->tls_port = TLS_PORT;
+	opt->tls_auth_port = NULL;
 	opt->tls_cert_bundle = NULL;
+	opt->tls_auth_xfr_only = 0;
 	opt->proxy_protocol_port = NULL;
 	opt->answer_cookie = 1;
 	opt->cookie_secret = NULL;
-	opt->cookie_secret_file = CONFIGDIR"/nsd_cookiesecrets.txt";
+	opt->cookie_staging_secret = NULL;
+	opt->cookie_secret_file = NULL;
+	opt->cookie_secret_file_is_default = 1;
 	opt->control_enable = 0;
 	opt->control_interface = NULL;
 	opt->control_port = NSD_CONTROL_PORT;
@@ -246,6 +259,16 @@ parse_options_file(struct nsd_options* o
 
 	opt->configfile = region_strdup(opt->region, file);
 
+	/* Set default cookie_secret_file value */
+	if(opt->cookie_secret_file_is_default && !opt->cookie_secret_file) {
+		opt->cookie_secret_file =
+			region_strdup(opt->region, COOKIESECRETSFILE);
+	}
+	/* Semantic errors */
+	if(opt->cookie_staging_secret && !opt->cookie_secret) {
+		c_error("a cookie-staging-secret cannot be configured without "
+		        "also providing a cookie-secret");
+	}
 	RBTREE_FOR(pat, struct pattern_options*, opt->patterns)
 	{
 		struct pattern_options* old_pat =
@@ -290,6 +313,13 @@ parse_options_file(struct nsd_options* o
 		}
 		for(acl=pat->provide_xfr; acl; acl=acl->next)
 		{
+			/* Find tls_auth */
+			if (acl->tls_auth_name) {
+				if (!(acl->tls_auth_options =
+			                tls_auth_options_find(opt, acl->tls_auth_name)))
+				    c_error("tls_auth %s in pattern %s could not be found",
+						acl->tls_auth_name, pat->pname);
+			}
 			if(acl->nokey || acl->blocked)
 				continue;
 			acl->key_options = key_options_find(opt, acl->key_name);
@@ -1940,9 +1970,16 @@ acl_check_incoming(struct acl_options* a
 
 	while(acl)
 	{
+#ifdef HAVE_SSL
+		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "testing acl %s %s %s",
+			acl->ip_address_spec, acl->nokey?"NOKEY":
+			(acl->blocked?"BLOCKED":acl->key_name),
+			(acl->tls_auth_name && q->tls_auth)?acl->tls_auth_name:""));
+#else
 		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "testing acl %s %s",
 			acl->ip_address_spec, acl->nokey?"NOKEY":
 			(acl->blocked?"BLOCKED":acl->key_name)));
+#endif
 		if(acl_addr_matches(acl, q) && acl_key_matches(acl, q)) {
 			if(!match)
 			{
@@ -1955,6 +1992,27 @@ acl_check_incoming(struct acl_options* a
 				return -1;
 			}
 		}
+#ifdef HAVE_SSL
+		/* we are in a acl with tls_auth */
+		if (acl->tls_auth_name && q->tls_auth) {
+			/* we have auth_domain_name in tls_auth */
+			if (acl->tls_auth_options && acl->tls_auth_options->auth_domain_name) {
+				if (!acl_tls_hostname_matches(q->tls_auth, acl->tls_auth_options->auth_domain_name)) {
+					VERBOSITY(3, (LOG_WARNING,
+							"client cert does not match %s %s",
+							acl->tls_auth_name, acl->tls_auth_options->auth_domain_name));
+					q->cert_cn = NULL;
+					return -1;
+				}
+				VERBOSITY(5, (LOG_INFO, "%s %s verified",
+					acl->tls_auth_name, acl->tls_auth_options->auth_domain_name));
+				q->cert_cn = acl->tls_auth_options->auth_domain_name;
+			} else {
+				/* nsd gives error on start for this, but check just in case */
+				log_msg(LOG_ERR, "auth-domain-name not defined in %s", acl->tls_auth_name);
+			}
+		}
+#endif
 		number++;
 		acl = acl->next;
 	}
@@ -2157,6 +2215,168 @@ acl_addr_match_range_v6(uint32_t* minval
 	return 1;
 }
 #endif /* INET6 */
+
+#ifdef HAVE_SSL
+/* Code in for matches_subject_alternative_name and matches_common_name
+ * functions is from https://wiki.openssl.org/index.php/Hostname_validation
+ * with modifications.
+ *
+ * Obtained from: https://github.com/iSECPartners/ssl-conservatory
+ * Copyright (C) 2012, iSEC Partners.
+ * License: MIT License
+ * Author:  Alban Diquet
+ */
+static int matches_subject_alternative_name(
+	const char *acl_cert_cn, size_t acl_cert_cn_len, const X509 *cert)
+{
+	int result = 0;
+	int san_names_nb = -1;
+	STACK_OF(GENERAL_NAME) *san_names = NULL;
+
+	/* Try to extract the names within the SAN extension from the certificate */
+	san_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+	if (san_names == NULL)
+		return 0;
+
+	san_names_nb = sk_GENERAL_NAME_num(san_names);
+
+	/* Check each name within the extension */
+	for (int i = 0; i < san_names_nb && !result; i++) {
+		int len;
+		const char *str;
+		const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i);
+		/* Skip non-DNS SAN entries. */
+		if (current_name->type != GEN_DNS)
+			continue;
+#if HAVE_ASN1_STRING_GET0_DATA
+		str = (const char *)ASN1_STRING_get0_data(current_name->d.dNSName);
+#else
+		str = (const char *)ASN1_STRING_data(current_name->d.dNSName);
+#endif
+		if (str == NULL)
+			continue;
+		len = ASN1_STRING_length(current_name->d.dNSName);
+		if (acl_cert_cn_len == (size_t)len &&
+		    strncasecmp(str, acl_cert_cn, len) == 0)
+		{
+			result = 1;
+		} else {
+			/* Make sure there isn't an embedded NUL character in the DNS name */
+			/* From the man page: In general it cannot be assumed that the data
+			 * returned by ASN1_STRING_data() is null terminated or does not
+			 * contain embedded nulls. */
+			int pos = 0;
+			while (pos < len && str[pos] != 0)
+				pos++;
+			if (pos == len) {
+				DEBUG(DEBUG_XFRD, 2, (LOG_INFO,
+					"SAN %*s does not match acl for %s", len, str, acl_cert_cn));
+			} else {
+				DEBUG(DEBUG_XFRD, 2, (LOG_INFO, "Malformed SAN in certificate"));
+				break;
+			}
+		}
+	}
+	sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
+
+	return result;
+}
+
+static int matches_common_name(
+	const char *acl_cert_cn, size_t acl_cert_cn_len, const X509 *cert)
+{
+	int len;
+	int common_name_loc = -1;
+	const char *common_name_str = NULL;
+	X509_NAME *subject_name = NULL;
+	X509_NAME_ENTRY *common_name_entry = NULL;
+	ASN1_STRING *common_name_asn1 = NULL;
+
+	if ((subject_name = X509_get_subject_name(cert)) == NULL)
+		return 0;
+
+	/* Find the position of the CN field in the Subject field of the certificate */
+	common_name_loc = X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1);
+	if (common_name_loc < 0)
+		return 0;
+
+	/* Extract the CN field */
+	common_name_entry = X509_NAME_get_entry(subject_name, common_name_loc);
+	if (common_name_entry == NULL)
+		return 0;
+
+	/* Convert the CN field to a C string */
+	common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
+	if (common_name_asn1 == NULL)
+		return 0;
+
+#if HAVE_ASN1_STRING_GET0_DATA
+	common_name_str = (const char *)ASN1_STRING_get0_data(common_name_asn1);
+#else
+	common_name_str = (const char *)ASN1_STRING_data(common_name_asn1);
+#endif
+
+	len = ASN1_STRING_length(common_name_asn1);
+	if (acl_cert_cn_len == (size_t)len &&
+	    strncasecmp(acl_cert_cn, common_name_str, len) == 0)
+	{
+		return 1;
+	} else {
+		/* Make sure there isn't an embedded NUL character in the CN */
+		int pos = 0;
+		while (pos < len && common_name_str[pos] != 0)
+			pos++;
+		if (pos == len) {
+			DEBUG(DEBUG_XFRD, 2, (LOG_INFO,
+				"CN %*s does not match acl for %s", len, common_name_str, acl_cert_cn));
+		} else {
+			DEBUG(DEBUG_XFRD, 2, (LOG_INFO, "Malformed CN in certificate"));
+		}
+	}
+
+	return 0;
+}
+
+int
+acl_tls_hostname_matches(SSL* tls_auth, const char *acl_cert_cn)
+{
+	int result = 0;
+	size_t acl_cert_cn_len;
+	X509 *client_cert;
+
+	assert(acl_cert_cn != NULL);
+
+#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
+	client_cert = SSL_get1_peer_certificate(tls_auth);
+#else
+	client_cert = SSL_get_peer_certificate(tls_auth);
+#endif
+
+	if (client_cert == NULL)
+		return 0;
+
+	/* OpenSSL provides functions for hostname checking from certificate
+	 * Following code should work but it doesn't.
+	 * Keep it for future test in order to not use custom code
+	 *
+	 * X509_VERIFY_PARAM *vpm = SSL_get0_param(tls_auth);
+	 * Hostname check is done here:
+	 * X509_VERIFY_PARAM_set1_host(vpm, acl_cert_cn, 0); // recommended
+	 * X509_check_host() // can also be used instead. Not recommended DANE-EE
+	 * SSL_get_verify_result(tls_auth) != X509_V_OK) // NOT ok
+	 * const char *peername = X509_VERIFY_PARAM_get0_peername(vpm); // NOT ok
+	 */
+
+	acl_cert_cn_len = strlen(acl_cert_cn);
+	/* semi follow RFC6125#section-6.4.4 check SAN DNS first */
+	if (!(result = matches_subject_alternative_name(acl_cert_cn, acl_cert_cn_len, client_cert)))
+		result = matches_common_name(acl_cert_cn, acl_cert_cn_len, client_cert);
+
+	X509_free(client_cert);
+
+	return result;
+}
+#endif
 
 int
 acl_key_matches(struct acl_options* acl, struct query* q)
Index: options.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/options.h,v
diff -u -p -r1.29 options.h
--- options.h	12 Apr 2024 15:53:34 -0000	1.29
+++ options.h	3 Sep 2025 13:53:32 -0000
@@ -11,6 +11,9 @@
 #define OPTIONS_H
 
 #include <stdarg.h>
+#ifdef HAVE_SSL
+#include <openssl/ssl.h>
+#endif
 #include "region-allocator.h"
 #include "rbtree.h"
 struct query;
@@ -113,9 +116,11 @@ struct nsd_options {
 	const char* zonelistfile;
 	const char* nsid;
 	int xfrd_reload_timeout;
+	int reload_config;
 	int zonefiles_check;
 	int zonefiles_write;
 	int log_time_ascii;
+	int log_time_iso;
 	int round_robin;
 	int minimal_responses;
 	int refuse_any;
@@ -133,8 +138,12 @@ struct nsd_options {
 	char* tls_service_pem;
 	/* TLS dedicated port */
 	const char* tls_port;
+	/* TLS-AUTH dedicated port */
+	const char* tls_auth_port;
 	/* TLS certificate bundle */
 	const char* tls_cert_bundle;
+	/* Answer XFR only from tls_auth_port and after authentication */
+	int tls_auth_xfr_only;
 
 	/* proxy protocol port list */
 	struct proxy_protocol_port_list* proxy_protocol_port;
@@ -200,8 +209,12 @@ struct nsd_options {
 	int answer_cookie;
 	/** cookie secret */
 	char *cookie_secret;
+	/** cookie staging secret */
+	char *cookie_staging_secret;
 	/** path to cookie secret store */
-	char const* cookie_secret_file;
+	char *cookie_secret_file;
+	/** set when the cookie_secret_file whas not explicitely configured */
+	uint8_t cookie_secret_file_is_default;
 	/** enable verify */
 	int verify_enable;
 	/** list of ip addresses used to serve zones for verification */
@@ -557,6 +570,9 @@ int acl_check_incoming(struct acl_option
 int acl_addr_matches_host(struct acl_options* acl, struct acl_options* host);
 int acl_addr_matches(struct acl_options* acl, struct query* q);
 int acl_addr_matches_proxy(struct acl_options* acl, struct query* q);
+#ifdef HAVE_SSL
+int acl_tls_hostname_matches(SSL* ssl, const char* acl_cert_cn);
+#endif
 int acl_key_matches(struct acl_options* acl, struct query* q);
 int acl_addr_match_mask(uint32_t* a, uint32_t* b, uint32_t* mask, size_t sz);
 int acl_addr_match_range_v6(uint32_t* minval, uint32_t* x, uint32_t* maxval, size_t sz);
Index: query.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/query.c,v
diff -u -p -r1.42 query.c
--- query.c	12 Apr 2024 15:53:34 -0000	1.42
+++ query.c	3 Sep 2025 13:53:32 -0000
@@ -451,10 +451,11 @@ answer_notify(struct nsd* nsd, struct qu
 			char address[128], proxy[128];
 			addr2str(&query->client_addr, address, sizeof(address));
 			addr2str(&query->remote_addr, proxy, sizeof(proxy));
-			VERBOSITY(2, (LOG_INFO, "notify for %s from %s via proxy %s refused because of proxy, %s %s",
+			VERBOSITY(2, (LOG_INFO, "notify for %s from %s via proxy %s refused because of proxy, %s%s%s",
 				dname_to_string(query->qname, NULL),
 				address, proxy,
-				(why?why->ip_address_spec:"."),
+				(why?why->ip_address_spec:""),
+				(why&&why->ip_address_spec[0]?" ":""),
 				(why ? ( why->nokey    ? "NOKEY"
 				       : why->blocked  ? "BLOCKED"
 				       : why->key_name )
@@ -465,8 +466,7 @@ answer_notify(struct nsd* nsd, struct qu
 	if((acl_num = acl_check_incoming(zone_opt->pattern->allow_notify, query,
 		&why)) != -1)
 	{
-		sig_atomic_t mode = NSD_PASS_TO_XFRD;
-		int s = nsd->this_child->parent_fd;
+		int s = nsd->serve2xfrd_fd_send[nsd->this_child->child_num];
 		uint16_t sz;
 		uint32_t acl_send = htonl(acl_num);
 		uint32_t acl_xfr;
@@ -484,14 +484,14 @@ answer_notify(struct nsd* nsd, struct qu
 			why->ip_address_spec,
 			why->nokey?"NOKEY":
 			(why->blocked?"BLOCKED":why->key_name)));
-		sz = buffer_limit(query->packet);
 		if(buffer_limit(query->packet) > MAX_PACKET_SIZE)
 			return query_error(query, NSD_RC_SERVFAIL);
 		/* forward to xfrd for processing
 		   Note. Blocking IPC I/O, but acl is OK. */
+		sz = buffer_limit(query->packet)
+		   + sizeof(acl_send) + sizeof(acl_xfr);
 		sz = htons(sz);
-		if(!write_socket(s, &mode, sizeof(mode)) ||
-			!write_socket(s, &sz, sizeof(sz)) ||
+		if(!write_socket(s, &sz, sizeof(sz)) ||
 			!write_socket(s, buffer_begin(query->packet),
 				buffer_limit(query->packet)) ||
 			!write_socket(s, &acl_send, sizeof(acl_send)) ||
@@ -531,10 +531,11 @@ answer_notify(struct nsd* nsd, struct qu
 	if (verbosity >= 2) {
 		char address[128];
 		addr2str(&query->client_addr, address, sizeof(address));
-		VERBOSITY(2, (LOG_INFO, "notify for %s from %s refused, %s %s",
+		VERBOSITY(2, (LOG_INFO, "notify for %s from %s refused, %s%s%s",
 			dname_to_string(query->qname, NULL),
 			address,
-			(why?why->ip_address_spec:"."),
+			(why?why->ip_address_spec:""),
+			(why&&why->ip_address_spec[0]?" ":""),
 			(why ? ( why->nokey    ? "NOKEY"
 			       : why->blocked  ? "BLOCKED"
 			       : why->key_name )
@@ -1339,10 +1340,11 @@ answer_lookup_zone(struct nsd *nsd, stru
 				char address[128], proxy[128];
 				addr2str(&q->client_addr, address, sizeof(address));
 				addr2str(&q->remote_addr, proxy, sizeof(proxy));
-				VERBOSITY(2, (LOG_INFO, "query %s from %s via proxy %s refused because of proxy, %s %s",
+				VERBOSITY(2, (LOG_INFO, "query %s from %s via proxy %s refused because of proxy, %s%s%s",
 					dname_to_string(q->qname, NULL),
 					address, proxy,
-					(why?why->ip_address_spec:"."),
+					(why?why->ip_address_spec:""),
+					(why&&why->ip_address_spec[0]?" ":""),
 					(why ? ( why->nokey    ? "NOKEY"
 					      : why->blocked  ? "BLOCKED"
 					      : why->key_name )
@@ -1378,17 +1380,18 @@ answer_lookup_zone(struct nsd *nsd, stru
 				why->nokey?"NOKEY":
 				(why->blocked?"BLOCKED":why->key_name)));
 		} else {
-			if (verbosity >= 2) {
+			if (q->cname_count == 0 && verbosity >= 2) {
 				char address[128];
 				addr2str(&q->client_addr, address, sizeof(address));
-				VERBOSITY(2, (LOG_INFO, "query %s from %s refused, %s %s",
+				VERBOSITY(2, (LOG_INFO, "query %s from %s refused, %s%s%s",
 					dname_to_string(q->qname, NULL),
 					address,
 					why ? ( why->nokey    ? "NOKEY"
 					      : why->blocked  ? "BLOCKED"
 					      : why->key_name )
 					    : "no acl matches",
-					why?why->ip_address_spec:"."));
+					(why&&why->ip_address_spec[0]?" ":""),
+					why?why->ip_address_spec:""));
 			}
 			/* no zone for this */
 			if(q->cname_count == 0) {
@@ -1777,6 +1780,17 @@ query_add_optional(query_type *q, nsd_ty
 				6 + ( q->edns.ede_text_len
 			            ? q->edns.ede_text_len : 0);
 
+		if(q->edns.zoneversion
+		&& q->zone
+		&& q->zone->soa_rrset
+		&& q->zone->soa_rrset->rrs
+		&& q->zone->soa_rrset->rrs->rdata_count >= 3)
+			q->edns.opt_reserved_space += sizeof(uint16_t)
+			                           +  sizeof(uint16_t)
+			                           +  sizeof(uint8_t)
+			                           +  sizeof(uint8_t)
+			                           +  sizeof(uint32_t);
+
 		if(q->edns.opt_reserved_space == 0 || !buffer_available(
 			q->packet, 2+q->edns.opt_reserved_space)) {
 			/* fill with NULLs */
@@ -1790,6 +1804,24 @@ query_add_optional(query_type *q, nsd_ty
 				buffer_write(q->packet, edns->nsid, OPT_HDR);
 				/* nsid payload */
 				buffer_write(q->packet, nsd->nsid, nsd->nsid_len);
+			}
+			if(q->edns.zoneversion
+			&& q->zone
+			&& q->zone->soa_rrset
+			&& q->zone->soa_rrset->rrs
+			&& q->zone->soa_rrset->rrs->rdata_count >= 3) {
+				buffer_write_u16(q->packet, ZONEVERSION_CODE);
+				buffer_write_u16( q->packet
+				                , sizeof(uint8_t)
+						+ sizeof(uint8_t)
+						+ sizeof(uint32_t));
+				buffer_write_u8(q->packet,
+				    domain_dname(q->zone->apex)->label_count - 1);
+				buffer_write_u8( q->packet
+				               , ZONEVERSION_SOA_SERIAL);
+				buffer_write_u32(q->packet,
+				    read_uint32(rdata_atom_data(
+				    q->zone->soa_rrset->rrs->rdatas[2])));
 			}
 			if(q->edns.cookie_status != COOKIE_NOT_PRESENT) {
 				/* cookie opt header */
Index: query.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/query.h,v
diff -u -p -r1.8 query.h
--- query.h	20 Dec 2023 17:29:01 -0000	1.8
+++ query.h	3 Sep 2025 13:53:32 -0000
@@ -79,6 +79,15 @@ struct query {
 
 	buffer_type *packet;
 
+#ifdef HAVE_SSL
+	/*
+	 * TLS objects.
+	*/
+	SSL* tls;
+	SSL* tls_auth;
+	char* cert_cn;
+#endif
+
 	/* Normalized query domain name.  */
 	const dname_type *qname;
 
Index: rdata.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/rdata.c,v
diff -u -p -r1.17 rdata.c
--- rdata.c	14 Nov 2022 21:09:32 -0000	1.17
+++ rdata.c	3 Sep 2025 13:53:32 -0000
@@ -17,6 +17,7 @@
 #include <netdb.h>
 #include <stdlib.h>
 #include <string.h>
+#include <inttypes.h>
 #ifdef HAVE_STRINGS_H
 #include <strings.h>
 #endif
@@ -68,7 +69,8 @@ lookup_table_type dns_algorithms[] = {
 
 const char *svcparamkey_strs[] = {
 		"mandatory", "alpn", "no-default-alpn", "port",
-		"ipv4hint", "ech", "ipv6hint", "dohpath"
+		"ipv4hint", "ech", "ipv6hint", "dohpath", "ohttp",
+		"tls-supported-groups"
 	};
 
 typedef int (*rdata_to_string_type)(buffer_type *output,
@@ -200,6 +202,57 @@ rdata_long_text_to_string(buffer_type *o
 }
 
 static int
+rdata_unquoted_to_string(buffer_type *output, rdata_atom_type rdata,
+	rr_type* ATTR_UNUSED(rr))
+{
+	const uint8_t *data = rdata_atom_data(rdata);
+	uint8_t length = data[0];
+	size_t i;
+
+	for (i = 1; i <= length; ++i) {
+		char ch = (char) data[i];
+		if (isprint((unsigned char)ch)) {
+			if (ch == '"' || ch == '\\'
+			||  isspace((unsigned char)ch)) {
+				buffer_printf(output, "\\");
+			}
+			buffer_printf(output, "%c", ch);
+		} else {
+			buffer_printf(output, "\\%03u", (unsigned) data[i]);
+		}
+	}
+	return 1;
+}
+
+static int
+rdata_unquoteds_to_string(buffer_type *output, rdata_atom_type rdata,
+	rr_type* ATTR_UNUSED(rr))
+{
+	uint16_t pos = 0;
+	const uint8_t *data = rdata_atom_data(rdata);
+	uint16_t length = rdata_atom_size(rdata);
+	size_t i;
+
+	while (pos < length && pos + data[pos] < length) {
+		for (i = 1; i <= data[pos]; ++i) {
+			char ch = (char) data[pos + i];
+			if (isprint((unsigned char)ch)) {
+				if (ch == '"' || ch == '\\'
+				||  isspace((unsigned char)ch)) {
+					buffer_printf(output, "\\");
+				}
+				buffer_printf(output, "%c", ch);
+			} else {
+				buffer_printf(output, "\\%03u", (unsigned) data[pos+i]);
+			}
+		}
+		pos += data[pos]+1;
+		buffer_printf(output, pos < length?" ":"");
+	}
+	return 1;
+}
+
+static int
 rdata_tag_to_string(buffer_type *output, rdata_atom_type rdata,
 	rr_type* ATTR_UNUSED(rr))
 {
@@ -507,6 +560,22 @@ rdata_apl_to_string(buffer_type *output,
 	return result;
 }
 
+/*
+ * Print protocol and service numbers rather than names for Well-Know Services
+ * (WKS) RRs. WKS RRs are deprecated, though not technically, and should not
+ * be used. The parser supports tcp/udp for protocols and a small subset of
+ * services because getprotobyname and/or getservbyname are marked MT-Unsafe
+ * and locale. getprotobyname_r and getservbyname_r exist on some platforms,
+ * but are still marked locale (meaning the locale object is used without
+ * synchonization, which is a problem for a library). Failure to load a zone
+ * on a primary server because of an unknown protocol or service name is
+ * acceptable as the operator can opt to use the numeric value. Failure to
+ * load a zone on a secondary server is problematic because "unsupported"
+ * protocols and services might be written. Print the numeric value for
+ * maximum compatibility.
+ *
+ * (see simdzone/generic/wks.h for details).
+ */
 static int
 rdata_services_to_string(buffer_type *output, rdata_atom_type rdata,
 	rr_type* ATTR_UNUSED(rr))
@@ -521,26 +590,16 @@ rdata_services_to_string(buffer_type *ou
 		uint8_t protocol_number = buffer_read_u8(&packet);
 		ssize_t bitmap_size = buffer_remaining(&packet);
 		uint8_t *bitmap = buffer_current(&packet);
-		struct protoent *proto = getprotobynumber(protocol_number);
-
-		if (proto) {
-			int i;
 
-			buffer_printf(output, "%s", proto->p_name);
+		buffer_printf(output, "%" PRIu8, protocol_number);
 
-			for (i = 0; i < bitmap_size * 8; ++i) {
-				if (get_bit(bitmap, i)) {
-					struct servent *service = getservbyport((int)htons(i), proto->p_name);
-					if (service) {
-						buffer_printf(output, " %s", service->s_name);
-					} else {
-						buffer_printf(output, " %d", i);
-					}
-				}
+		for (int i = 0; i < bitmap_size * 8; ++i) {
+			if (get_bit(bitmap, i)) {
+				buffer_printf(output, " %d", i);
 			}
-			buffer_skip(&packet, bitmap_size);
-			result = 1;
 		}
+		buffer_skip(&packet, bitmap_size);
+		result = 1;
 	}
 	return result;
 }
@@ -799,6 +858,21 @@ rdata_svcparam_alpn_to_string(buffer_typ
 }
 
 static int
+rdata_svcparam_tls_supported_groups_to_string(buffer_type *output,
+		uint16_t val_len, uint16_t *data)
+{
+	assert(val_len > 0); /* Guaranteed by rdata_svcparam_to_string */
+
+	if ((val_len % sizeof(uint16_t)) == 1)
+		return 0; /* A series of uint16_t is an even number of bytes */
+
+	buffer_printf(output, "=%d", (int)ntohs(*data++));
+	while ((val_len -= sizeof(uint16_t)) > 0) 
+		buffer_printf(output, ",%d", (int)ntohs(*data++));
+	return 1;
+}
+
+static int
 rdata_svcparam_to_string(buffer_type *output, rdata_atom_type rdata,
 	rr_type* ATTR_UNUSED(rr))
 {
@@ -825,6 +899,7 @@ rdata_svcparam_to_string(buffer_type *ou
 		case SVCB_KEY_IPV6HINT:
 		case SVCB_KEY_MANDATORY:
 		case SVCB_KEY_DOHPATH:
+		case SVCB_KEY_TLS_SUPPORTED_GROUPS:
 			return 0;
 		default:
 			return 1;
@@ -845,6 +920,10 @@ rdata_svcparam_to_string(buffer_type *ou
 		return rdata_svcparam_alpn_to_string(output, val_len, data+2);
 	case SVCB_KEY_ECH:
 		return rdata_svcparam_ech_to_string(output, val_len, data+2);
+	case SVCB_KEY_OHTTP:
+		return 0; /* wireformat error, should not have a value */
+	case SVCB_KEY_TLS_SUPPORTED_GROUPS:
+		return rdata_svcparam_tls_supported_groups_to_string(output, val_len, data+2);
 	case SVCB_KEY_DOHPATH:
 		/* fallthrough */
 	default:
@@ -868,6 +947,34 @@ rdata_svcparam_to_string(buffer_type *ou
 }
 
 static int
+rdata_hip_to_string(buffer_type *output, rdata_atom_type rdata,
+	rr_type* ATTR_UNUSED(rr))
+{
+ 	uint16_t size = rdata_atom_size(rdata);
+	uint8_t hit_length;
+	uint16_t pk_length;
+	int length = 0;
+
+	if(size < 4)
+		return 0;
+	hit_length = rdata_atom_data(rdata)[0];
+	pk_length  = read_uint16(rdata_atom_data(rdata) + 2);
+	length     = 4 + hit_length + pk_length;
+	if(hit_length == 0 || pk_length == 0 || size < length)
+		return 0;
+	buffer_printf(output, "%u ", (unsigned)rdata_atom_data(rdata)[1]);
+	hex_to_string(output, rdata_atom_data(rdata) + 4, hit_length);
+	buffer_printf(output, " ");
+	buffer_reserve(output, pk_length * 2 + 1);
+	length = __b64_ntop(rdata_atom_data(rdata) + 4 + hit_length, pk_length,
+			  (char *) buffer_current(output), pk_length * 2);
+	if (length > 0) {
+		buffer_skip(output, length);
+	}
+	return length != -1;
+}
+
+static int
 rdata_unknown_to_string(buffer_type *output, rdata_atom_type rdata,
 	rr_type* ATTR_UNUSED(rr))
 {
@@ -907,8 +1014,11 @@ static rdata_to_string_type rdata_to_str
 	rdata_eui48_to_string,
 	rdata_eui64_to_string,
 	rdata_long_text_to_string,
+	rdata_unquoted_to_string,
+	rdata_unquoteds_to_string,
 	rdata_tag_to_string,
 	rdata_svcparam_to_string,
+	rdata_hip_to_string,
 	rdata_unknown_to_string
 };
 
@@ -1035,8 +1145,16 @@ rdata_wireformat_to_rdata_atoms(region_t
 				    read_uint16(buffer_current(packet) + 2);
 			}
 			break;
+		case RDATA_WF_HIP:
+			/* Length is stored in the first byte (HIT length)
+			 * plus the third and fourth byte (PK length) */
+			length = 4;
+			if (buffer_position(packet) + length <= end) {
+				length += buffer_current(packet)[0];
+				length += read_uint16(buffer_current(packet) + 2);
+			}
+			break;
 		}
-
 		if (is_domain) {
 			const dname_type *dname;
 
Index: region-allocator.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/region-allocator.c,v
diff -u -p -r1.13 region-allocator.c
--- region-allocator.c	14 May 2020 06:08:40 -0000	1.13
+++ region-allocator.c	3 Sep 2025 13:53:32 -0000
@@ -429,6 +429,25 @@ region_strdup(region_type *region, const
 }
 
 void
+region_str_replace(region_type *region, char **to_replace, const char *string)
+{
+	assert(to_replace);
+	if(!*to_replace) {
+		if(!string)
+			return;
+		*to_replace = region_strdup(region, string);
+	}
+	else if(!string) {
+		region_recycle(region, *to_replace, strlen(*to_replace) + 1);
+		*to_replace = NULL;
+	}
+	else if(strcmp(*to_replace, string)) {
+		region_recycle(region, *to_replace, strlen(*to_replace) + 1);
+		*to_replace = region_strdup(region, string);
+	}
+}
+
+void
 region_recycle(region_type *region, void *block, size_t size)
 {
 	size_t aligned_size;
Index: region-allocator.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/region-allocator.h,v
diff -u -p -r1.3 region-allocator.h
--- region-allocator.h	29 Jun 2023 19:38:50 -0000	1.3
+++ region-allocator.h	3 Sep 2025 13:53:32 -0000
@@ -130,6 +130,11 @@ void region_free_all(region_type *region
 char *region_strdup(region_type *region, const char *string);
 
 /*
+ * Replace a string on the to_replace location, if string is different
+ */
+void region_str_replace(region_type* region, char **to_replace,
+		const char *string);
+/*
  * Recycle an allocated memory block. Pass size used to alloc it.
  * Does nothing if recycling is not enabled for the region.
  */
Index: remote.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/remote.c,v
diff -u -p -r1.27 remote.c
--- remote.c	12 Apr 2024 15:53:34 -0000	1.27
+++ remote.c	3 Sep 2025 13:53:32 -0000
@@ -71,7 +71,6 @@
 #else
 #  include "mini_event.h"
 #endif
-#include "remote.h"
 #include "util.h"
 #include "xfrd.h"
 #include "xfrd-catalog-zones.h"
@@ -81,6 +80,7 @@
 #include "options.h"
 #include "difffile.h"
 #include "ipc.h"
+#include "remote.h"
 
 #ifdef HAVE_SYS_TYPES_H
 #  include <sys/types.h>
@@ -1969,21 +1969,66 @@ repat_options_changed(xfrd_state_type* x
 	return 0;
 }
 
+static int opt_str_changed(const char* old, const char* new)
+{ return !old ? ( !new ? 0 : 1 ) : ( !new ? 1 : strcasecmp(old, new) ); }
+
+/** true if cookie options are different that can be set via repat. */
+static int
+repat_cookie_options_changed(struct nsd_options* old, struct nsd_options* new)
+{
+	return old->answer_cookie != new->answer_cookie
+	    || opt_str_changed( old->cookie_secret
+	                      , new->cookie_secret)
+	    || opt_str_changed( old->cookie_staging_secret
+	                      , new->cookie_staging_secret)
+	    || old->cookie_secret_file_is_default !=
+	       new->cookie_secret_file_is_default
+	    || opt_str_changed( old->cookie_secret_file
+	                      , new->cookie_secret_file);
+}
+
 /** check if global options have changed */
 static void
 repat_options(xfrd_state_type* xfrd, struct nsd_options* newopt)
 {
+	struct nsd_options* oldopt = xfrd->nsd->options;
+
 	if(repat_options_changed(xfrd, newopt)) {
 		/* update our options */
 #ifdef RATELIMIT
-		xfrd->nsd->options->rrl_ratelimit = newopt->rrl_ratelimit;
-		xfrd->nsd->options->rrl_whitelist_ratelimit = newopt->rrl_whitelist_ratelimit;
-		xfrd->nsd->options->rrl_slip = newopt->rrl_slip;
+		oldopt->rrl_ratelimit = newopt->rrl_ratelimit;
+		oldopt->rrl_whitelist_ratelimit = newopt->rrl_whitelist_ratelimit;
+		oldopt->rrl_slip = newopt->rrl_slip;
 #endif
 		task_new_opt_change(xfrd->nsd->task[xfrd->nsd->mytask],
 			xfrd->last_task, newopt);
 		xfrd_set_reload_now(xfrd);
 	}
+	if(repat_cookie_options_changed(oldopt, newopt)) {
+		/* update our options */
+		oldopt->answer_cookie = newopt->answer_cookie;
+		region_str_replace(  oldopt->region
+		                  , &oldopt->cookie_secret
+		                  ,  newopt->cookie_secret);
+		region_str_replace(  oldopt->region
+		                  , &oldopt->cookie_staging_secret
+		                  ,  newopt->cookie_staging_secret);
+		oldopt->cookie_secret_file_is_default =
+			newopt->cookie_secret_file_is_default;
+		region_str_replace(  oldopt->region
+		                  , &oldopt->cookie_secret_file
+		                  ,  newopt->cookie_secret_file);
+
+		xfrd->nsd->cookie_count = 0;
+		xfrd->nsd->cookie_secrets_source = COOKIE_SECRETS_NONE;
+		reconfig_cookies(xfrd->nsd, newopt);
+		task_new_cookies( xfrd->nsd->task[xfrd->nsd->mytask]
+		                , xfrd->last_task
+		                , xfrd->nsd->do_answer_cookie
+		                , xfrd->nsd->cookie_count
+		                , xfrd->nsd->cookie_secrets);
+		xfrd_set_reload_now(xfrd);
+	}
 }
 
 /** print errors over ssl, gets pointer-to-pointer to ssl, so it can set
@@ -2038,6 +2083,48 @@ do_repattern(RES* ssl, xfrd_state_type* 
 	region_destroy(region);
 }
 
+static void print_cfg_err(void *unused, const char *message)
+{
+	(void)unused;
+	log_msg(LOG_ERR, "%s", message);
+}
+
+/* mostly identical to do_repattern */
+void xfrd_reload_config(xfrd_state_type *xfrd)
+{
+	const char *chrootdir = xfrd->nsd->chrootdir;
+	const char *file = xfrd->nsd->options->configfile;
+	region_type* region;
+	struct nsd_options* options;
+
+	if (chrootdir && !file_inside_chroot(file, chrootdir))
+	{
+		log_msg(LOG_ERR, "%s is not relative to %s: %s",
+			xfrd->nsd->options->configfile, xfrd->nsd->chrootdir,
+			"chroot prevents reread of config");
+		goto error_chroot;
+	}
+
+	region = region_create(xalloc, free);
+	options = nsd_options_create(region);
+
+	if (!parse_options_file(
+		options, file, print_cfg_err, NULL, xfrd->nsd->options))
+	{
+		goto error_parse;
+	}
+
+	repat_keys(xfrd, options);
+	repat_patterns(xfrd, options); /* adds/deletes zones too */
+	repat_options(xfrd, options);
+	zonestat_inc_ifneeded(xfrd);
+
+error_parse:
+	region_destroy(region);
+error_chroot:
+	return;
+}
+
 /** do the serverpid command: printout pid of server process */
 static void
 do_serverpid(RES* ssl, xfrd_state_type* xfrd)
@@ -2298,19 +2385,42 @@ do_del_tsig(RES* ssl, xfrd_state_type* x
 	send_ok(ssl);
 }
 
+
+static int
+can_dump_cookie_secrets(RES* ssl, nsd_type* const nsd)
+{
+	if(!nsd->options->cookie_secret_file)
+		(void)ssl_printf(ssl, "error: empty cookie-secret-file\n");
+
+	else if(nsd->cookie_secrets_source == COOKIE_SECRETS_FROM_CONFIG)
+		(void)ssl_printf(ssl, "error: cookie secrets are already "
+			"configured. Remove \"cookie-secret:\" and "
+			"\"cookie-staging-secret:\" entries from configuration "
+			"first (and reconfig) before managing cookies with "
+			"nsd-control\n");
+	else
+		return 1;
+	return 0;
+	
+}
+
 /* returns `0` on failure */
 static int
-cookie_secret_file_dump(RES* ssl, nsd_type const* nsd) {
-	char const* secret_file = nsd->options->cookie_secret_file;
+cookie_secret_file_dump_and_reload(RES* ssl, nsd_type* const nsd) {
 	char secret_hex[NSD_COOKIE_SECRET_SIZE * 2 + 1];
 	FILE* f;
 	size_t i;
-	assert( secret_file != NULL );
 
 	/* open write only and truncate */
-	if((f = fopen(secret_file, "w")) == NULL ) {
-		(void)ssl_printf(ssl, "unable to open cookie secret file %s: %s",
-		                 secret_file, strerror(errno));
+	if(!nsd->options->cookie_secret_file) {
+		(void)ssl_printf(ssl, "cookie-secret-file empty\n");
+		return 0;
+	}
+	else if((f = fopen(nsd->options->cookie_secret_file, "w")) == NULL ) {
+		(void)ssl_printf( ssl
+		                , "unable to open cookie secret file %s: %s\n"
+		                , nsd->options->cookie_secret_file
+		                , strerror(errno));
 		return 0;
 	}
 	for(i = 0; i < nsd->cookie_count; i++) {
@@ -2324,65 +2434,77 @@ cookie_secret_file_dump(RES* ssl, nsd_ty
 	}
 	explicit_bzero(secret_hex, sizeof(secret_hex));
 	fclose(f);
+	nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE;
+	region_str_replace(nsd->region, &nsd->cookie_secrets_filename
+	                              , nsd->options->cookie_secret_file);
+	task_new_cookies(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task,
+		nsd->do_answer_cookie, nsd->cookie_count, nsd->cookie_secrets);
+	xfrd_set_reload_now(xfrd);
+	send_ok(ssl);
 	return 1;
 }
 
 static void
 do_activate_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) {
 	nsd_type* nsd = xrfd->nsd;
+	size_t backup_cookie_count;
+	cookie_secrets_type backup_cookie_secrets;
 	(void)arg;
 
+	if(!can_dump_cookie_secrets(ssl, xfrd->nsd))
+		return;
+
 	if(nsd->cookie_count <= 1 ) {
 		(void)ssl_printf(ssl, "error: no staging cookie secret to activate\n");
 		return;
 	}
-	if(!nsd->options->cookie_secret_file || !nsd->options->cookie_secret_file[0]) {
-		(void)ssl_printf(ssl, "error: no cookie secret file configured\n");
-		return;
-	}
-	if(!cookie_secret_file_dump(ssl, nsd)) {
-		(void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n",
-				nsd->options->cookie_secret_file);
-		return;
-	}
+	backup_cookie_count = nsd->cookie_count;
+	memcpy( backup_cookie_secrets, nsd->cookie_secrets
+	      , sizeof(cookie_secrets_type));
 	activate_cookie_secret(nsd);
-	(void)cookie_secret_file_dump(ssl, nsd);
-	task_new_activate_cookie_secret(xfrd->nsd->task[xfrd->nsd->mytask],
-	    xfrd->last_task);
-	xfrd_set_reload_now(xfrd);
-	send_ok(ssl);
+	if(!cookie_secret_file_dump_and_reload(ssl, nsd)) {
+		memcpy( nsd->cookie_secrets, backup_cookie_secrets
+		      , sizeof(cookie_secrets_type));
+		nsd->cookie_count = backup_cookie_count;
+	}
+	explicit_bzero(backup_cookie_secrets, sizeof(cookie_secrets_type));
 }
 
 static void
 do_drop_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) {
 	nsd_type* nsd = xrfd->nsd;
+	size_t backup_cookie_count;
+	cookie_secrets_type backup_cookie_secrets;
 	(void)arg;
 
+	if(!can_dump_cookie_secrets(ssl, xfrd->nsd))
+		return;
+
 	if(nsd->cookie_count <= 1 ) {
 		(void)ssl_printf(ssl, "error: can not drop the currently active cookie secret\n");
 		return;
 	}
-	if(!nsd->options->cookie_secret_file || !nsd->options->cookie_secret_file[0]) {
-		(void)ssl_printf(ssl, "error: no cookie secret file configured\n");
-		return;
-	}
-	if(!cookie_secret_file_dump(ssl, nsd)) {
-		(void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n",
-				nsd->options->cookie_secret_file);
-		return;
-	}
+	backup_cookie_count = nsd->cookie_count;
+	memcpy( backup_cookie_secrets, nsd->cookie_secrets
+	      , sizeof(cookie_secrets_type));
 	drop_cookie_secret(nsd);
-	(void)cookie_secret_file_dump(ssl, nsd);
-	task_new_drop_cookie_secret(xfrd->nsd->task[xfrd->nsd->mytask],
-	    xfrd->last_task);
-	xfrd_set_reload_now(xfrd);
-	send_ok(ssl);
+	if(!cookie_secret_file_dump_and_reload(ssl, nsd)) {
+		memcpy( nsd->cookie_secrets, backup_cookie_secrets
+		      , sizeof(cookie_secrets_type));
+		nsd->cookie_count = backup_cookie_count;
+	}
+	explicit_bzero(backup_cookie_secrets, sizeof(cookie_secrets_type));
 }
 
 static void
 do_add_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) {
 	nsd_type* nsd = xrfd->nsd;
 	uint8_t secret[NSD_COOKIE_SECRET_SIZE];
+	size_t backup_cookie_count;
+	cookie_secrets_type backup_cookie_secrets;
+
+	if(!can_dump_cookie_secrets(ssl, xfrd->nsd))
+		return;
 
 	if(*arg == '\0') {
 		(void)ssl_printf(ssl, "error: missing argument (cookie_secret)\n");
@@ -2401,27 +2523,26 @@ do_add_cookie_secret(RES* ssl, xfrd_stat
 		(void)ssl_printf(ssl, "please provide a 128bit hex encoded secret\n");
 		return;
 	}
-	if(!nsd->options->cookie_secret_file || !nsd->options->cookie_secret_file[0]) {
-		explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE);
-		explicit_bzero(arg, strlen(arg));
-		(void)ssl_printf(ssl, "error: no cookie secret file configured\n");
-		return;
-	}
-	if(!cookie_secret_file_dump(ssl, nsd)) {
-		explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE);
-		explicit_bzero(arg, strlen(arg));
-		(void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n",
-				nsd->options->cookie_secret_file);
-		return;
+	explicit_bzero(arg, strlen(arg));
+
+	backup_cookie_count = nsd->cookie_count;
+	memcpy( backup_cookie_secrets, nsd->cookie_secrets
+	      , sizeof(cookie_secrets_type));
+	if(nsd->cookie_secrets_source != COOKIE_SECRETS_FROM_FILE
+	&& nsd->cookie_secrets_source != COOKIE_SECRETS_FROM_CONFIG) {
+		nsd->cookie_count = 0;
 	}
 	add_cookie_secret(nsd, secret);
 	explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE);
-	(void)cookie_secret_file_dump(ssl, nsd);
-	task_new_add_cookie_secret(xfrd->nsd->task[xfrd->nsd->mytask],
-	    xfrd->last_task, arg);
-	explicit_bzero(arg, strlen(arg));
-	xfrd_set_reload_now(xfrd);
-	send_ok(ssl);
+	if(!cookie_secret_file_dump_and_reload(ssl, nsd)) {
+		explicit_bzero(arg, strlen(arg));
+		(void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n"
+		                , nsd->options->cookie_secret_file);
+		memcpy( nsd->cookie_secrets, backup_cookie_secrets
+		      , sizeof(cookie_secrets_type));
+		nsd->cookie_count = backup_cookie_count;
+	}
+	explicit_bzero(backup_cookie_secrets, sizeof(cookie_secrets_type));
 }
 
 static void
@@ -2431,7 +2552,27 @@ do_print_cookie_secrets(RES* ssl, xfrd_s
 	int i;
 	(void)arg;
 
-	/* (void)ssl_printf(ssl, "cookie_secret_count=%zu\n", nsd->cookie_count); */
+	switch(nsd->cookie_secrets_source){
+	case COOKIE_SECRETS_NONE:
+		break;
+	case COOKIE_SECRETS_GENERATED:
+		if(!ssl_printf(ssl, "source : random generated\n"))
+			return;
+		break;
+	case COOKIE_SECRETS_FROM_FILE:
+		if(!ssl_printf( ssl, "source : \"%s\"\n"
+		          , nsd->cookie_secrets_filename))
+			return;
+		break;
+	case COOKIE_SECRETS_FROM_CONFIG:
+		if(!ssl_printf(ssl, "source : configuration\n"))
+			return;
+		break;
+	default:
+		if(!ssl_printf(ssl, "source : unknown\n"))
+			return;
+		break;
+	}
 	for(i = 0; (size_t)i < nsd->cookie_count; i++) {
 		struct cookie_secret const* cs = &nsd->cookie_secrets[i];
 		ssize_t const len = hex_ntop(cs->cookie_secret, NSD_COOKIE_SECRET_SIZE,
@@ -2669,7 +2810,11 @@ remote_control_callback(int fd, short ev
 		VERBOSITY(3, (LOG_INFO, "unauthenticated remote control connection"));
 #ifdef HAVE_SSL
 	} else if(SSL_get_verify_result(s->ssl) == X509_V_OK) {
+#  ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
+		X509* x = SSL_get1_peer_certificate(s->ssl);
+#  else
 		X509* x = SSL_get_peer_certificate(s->ssl);
+#  endif
 		if(!x) {
 			VERBOSITY(2, (LOG_INFO, "remote control connection "
 				"provided no client certificate"));
Index: remote.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/remote.h,v
diff -u -p -r1.4 remote.h
--- remote.h	20 Dec 2023 17:29:01 -0000	1.4
+++ remote.h	3 Sep 2025 13:53:32 -0000
@@ -101,4 +101,6 @@ void daemon_remote_attach(struct daemon_
  */
 int create_local_accept_sock(const char* path, int* noproto);
 
+void xfrd_reload_config(struct xfrd_state *xfrd);
+
 #endif /* DAEMON_REMOTE_H */
Index: server.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/server.c,v
diff -u -p -r1.52 server.c
--- server.c	12 Apr 2024 15:53:34 -0000	1.52
+++ server.c	3 Sep 2025 13:53:32 -0000
@@ -162,6 +162,7 @@ struct tcp_accept_handler_data {
 #ifdef HAVE_SSL
 	/* handler accepts TLS connections on the dedicated port */
 	int                tls_accept;
+	int                tls_auth_accept;
 #endif
 	/* if set, PROXYv2 is expected on this connection */
 	int pp2_enabled;
@@ -285,9 +286,10 @@ struct tcp_handler_data
 
 #ifdef HAVE_SSL
 	/*
-	 * TLS object.
+	 * TLS objects.
 	 */
 	SSL* tls;
+	SSL* tls_auth;
 
 	/*
 	 * TLS handshake state.
@@ -435,7 +437,6 @@ static int
 restart_child_servers(struct nsd *nsd, region_type* region, netio_type* netio,
 	int* xfrd_sock_p)
 {
-	struct main_ipc_handler_data *ipc_data;
 	size_t i;
 	int sv[2];
 
@@ -461,17 +462,11 @@ restart_child_servers(struct nsd *nsd, r
 				}
 				if(!nsd->children[i].handler)
 				{
+					struct main_ipc_handler_data *ipc_data;
 					ipc_data = (struct main_ipc_handler_data*) region_alloc(
 						region, sizeof(struct main_ipc_handler_data));
 					ipc_data->nsd = nsd;
 					ipc_data->child = &nsd->children[i];
-					ipc_data->child_num = i;
-					ipc_data->xfrd_sock = xfrd_sock_p;
-					ipc_data->packet = buffer_create(region, QIOBUFSZ);
-					ipc_data->forward_mode = 0;
-					ipc_data->got_bytes = 0;
-					ipc_data->total_bytes = 0;
-					ipc_data->acl_num = 0;
 					nsd->children[i].handler = (struct netio_handler*) region_alloc(
 						region, sizeof(struct netio_handler));
 					nsd->children[i].handler->fd = nsd->children[i].child_fd;
@@ -481,10 +476,6 @@ restart_child_servers(struct nsd *nsd, r
 					nsd->children[i].handler->event_handler = parent_handle_child_command;
 					netio_add_handler(netio, nsd->children[i].handler);
 				}
-				/* clear any ongoing ipc */
-				ipc_data = (struct main_ipc_handler_data*)
-					nsd->children[i].handler->user_data;
-				ipc_data->forward_mode = 0;
 				/* restart - update fd */
 				nsd->children[i].handler->fd = nsd->children[i].child_fd;
 				break;
@@ -1455,13 +1446,11 @@ server_init(struct nsd *nsd)
 			if(open_udp_socket(nsd, &nsd->udp[i], &reuseport) == -1) {
 				return -1;
 			}
-			/* Turn off REUSEPORT for TCP by copying the socket
-			 * file descriptor.
-			 * This means we should not close TCP used by
-			 * other servers in reuseport enabled mode, in
-			 * server_child().
-			 */
 			nsd->tcp[i] = nsd->tcp[i%nsd->ifs];
+			nsd->tcp[i].s = -1;
+			if(open_tcp_socket(nsd, &nsd->tcp[i], &reuseport) == -1) {
+				return -1;
+			}
 		}
 
 		nsd->ifs = ifs;
@@ -1620,6 +1609,8 @@ server_shutdown(struct nsd *nsd)
 #ifdef HAVE_SSL
 	if (nsd->tls_ctx)
 		SSL_CTX_free(nsd->tls_ctx);
+	if (nsd->tls_auth_ctx)
+		SSL_CTX_free(nsd->tls_auth_ctx);
 #endif
 
 #ifdef MEMCLEAN /* OS collects memory pages */
@@ -1644,6 +1635,7 @@ void
 server_prepare_xfrd(struct nsd* nsd)
 {
 	char tmpfile[256];
+	size_t i;
 	/* create task mmaps */
 	nsd->mytask = 0;
 	snprintf(tmpfile, sizeof(tmpfile), "%snsd-xfr-%d/nsd.%u.task.0",
@@ -1687,6 +1679,24 @@ server_prepare_xfrd(struct nsd* nsd)
 		nsd;
 	((struct ipc_handler_conn_data*)nsd->xfrd_listener->user_data)->conn =
 		xfrd_tcp_create(nsd->region, QIOBUFSZ);
+	/* setup sockets to pass NOTIFY messages from the serve processes */
+	nsd->serve2xfrd_fd_send = region_alloc_array(
+			nsd->region, 2 * nsd->child_count, sizeof(int));
+	nsd->serve2xfrd_fd_recv= region_alloc_array(
+			nsd->region, 2 * nsd->child_count, sizeof(int));
+	for(i=0; i < 2 * nsd->child_count; i++) {
+		int pipefd[2];
+		pipefd[0] = -1; /* For receiving by parent (xfrd) */
+		pipefd[1] = -1; /* For sending   by child  (server childs) */
+		if(pipe(pipefd) < 0) {
+                        log_msg(LOG_ERR, "fatal error: cannot create NOTIFY "
+				"communication channel: %s", strerror(errno));
+			exit(1);
+                }
+                nsd->serve2xfrd_fd_recv[i] = pipefd[0];
+                nsd->serve2xfrd_fd_send[i] = pipefd[1];
+	}
+	nsd->serve2xfrd_fd_swap = nsd->serve2xfrd_fd_send + nsd->child_count;
 }
 
 
@@ -1696,6 +1706,7 @@ server_start_xfrd(struct nsd *nsd, int d
 	pid_t pid;
 	int sockets[2] = {0,0};
 	struct ipc_handler_conn_data *data;
+	size_t i;
 
 	if(nsd->xfrd_listener->fd != -1)
 		close(nsd->xfrd_listener->fd);
@@ -1732,6 +1743,14 @@ server_start_xfrd(struct nsd *nsd, int d
 		 * restarted, the reload is using nsd->mytask */
 		nsd->mytask = 1 - nsd->mytask;
 
+		/* close the send site of the serve2xfrd fds */
+		assert(nsd->serve2xfrd_fd_send < nsd->serve2xfrd_fd_swap);
+		for(i = 0; i < 2 * nsd->child_count; i++) {
+			if(nsd->serve2xfrd_fd_send[i] != -1) {
+				close(nsd->serve2xfrd_fd_send[i]);
+				nsd->serve2xfrd_fd_send[i] = -1;
+			}
+		}
 #ifdef HAVE_SETPROCTITLE
 		setproctitle("xfrd");
 #endif
@@ -1754,6 +1773,13 @@ server_start_xfrd(struct nsd *nsd, int d
 			log_msg(LOG_ERR, "cannot fcntl pipe: %s", strerror(errno));
 		}
 		nsd->xfrd_listener->fd = sockets[0];
+		/* close the receive site of the serve2xfrd fds */
+		for(i = 0; i < 2 * nsd->child_count; i++) {
+			if(nsd->serve2xfrd_fd_recv[i] != -1) {
+				close(nsd->serve2xfrd_fd_recv[i]);
+				nsd->serve2xfrd_fd_recv[i] = -1;
+			}
+		}
 #ifdef HAVE_SETPROCTITLE
 		setproctitle("main");
 #endif
@@ -1809,7 +1835,7 @@ server_send_soa_xfrd(struct nsd* nsd, in
 			server_close_all_sockets(nsd->tcp, nsd->ifs);
 			daemon_remote_close(nsd->rc);
 			/* Unlink it if possible... */
-			unlinkpid(nsd->pidfile);
+			unlinkpid(nsd->pidfile, nsd->username);
 			unlink(nsd->task[0]->fname);
 			unlink(nsd->task[1]->fname);
 #ifdef USE_ZONE_STATS
@@ -2152,7 +2178,7 @@ server_tls_ctx_setup(char* key, char* pe
 			return NULL;
 		}
 		SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(verifypem));
-		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
+		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
 	}
 	return ctx;
 }
@@ -2272,50 +2298,85 @@ block_read(struct nsd* nsd, int s, void*
 }
 
 static void
-reload_process_tasks(struct nsd* nsd, udb_ptr* last_task, int cmdsocket)
+reload_process_non_xfr_tasks(struct nsd* nsd, udb_ptr* xfrs2process,
+		udb_ptr* last_task)
 {
-	sig_atomic_t cmd = NSD_QUIT_SYNC;
-	udb_ptr t, next;
+	udb_ptr t, next, xfr_tail;
 	udb_base* u = nsd->task[nsd->mytask];
 	udb_ptr_init(&next, u);
+	udb_ptr_init(&xfr_tail, u);
 	udb_ptr_new(&t, u, udb_base_get_userdata(u));
 	udb_base_set_userdata(u, 0);
+	/* Execute all tasks except of type "task_apply_xfr". */
 	while(!udb_ptr_is_null(&t)) {
 		/* store next in list so this one can be deleted or reused */
 		udb_ptr_set_rptr(&next, u, &TASKLIST(&t)->next);
 		udb_rptr_zero(&TASKLIST(&t)->next, u);
 
-		/* process task t */
-		/* append results for task t and update last_task */
-		task_process_in_reload(nsd, u, last_task, &t);
-
+		if(TASKLIST(&t)->task_type != task_apply_xfr) {
+			/* process task t */
+			/* append results for task t and update last_task */
+			task_process_in_reload(nsd, u, last_task, &t);
+
+		} else if(udb_ptr_is_null(xfrs2process)) {
+			udb_ptr_set_ptr( xfrs2process, u, &t);
+			udb_ptr_set_ptr(&xfr_tail, u, &t);
+		} else {
+			udb_rptr_set_ptr(&TASKLIST(&xfr_tail)->next, u, &t);
+			udb_ptr_set_ptr(&xfr_tail, u, &t);
+		}
 		/* go to next */
 		udb_ptr_set_ptr(&t, u, &next);
+	}
+	/* t and next are already unlinked (because they are null) */
+	udb_ptr_unlink(&xfr_tail, u);
+}
 
-		/* if the parent has quit, we must quit too, poll the fd for cmds */
-		if(block_read(nsd, cmdsocket, &cmd, sizeof(cmd), 0) == sizeof(cmd)) {
-			DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: ipc command from main %d", (int)cmd));
-			if(cmd == NSD_QUIT) {
-				DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: quit to follow nsd"));
-				/* unlink files of remainder of tasks */
-				while(!udb_ptr_is_null(&t)) {
-					if(TASKLIST(&t)->task_type == task_apply_xfr) {
-						xfrd_unlink_xfrfile(nsd, TASKLIST(&t)->yesno);
-					}
-					udb_ptr_set_rptr(&t, u, &TASKLIST(&t)->next);
-				}
-				udb_ptr_unlink(&t, u);
-				udb_ptr_unlink(&next, u);
-				exit(0);
+static size_t
+reload_process_xfr_tasks(struct nsd* nsd, int cmdsocket, udb_ptr* xfrs2process)
+{
+	sig_atomic_t cmd = NSD_QUIT_SYNC;
+	udb_ptr next;
+	udb_base* u = nsd->task[nsd->mytask];
+	size_t xfrs_processed = 0;
+
+	udb_ptr_init(&next, u);
+	while(!udb_ptr_is_null(xfrs2process)) {
+		/* store next in list so this one can be deleted or reused */
+		udb_ptr_set_rptr(&next, u, &TASKLIST(xfrs2process)->next);
+		udb_rptr_zero(&TASKLIST(xfrs2process)->next, u);
+		
+		/* process xfr task at xfrs2process */
+		assert(TASKLIST(xfrs2process)->task_type == task_apply_xfr);
+		task_process_apply_xfr(nsd, u, xfrs2process);
+		xfrs_processed += 1;
+
+		/* go to next */
+		udb_ptr_set_ptr(xfrs2process, u, &next);
+
+		/* if the "old-main" has quit, we must quit too, poll the fd for cmds */
+		if(block_read(nsd, cmdsocket, &cmd, sizeof(cmd), 0) != sizeof(cmd))
+			; /* pass */
+		else if (cmd != NSD_QUIT)
+			DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: ipc command from old-main %d", (int)cmd));
+		else {
+			udb_ptr_unlink(&next, u);
+			DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: quit to follow nsd"));
+			/* unlink files of remainder of tasks */
+			while(!udb_ptr_is_null(xfrs2process)) {
+				assert(TASKLIST(xfrs2process)->task_type == task_apply_xfr);
+				xfrd_unlink_xfrfile(nsd, TASKLIST(xfrs2process)->yesno);
+				udb_ptr_set_rptr(xfrs2process, u, &TASKLIST(xfrs2process)->next);
 			}
+			exit(0);
 		}
-
 	}
-	udb_ptr_unlink(&t, u);
-	udb_ptr_unlink(&next, u);
+	/* xfrs2process and next are already unlinked (because they are null) */
+	return xfrs_processed;
 }
 
-void server_verify(struct nsd *nsd, int cmdsocket);
+static void server_verify(struct nsd *nsd, int cmdsocket,
+	struct sigaction* old_sigchld);
 
 struct quit_sync_event_data {
 	struct event_base* base;
@@ -2395,11 +2456,10 @@ static void server_reload_handle_quit_sy
  */
 static void
 server_reload(struct nsd *nsd, region_type* server_region, netio_type* netio,
-	int cmdsocket)
+	int cmdsocket, udb_ptr* xfrs2process, udb_ptr* last_task)
 {
 	pid_t mypid;
 	sig_atomic_t cmd;
-	udb_ptr last_task;
 	struct sigaction old_sigchld, ign_sigchld;
 	struct radnode* node;
 	zone_type* zone;
@@ -2407,6 +2467,10 @@ server_reload(struct nsd *nsd, region_ty
 	struct quit_sync_event_data cb_data;
 	struct event signal_event, cmd_event;
 	struct timeval reload_sync_timeout;
+	size_t xfrs_processed = 0;
+	/* For swapping filedescriptors from the serve childs to the xfrd
+	 * and/or the dnstap collector */
+	int *swap_fd_send;
 
 	/* ignore SIGCHLD from the previous server_main that used this pid */
 	memset(&ign_sigchld, 0, sizeof(ign_sigchld));
@@ -2420,9 +2484,7 @@ server_reload(struct nsd *nsd, region_ty
 #endif
 
 	/* see what tasks we got from xfrd */
-	task_remap(nsd->task[nsd->mytask]);
-	udb_ptr_init(&last_task, nsd->task[nsd->mytask]);
-	reload_process_tasks(nsd, &last_task, cmdsocket);
+	xfrs_processed = reload_process_xfr_tasks(nsd, cmdsocket, xfrs2process);
 
 #ifndef NDEBUG
 	if(nsd_debug_level >= 1)
@@ -2452,17 +2514,16 @@ server_reload(struct nsd *nsd, region_ty
 #endif
 
 		/* spin-up server and execute verifiers for each zone */
-		server_verify(nsd, cmdsocket);
+		server_verify(nsd, cmdsocket, &old_sigchld);
 #ifdef RATELIMIT
 		/* deallocate rate limiting resources */
 		rrl_deinit(nsd->child_count + 1);
 #endif
 	}
 
-	for(node = radix_first(nsd->db->zonetree);
-	    node != NULL;
-	    node = radix_next(node))
-	{
+	if(xfrs_processed) for( node = radix_first(nsd->db->zonetree)
+	                      ; node != NULL; node = radix_next(node)) {
+
 		zone = (zone_type *)node->elem;
 		if(zone->is_updated) {
 			if(zone->is_bad) {
@@ -2474,12 +2535,12 @@ server_reload(struct nsd *nsd, region_ty
 			/* update(s), verified or not, possibly with subsequent
 			   skipped update(s). skipped update(s) are picked up
 			   by failed update check in xfrd */
-			task_new_soainfo(nsd->task[nsd->mytask], &last_task,
+			task_new_soainfo(nsd->task[nsd->mytask], last_task,
 			                 zone, hint);
 		} else if(zone->is_skipped) {
 			/* corrupt or inconsistent update without preceding
 			   update(s), communicate soainfo_gone */
-			task_new_soainfo(nsd->task[nsd->mytask], &last_task,
+			task_new_soainfo(nsd->task[nsd->mytask], last_task,
 			                 zone, soainfo_gone);
 		}
 		zone->is_updated = 0;
@@ -2494,7 +2555,6 @@ server_reload(struct nsd *nsd, region_ty
 	sigaction(SIGCHLD, &old_sigchld, NULL);
 #ifdef USE_DNSTAP
 	if (nsd->dt_collector) {
-		int *swap_fd_send;
 		DEBUG(DEBUG_IPC,1, (LOG_INFO, "reload: swap dnstap collector pipes"));
 		/* Swap fd_send with fd_swap so old serve child and new serve
 		 * childs will not write to the same pipe ends simultaneously */
@@ -2504,6 +2564,9 @@ server_reload(struct nsd *nsd, region_ty
 
 	}
 #endif
+	swap_fd_send = nsd->serve2xfrd_fd_send;
+	nsd->serve2xfrd_fd_send = nsd->serve2xfrd_fd_swap;
+	nsd->serve2xfrd_fd_swap = swap_fd_send;
 	/* Start new child processes */
 	if (server_start_children(nsd, server_region, netio, &nsd->
 		xfrd_listener->fd) != 0) {
@@ -2544,7 +2607,7 @@ server_reload(struct nsd *nsd, region_ty
 	event_set(&signal_event, SIGCHLD, EV_SIGNAL|EV_PERSIST,
 	    server_reload_handle_sigchld, NULL);
 	if(event_base_set(cb_data.base, &signal_event) != 0
-	|| event_add(&signal_event, NULL) != 0) {
+	|| signal_add(&signal_event, NULL) != 0) {
 		log_msg(LOG_ERR, "NSD quit sync: could not add signal event");
 	}
 
@@ -2560,7 +2623,9 @@ server_reload(struct nsd *nsd, region_ty
 
 	/* remove command and signal event handlers */
 	event_del(&cmd_event);
-	event_del(&signal_event);
+	signal_del(&signal_event);
+	/* restore the ordinary signal handler for SIGCHLD */
+	sigaction(SIGCHLD, &old_sigchld, NULL);
 	event_base_free(cb_data.base);
 	cmd = cb_data.to_read.cmd;
 
@@ -2570,7 +2635,7 @@ server_reload(struct nsd *nsd, region_ty
 		exit(1);
 	}
 	assert(cmd == NSD_RELOAD);
-	udb_ptr_unlink(&last_task, nsd->task[nsd->mytask]);
+	udb_ptr_unlink(last_task, nsd->task[nsd->mytask]);
 	task_process_sync(nsd->task[nsd->mytask]);
 #ifdef USE_ZONE_STATS
 	server_zonestat_realloc(nsd); /* realloc for next children */
@@ -2647,6 +2712,10 @@ server_main(struct nsd *nsd)
 	netio_type *netio = netio_create(server_region);
 	netio_handler_type reload_listener;
 	int reload_sockets[2] = {-1, -1};
+	/* pointer to the xfr tasks that will be processed in a second pass */
+	udb_ptr xfrs2process;
+	/* pointer to results of task processing */
+	udb_ptr last_task;
 	struct timespec timeout_spec;
 	int status;
 	pid_t child_pid;
@@ -2664,6 +2733,8 @@ server_main(struct nsd *nsd)
 	nsd->st->db_disk = 0;
 	nsd->st->db_mem = region_get_mem(nsd->db->region);
 #endif
+	memset(&xfrs2process, 0, sizeof(xfrs2process));
+	memset(&last_task, 0, sizeof(last_task));
 
 	/* Start the child processes that handle incoming queries */
 	if (server_start_children(nsd, server_region, netio,
@@ -2864,7 +2935,22 @@ server_main(struct nsd *nsd)
 				reload_pid = -1;
 				break;
 			}
-
+			/* Execute the tasks that cannot fail */
+#ifdef HAVE_SETPROCTITLE
+			setproctitle("load");
+#endif
+#ifdef USE_LOG_PROCESS_ROLE
+			log_set_process_role("load");
+#endif
+			/* Already process the non xfr tasks, so that a failed
+			 * transfer (which can exit) will not nullify the
+			 * effects of the other tasks that will not exit.
+			 */
+			task_remap(nsd->task[nsd->mytask]);
+			udb_ptr_init(&xfrs2process, nsd->task[nsd->mytask]);
+			udb_ptr_init(&last_task   , nsd->task[nsd->mytask]);
+			reload_process_non_xfr_tasks(nsd, &xfrs2process
+			                                , &last_task);
 			/* Do actual reload */
 			reload_pid = fork();
 			switch (reload_pid) {
@@ -2874,21 +2960,11 @@ server_main(struct nsd *nsd)
 			default:
 				/* PARENT */
 				close(reload_sockets[0]);
-#ifdef HAVE_SETPROCTITLE
-				setproctitle("load");
-#endif
-#ifdef USE_LOG_PROCESS_ROLE
-				log_set_process_role("load");
-#endif
-				server_reload(nsd, server_region, netio,
-					reload_sockets[1]);
+				server_reload(nsd, server_region, netio
+				                 , reload_sockets[1]
+				                 , &xfrs2process
+						 , &last_task);
 				DEBUG(DEBUG_IPC,2, (LOG_INFO, "Reload exited to become new main"));
-#ifdef HAVE_SETPROCTITLE
-				setproctitle("main");
-#endif
-#ifdef USE_LOG_PROCESS_ROLE
-				log_set_process_role("main");
-#endif
 				close(reload_sockets[1]);
 				DEBUG(DEBUG_IPC,2, (LOG_INFO, "Reload closed"));
 				/* drop stale xfrd ipc data */
@@ -2920,6 +2996,24 @@ server_main(struct nsd *nsd)
 				reload_pid = getppid();
 				break;
 			}
+			if(reload_pid == -1) {
+				/* Reset proctitle after "load" process exited
+				 * or when fork() failed
+				 */
+#ifdef HAVE_SETPROCTITLE
+				setproctitle("main");
+#endif
+#ifdef USE_LOG_PROCESS_ROLE
+				log_set_process_role("main");
+#endif
+			}
+			/* xfrs2process and last_task need to be reset in case
+			 * "old-main" becomes "main" (due to an failed (exited)
+			 * xfr update). If needed xfrs2process gets unlinked by
+			 * "load", and last_task by the xfrd.
+			 */
+			memset(&xfrs2process, 0, sizeof(xfrs2process));
+			memset(&last_task, 0, sizeof(last_task));
 			break;
 		case NSD_QUIT_SYNC:
 			/* synchronisation of xfrd, parent and reload */
@@ -2989,7 +3083,7 @@ server_main(struct nsd *nsd)
 	send_children_quit_and_wait(nsd);
 
 	/* Unlink it if possible... */
-	unlinkpid(nsd->pidfile);
+	unlinkpid(nsd->pidfile, nsd->username);
 	unlink(nsd->task[0]->fname);
 	unlink(nsd->task[1]->fname);
 #ifdef USE_ZONE_STATS
@@ -3189,11 +3283,25 @@ add_tcp_handler(
 		if(verbosity >= 2) {
 			char buf[48];
 			addrport2str((void*)(struct sockaddr_storage*)&sock->addr.ai_addr, buf, sizeof(buf));
-			VERBOSITY(4, (LOG_NOTICE, "setup TCP for TLS service on interface %s", buf));
+			VERBOSITY(5, (LOG_NOTICE, "setup TCP for TLS service on interface %s", buf));
 		}
 	} else {
 		data->tls_accept = 0;
 	}
+	if (nsd->tls_auth_ctx &&
+	    nsd->options->tls_auth_port &&
+	    using_tls_port((struct sockaddr *)&sock->addr.ai_addr, nsd->options->tls_auth_port))
+	{
+		data->tls_auth_accept = 1;
+		if(verbosity >= 2) {
+			char buf[48];
+			addrport2str((void*)(struct sockaddr_storage*)&sock->addr.ai_addr, buf, sizeof(buf));
+			VERBOSITY(4, (LOG_NOTICE, "setup TCP for TLS-AUTH service on interface %s", buf));
+		}
+
+	} else {
+		data->tls_auth_accept = 0;
+	}
 #endif
 
 	memset(handler, 0, sizeof(*handler));
@@ -3208,7 +3316,8 @@ add_tcp_handler(
 /*
  * Serve DNS request to verifiers (short-lived)
  */
-void server_verify(struct nsd *nsd, int cmdsocket)
+static void server_verify(struct nsd *nsd, int cmdsocket,
+	struct sigaction* old_sigchld)
 {
 	size_t size = 0;
 	struct event cmd_event, signal_event, exit_event;
@@ -3315,12 +3424,13 @@ void server_verify(struct nsd *nsd, int 
 
 	/* remove command and exit event handlers */
 	event_del(&exit_event);
-	event_del(&signal_event);
 	event_del(&cmd_event);
 
 	assert(nsd->next_zone_to_verify == NULL || nsd->mode == NSD_QUIT);
 	assert(nsd->verifier_count == 0 || nsd->mode == NSD_QUIT);
+	signal_del(&signal_event);
 fail:
+	sigaction(SIGCHLD, old_sigchld, NULL);
 	close(nsd->verifier_pipe[0]);
 	close(nsd->verifier_pipe[1]);
 fail_pipe:
@@ -3478,15 +3588,7 @@ server_child(struct nsd *nsd)
 				add_tcp_handler(nsd, &nsd->tcp[i], data);
 			} else {
 				/* close sockets intended for other servers */
-				/*
-				 * uncomment this once tcp servers are no
-				 * longer copied in the tcp fd copy line
-				 * in server_init().
 				server_close_socket(&nsd->tcp[i]);
-				*/
-				/* close sockets not meant for this server*/
-				if(!listen)
-					server_close_socket(&nsd->tcp[i]);
 			}
 		}
 	} else {
@@ -3575,7 +3677,7 @@ service_remaining_tcp(struct nsd* nsd)
 	/* check if it is needed */
 	if(nsd->current_tcp_count == 0 || tcp_active_list == NULL)
 		return;
-	VERBOSITY(4, (LOG_INFO, "service remaining TCP connections"));
+	VERBOSITY(5, (LOG_INFO, "service remaining TCP connections"));
 #ifdef USE_DNSTAP
 	/* remove dnstap collector, we cannot write there because the new
 	 * child process is using the file descriptor, or the child
@@ -3604,6 +3706,10 @@ service_remaining_tcp(struct nsd* nsd)
 			if((event&EV_READ))
 				fn = handle_tls_reading;
 			else	fn = handle_tls_writing;
+		} else if(p->tls_auth) {
+			if((event&EV_READ))
+				fn = handle_tls_reading;
+			else	fn = handle_tls_writing;
 		} else {
 #endif
 			if((event&EV_READ))
@@ -3663,7 +3769,7 @@ service_remaining_tcp(struct nsd* nsd)
 			event_del(&timeout);
 		} else {
 			/* timed out, quit */
-			VERBOSITY(4, (LOG_INFO, "service remaining TCP connections: timed out, quit"));
+			VERBOSITY(5, (LOG_INFO, "service remaining TCP connections: timed out, quit"));
 			break;
 		}
 	}
@@ -4111,6 +4217,11 @@ cleanup_tcp_handler(struct tcp_handler_d
 		SSL_free(data->tls);
 		data->tls = NULL;
 	}
+	if(data->tls_auth) {
+		SSL_shutdown(data->tls_auth);
+		SSL_free(data->tls_auth);
+		data->tls_auth = NULL;
+	}
 #endif
 	data->pp2_header_state = pp2_header_none;
 	close(data->event.ev_fd);
@@ -4659,10 +4770,17 @@ tls_handshake(struct tcp_handler_data* d
 
 	/* (continue to) setup the TLS connection */
 	ERR_clear_error();
-	r = SSL_do_handshake(data->tls);
+	if(data->tls_auth)
+		r = SSL_do_handshake(data->tls_auth);
+	else
+		r = SSL_do_handshake(data->tls);
 
 	if(r != 1) {
-		int want = SSL_get_error(data->tls, r);
+		int want;
+		if(data->tls_auth)
+			want = SSL_get_error(data->tls_auth, r);
+		else
+			want = SSL_get_error(data->tls, r);
 		if(want == SSL_ERROR_WANT_READ) {
 			if(data->shake_state == tls_hs_read) {
 				/* try again later */
@@ -4683,7 +4801,7 @@ tls_handshake(struct tcp_handler_data* d
 			return 1;
 		} else {
 			if(r == 0)
-				VERBOSITY(3, (LOG_ERR, "TLS handshake: connection closed prematurely"));
+				VERBOSITY(5, (LOG_ERR, "TLS handshake: connection closed prematurely"));
 			else {
 				unsigned long err = ERR_get_error();
 				if(!squelch_err_ssl_handshake(err)) {
@@ -4699,7 +4817,10 @@ tls_handshake(struct tcp_handler_data* d
 	}
 
 	/* Use to log successful upgrade for testing - could be removed*/
-	VERBOSITY(3, (LOG_INFO, "TLS handshake succeeded."));
+	if(data->tls_auth)
+		VERBOSITY(5, (LOG_INFO, "TLS-AUTH handshake succeeded."));
+	else
+		VERBOSITY(5, (LOG_INFO, "TLS handshake succeeded."));
 	/* set back to the event we need to have when reading (or writing) */
 	if(data->shake_state == tls_hs_read && writing) {
 		tcp_handler_setup_event(data, handle_tls_writing, fd, EV_PERSIST|EV_TIMEOUT|EV_WRITE);
@@ -4717,9 +4838,18 @@ static int
 more_read_buf_tls(int fd, struct tcp_handler_data* data, void* bufpos,
 	size_t add_amount, ssize_t* received)
 {
+	int r;
 	ERR_clear_error();
-	if((*received=SSL_read(data->tls, bufpos, add_amount)) <= 0) {
-		int want = SSL_get_error(data->tls, *received);
+	if(data->tls_auth)
+		r = (*received=SSL_read(data->tls_auth, bufpos, add_amount));
+	else
+		r = (*received=SSL_read(data->tls, bufpos, add_amount));
+	if(r <= 0) {
+		int want;
+		if(data->tls_auth)
+			want = SSL_get_error(data->tls_auth, *received);
+		else
+			want = SSL_get_error(data->tls, *received);
 		if(want == SSL_ERROR_ZERO_RETURN) {
 			cleanup_tcp_handler(data);
 			return 0; /* shutdown, closed */
@@ -5039,7 +5169,10 @@ handle_tls_writing(int fd, short event, 
 			return;
 	}
 
-	(void)SSL_set_mode(data->tls, SSL_MODE_ENABLE_PARTIAL_WRITE);
+	if(data->tls_auth)
+		(void)SSL_set_mode(data->tls_auth, SSL_MODE_ENABLE_PARTIAL_WRITE);
+	else
+		(void)SSL_set_mode(data->tls, SSL_MODE_ENABLE_PARTIAL_WRITE);
 
 	/* If we are writing the start of a message, we must include the length
 	 * this is done with a copy into write_buffer. */
@@ -5066,9 +5199,16 @@ handle_tls_writing(int fd, short event, 
 
 	/* Write the response */
 	ERR_clear_error();
-	sent = SSL_write(data->tls, buffer_current(write_buffer), buffer_remaining(write_buffer));
+	if(data->tls_auth)
+		sent = SSL_write(data->tls_auth, buffer_current(write_buffer), buffer_remaining(write_buffer));
+	else
+		sent = SSL_write(data->tls, buffer_current(write_buffer), buffer_remaining(write_buffer));
 	if(sent <= 0) {
-		int want = SSL_get_error(data->tls, sent);
+		int want;
+		if(data->tls_auth)
+			want = SSL_get_error(data->tls_auth, sent);
+		else
+			want = SSL_get_error(data->tls, sent);
 		if(want == SSL_ERROR_ZERO_RETURN) {
 			cleanup_tcp_handler(data);
 			/* closed */
@@ -5269,7 +5409,9 @@ handle_tcp_accept(int fd, short event, v
 	tcp_data->query_count = 0;
 #ifdef HAVE_SSL
 	tcp_data->shake_state = tls_hs_none;
+	/* initialize both incase of dangling pointers */
 	tcp_data->tls = NULL;
+	tcp_data->tls_auth = NULL;
 #endif
 	tcp_data->query_needs_reset = 1;
 	tcp_data->pp2_enabled = data->pp2_enabled;
@@ -5309,6 +5451,18 @@ handle_tcp_accept(int fd, short event, v
 			close(s);
 			return;
 		}
+		tcp_data->query->tls = tcp_data->tls;
+		tcp_data->shake_state = tls_hs_read;
+		memset(&tcp_data->event, 0, sizeof(tcp_data->event));
+		event_set(&tcp_data->event, s, EV_PERSIST | EV_READ | EV_TIMEOUT,
+			  handle_tls_reading, tcp_data);
+	} else if (data->tls_auth_accept) {
+		tcp_data->tls_auth = incoming_ssl_fd(tcp_data->nsd->tls_auth_ctx, s);
+		if(!tcp_data->tls_auth) {
+			close(s);
+			return;
+		}
+		tcp_data->query->tls_auth = tcp_data->tls_auth;
 		tcp_data->shake_state = tls_hs_read;
 		memset(&tcp_data->event, 0, sizeof(tcp_data->event));
 		event_set(&tcp_data->event, s, EV_PERSIST | EV_READ | EV_TIMEOUT,
Index: util.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/util.c,v
diff -u -p -r1.29 util.c
--- util.c	12 Apr 2024 15:53:34 -0000	1.29
+++ util.c	3 Sep 2025 13:53:32 -0000
@@ -12,6 +12,9 @@
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
+#ifdef HAVE_OPENSSL_RAND_H
+#include <openssl/rand.h>
+#endif
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -37,6 +40,7 @@
 #include "rdata.h"
 #include "zonec.h"
 #include "nsd.h"
+#include "options.h"
 
 #ifdef USE_MMAP_ALLOC
 #include <sys/mman.h>
@@ -62,6 +66,7 @@ static const char *global_ident = NULL;
 static log_function_type *current_log_function = log_file;
 static FILE *current_log_file = NULL;
 int log_time_asc = 1;
+int log_time_iso = 0;
 
 #ifdef USE_LOG_PROCESS_ROLE
 void
@@ -160,15 +165,39 @@ log_file(int priority, const char *messa
 		char tmbuf[32];
 		tmbuf[0]=0;
 		tv.tv_usec = 0;
-		if(gettimeofday(&tv, NULL) == 0) {
-			struct tm tm;
-			time_t now = (time_t)tv.tv_sec;
-			strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S",
-				localtime_r(&now, &tm));
+		if(log_time_iso) {
+			char tzbuf[16];
+			tzbuf[0]=0;
+			/* log time in iso format */
+			if(gettimeofday(&tv, NULL) == 0) {
+				struct tm tm, *tm_p;
+				time_t now = (time_t)tv.tv_sec;
+				tm_p = localtime_r(&now, &tm);
+				strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%dT%H:%M:%S",
+					tm_p);
+				if(strftime(tzbuf, sizeof(tzbuf), "%z", tm_p) == 5) {
+					/* put ':' in "+hh:mm" */
+					tzbuf[5] = tzbuf[4];
+					tzbuf[4] = tzbuf[3];
+					tzbuf[3] = ':';
+					tzbuf[6] = 0;
+				}
+			}
+			fprintf(current_log_file, "%s.%3.3d%s %s[%d]: %s: %s",
+				tmbuf, (int)tv.tv_usec/1000, tzbuf,
+				global_ident, (int) getpid(), priority_text, message);
+		} else {
+			/* log time in ascii format */
+			if(gettimeofday(&tv, NULL) == 0) {
+				struct tm tm;
+				time_t now = (time_t)tv.tv_sec;
+				strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S",
+					localtime_r(&now, &tm));
+			}
+			fprintf(current_log_file, "[%s.%3.3d] %s[%d]: %s: %s",
+				tmbuf, (int)tv.tv_usec/1000,
+				global_ident, (int) getpid(), priority_text, message);
 		}
-		fprintf(current_log_file, "[%s.%3.3d] %s[%d]: %s: %s",
-			tmbuf, (int)tv.tv_usec/1000,
-			global_ident, (int) getpid(), priority_text, message);
  	} else
 #endif /* have time functions */
 		fprintf(current_log_file, "[%d] %s[%d]: %s: %s",
@@ -508,113 +537,6 @@ timespec_subtract(struct timespec *left,
 	}
 }
 
-uint32_t
-strtoserial(const char* nptr, const char** endptr)
-{
-	uint32_t i = 0;
-	uint32_t serial = 0;
-
-	for(*endptr = nptr; **endptr; (*endptr)++) {
-		switch (**endptr) {
-		case ' ':
-		case '\t':
-			break;
-		case '0':
-		case '1':
-		case '2':
-		case '3':
-		case '4':
-		case '5':
-		case '6':
-		case '7':
-		case '8':
-		case '9':
-			if((i*10)/10 != i)
-				/* number too large, return i
-				 * with *endptr != 0 as a failure*/
-				return i;
-			i *= 10;
-			i += (**endptr - '0');
-			break;
-		default:
-			return 0;
-		}
-	}
-	serial += i;
-	return serial;
-}
-
-uint32_t
-strtottl(const char *nptr, const char **endptr)
-{
-	uint32_t i = 0;
-	uint32_t seconds = 0;
-
-	for(*endptr = nptr; **endptr; (*endptr)++) {
-		switch (**endptr) {
-		case ' ':
-		case '\t':
-			break;
-		case 's':
-		case 'S':
-			seconds += i;
-			i = 0;
-			break;
-		case 'm':
-		case 'M':
-			seconds += i * 60;
-			i = 0;
-			break;
-		case 'h':
-		case 'H':
-			seconds += i * 60 * 60;
-			i = 0;
-			break;
-		case 'd':
-		case 'D':
-			seconds += i * 60 * 60 * 24;
-			i = 0;
-			break;
-		case 'w':
-		case 'W':
-			seconds += i * 60 * 60 * 24 * 7;
-			i = 0;
-			break;
-		case '0':
-		case '1':
-		case '2':
-		case '3':
-		case '4':
-		case '5':
-		case '6':
-		case '7':
-		case '8':
-		case '9':
-			i *= 10;
-			i += (**endptr - '0');
-			break;
-		default:
-			seconds += i;
-			/**
-			 * According to RFC2308, Section 8, the MSB
-			 * (sign bit) should be set to zero.
-			 * If we encounter a value larger than 2^31 -1,
-			 * we fall back to the default TTL.
-			 */
-			if ((seconds & MSB_32)) {
-				seconds = DEFAULT_TTL;
-			}
-			return seconds;
-		}
-	}
-	seconds += i;
-	if ((seconds & MSB_32)) {
-		seconds = DEFAULT_TTL;
-	}
-	return seconds;
-}
-
-
 ssize_t
 hex_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize)
 {
@@ -822,53 +744,6 @@ hexdigit_to_int(char ch)
 	}
 }
 
-/* Number of days per month (except for February in leap years). */
-static const int mdays[] = {
-    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-};
-
-static int
-is_leap_year(int year)
-{
-    return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
-}
-
-static int
-leap_days(int y1, int y2)
-{
-    --y1;
-    --y2;
-    return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400);
-}
-
-/*
- * Code adapted from Python 2.4.1 sources (Lib/calendar.py).
- */
-time_t
-mktime_from_utc(const struct tm *tm)
-{
-    int year = 1900 + tm->tm_year;
-    time_t days = 365 * (year - 1970) + leap_days(1970, year);
-    time_t hours;
-    time_t minutes;
-    time_t seconds;
-    int i;
-
-    for (i = 0; i < tm->tm_mon; ++i) {
-        days += mdays[i];
-    }
-    if (tm->tm_mon > 1 && is_leap_year(year)) {
-        ++days;
-    }
-    days += tm->tm_mday - 1;
-
-    hours = days * 24 + tm->tm_hour;
-    minutes = hours * 60 + tm->tm_min;
-    seconds = minutes * 60 + tm->tm_sec;
-
-    return seconds;
-}
-
 /* code to calculate CRC. Lifted from BSD 4.4 crc.c in cksum(1). BSD license.
    http://www.tsfr.org/~orc/Code/bsd/bsd-current/cksum/crc.c.
    or http://gobsd.com/code/freebsd/usr.bin/cksum/crc.c
@@ -1223,7 +1098,7 @@ int number_of_cpus(void)
  * with a linker error, we print an error when it is used */
 int set_cpu_affinity(cpuset_t *ATTR_UNUSED(set))
 {
-	log_err("sched_setaffinity: not available on this system");
+	log_msg(LOG_ERR, "sched_setaffinity: not available on this system");
 	return -1;
 }
 #elif defined(HAVE_SCHED_SETAFFINITY)
@@ -1299,3 +1174,133 @@ void drop_cookie_secret(struct nsd* nsd)
 	              , NSD_COOKIE_SECRET_SIZE);
 	nsd->cookie_count -= 1;
 }
+
+void reconfig_cookies(struct nsd* nsd, struct nsd_options* options)
+{
+	cookie_secret_type cookie_secrets[NSD_COOKIE_HISTORY_SIZE];
+	char secret[NSD_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/];
+	FILE* f = NULL;
+	size_t count = 0;
+	const char* fn;
+	size_t i, j;
+
+	nsd->do_answer_cookie = options->answer_cookie;
+
+	/* Cookie secrets in the configuration file take precedence */
+	if(options->cookie_secret) {
+		ssize_t len = hex_pton(options->cookie_secret,
+				nsd->cookie_secrets[0].cookie_secret,
+				NSD_COOKIE_SECRET_SIZE);
+
+		/* Cookie length guaranteed in configparser.y */
+		assert(len == NSD_COOKIE_SECRET_SIZE);
+		nsd->cookie_count = 1;
+		if(options->cookie_staging_secret) {
+			len = hex_pton(options->cookie_staging_secret,
+					nsd->cookie_secrets[1].cookie_secret,
+					NSD_COOKIE_SECRET_SIZE);
+			/* Cookie length guaranteed in configparser.y */
+			assert(len == NSD_COOKIE_SECRET_SIZE);
+			nsd->cookie_count = 2;
+		}
+		/*************************************************************/
+		nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_CONFIG;
+		return;
+		/*************************************************************/
+	}
+	/* Are cookies from file explicitly disabled? */
+	if(!(fn = nsd->options->cookie_secret_file))
+		goto generate_cookie_secrets;
+
+	else if((f = fopen(fn, "r")) != NULL)
+		; /* pass */
+
+	/* a non-existing cookie file is not necessarily an error */
+	else if(errno != ENOENT) {
+		log_msg( LOG_ERR
+		       , "error reading cookie secret file \"%s\": \"%s\""
+		       , fn, strerror(errno));
+		goto generate_cookie_secrets;
+
+	/* Only at startup cookie_secrets_source == COOKIE_SECRETS_NONE.
+	 * Only then the previous default file location will be tried
+	 * when the current default file location didn't exist.
+	 */
+	} else if(nsd->cookie_secrets_source == COOKIE_SECRETS_NONE
+	       && nsd->options->cookie_secret_file_is_default
+	       && (f = fopen((fn = CONFIGDIR"/nsd_cookiesecrets.txt"),"r")))
+		; /* pass */
+
+	else if(errno != ENOENT) {
+		log_msg( LOG_ERR
+		       , "error reading cookie secret file \"%s\": \"%s\""
+		       , fn, strerror(errno));
+		goto generate_cookie_secrets;
+	} else
+		goto generate_cookie_secrets;
+
+	/* cookie secret file exists and is readable */
+	for( count = 0; count < NSD_COOKIE_HISTORY_SIZE; count++ ) {
+		size_t secret_len = 0;
+		ssize_t decoded_len = 0;
+		if( fgets(secret, sizeof(secret), f) == NULL ) { break; }
+		secret_len = strlen(secret);
+		if( secret_len == 0 ) { break; }
+		assert( secret_len <= sizeof(secret) );
+		secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len;
+		if( secret_len != NSD_COOKIE_SECRET_SIZE * 2 ) {
+			fclose(f);
+			log_msg( LOG_ERR
+			       , "error parsing cookie secret file \"%s\""
+			       , fn);
+			explicit_bzero(cookie_secrets, sizeof(cookie_secrets));
+			explicit_bzero(secret, sizeof(secret));
+			goto generate_cookie_secrets;
+		}
+		/* needed for `hex_pton`; stripping potential `\n` */
+		secret[secret_len] = '\0';
+		decoded_len = hex_pton(secret, cookie_secrets[count].cookie_secret,
+		                       NSD_COOKIE_SECRET_SIZE);
+		if( decoded_len != NSD_COOKIE_SECRET_SIZE ) {
+			fclose(f);
+			log_msg( LOG_ERR
+			       , "error parsing cookie secret file \"%s\""
+			       , fn);
+			explicit_bzero(cookie_secrets, sizeof(cookie_secrets));
+			explicit_bzero(secret, sizeof(secret));
+			goto generate_cookie_secrets;
+		}
+		explicit_bzero(secret, sizeof(secret));
+	}
+	fclose(f);
+	if(count) {
+		nsd->cookie_count = count;
+		memcpy(nsd->cookie_secrets, cookie_secrets, sizeof(cookie_secrets));
+		region_str_replace(  nsd->region
+		                  , &nsd->cookie_secrets_filename, fn );
+		explicit_bzero(cookie_secrets, sizeof(cookie_secrets));
+		/*************************************************************/
+		nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE;
+		return;
+		/*************************************************************/
+	}
+	explicit_bzero(cookie_secrets, sizeof(cookie_secrets));
+
+generate_cookie_secrets:
+	/* Calculate a new random secret */
+	srandom(getpid() ^ time(NULL));
+
+	for( j = 0; j < NSD_COOKIE_HISTORY_SIZE; j++) {
+#if defined(HAVE_SSL)
+		if (!RAND_status()
+		||  !RAND_bytes(nsd->cookie_secrets[j].cookie_secret, NSD_COOKIE_SECRET_SIZE))
+#endif
+		for (i = 0; i < NSD_COOKIE_SECRET_SIZE; i++)
+			nsd->cookie_secrets[j].cookie_secret[i] = random_generate(256);
+	}
+	nsd->cookie_count = 1;
+	/*********************************************************************/
+	nsd->cookie_secrets_source = COOKIE_SECRETS_GENERATED;
+	/*********************************************************************/
+}
+
Index: util.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/util.h,v
diff -u -p -r1.21 util.h
--- util.h	12 Apr 2024 15:53:34 -0000	1.21
+++ util.h	3 Sep 2025 13:53:32 -0000
@@ -18,6 +18,7 @@ struct rr;
 struct buffer;
 struct region;
 struct nsd;
+struct nsd_options;
 
 #ifdef HAVE_SYSLOG_H
 #  include <syslog.h>
@@ -303,6 +304,9 @@ extern int nsd_debug_level;
 /* set to true to log time prettyprinted, or false to print epoch */
 extern int log_time_asc;
 
+/* set to true to log time in iso format */
+extern int log_time_iso;
+
 /*
  * Timespec functions.
  */
@@ -322,25 +326,6 @@ timeval_to_timespec(struct timespec *lef
 void get_time(struct timespec* t);
 
 /*
- * Converts a string representation of a period of time into
- * a long integer of seconds or serial value.
- *
- * Set the endptr to the first illegal character.
- *
- * Interface is similar as strtol(3)
- *
- * Returns:
- *	LONG_MIN if underflow occurs
- *	LONG_MAX if overflow occurs.
- *	otherwise number of seconds
- *
- * XXX These functions do not check the range.
- *
- */
-uint32_t strtoserial(const char *nptr, const char **endptr);
-uint32_t strtottl(const char *nptr, const char **endptr);
-
-/*
  * Convert binary data to a string of hexadecimal characters.
  */
 ssize_t hex_ntop(uint8_t const *src, size_t srclength, char *target,
@@ -366,12 +351,6 @@ void strip_string(char *str);
 int hexdigit_to_int(char ch);
 
 /*
- * Convert TM to seconds since epoch (midnight, January 1st, 1970).
- * Like timegm(3), which is not always available.
- */
-time_t mktime_from_utc(const struct tm *tm);
-
-/*
  * Add bytes to given crc. Returns new CRC sum.
  * Start crc val with 0xffffffff on first call. XOR crc with
  * 0xffffffff at the end again to get final POSIX 1003.2 checksum.
@@ -460,4 +439,7 @@ void activate_cookie_secret(struct nsd* 
 /* Drop a cookie secret. Drops the staging secret. An active secret will not
  * be dropped. */
 void drop_cookie_secret(struct nsd* nsd);
+/* Configure nsd struct with how to respond to DNS Cookies based on options */
+void reconfig_cookies(struct nsd* nsd, struct nsd_options* options);
+
 #endif /* UTIL_H */
Index: xfrd-catalog-zones.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/xfrd-catalog-zones.c,v
diff -u -p -r1.1 xfrd-catalog-zones.c
--- xfrd-catalog-zones.c	12 Apr 2024 15:53:34 -0000	1.1
+++ xfrd-catalog-zones.c	3 Sep 2025 13:53:32 -0000
@@ -1111,8 +1111,8 @@ xfr_writer_init(struct xfrd_xfr_writer* 
 		struct xfrd_catalog_producer_zone* producer_zone)
 {
 	xw->producer_zone = producer_zone;
-	buffer_create_from( &xw->packet, &xw->packet_space
-	                               , sizeof(xw->packet_space));
+	memset(&xw->packet, 0, sizeof(xw->packet));
+	buffer_create_from(&xw->packet, xw->packet_space, sizeof(xw->packet_space));
 	buffer_write(&xw->packet, "\000\000\000\000\000\000"
 	                          "\000\000\000\000\000\000", 12); /* header */
 	xw->seq_nr = 0;
Index: xfrd-notify.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/xfrd-notify.c,v
diff -u -p -r1.4 xfrd-notify.c
--- xfrd-notify.c	17 Sep 2019 16:19:35 -0000	1.4
+++ xfrd-notify.c	3 Sep 2025 13:53:32 -0000
@@ -274,18 +274,20 @@ xfrd_handle_notify_reply(struct notify_z
 static int
 xfrd_notify_send_udp(struct notify_zone* zone, int index)
 {
+	int apex_compress = 0;
 	buffer_type* packet = xfrd_get_temp_buffer();
 	if(!zone->pkts[index].dest) return 0;
 	/* send NOTIFY to secondary. */
 	xfrd_setup_packet(packet, TYPE_SOA, CLASS_IN, zone->apex,
-		qid_generate());
+		qid_generate(), &apex_compress);
 	zone->pkts[index].notify_query_id = ID(packet);
 	OPCODE_SET(packet, OPCODE_NOTIFY);
 	AA_SET(packet);
 	if(zone->current_soa->serial != 0) {
 		/* add current SOA to answer section */
 		ANCOUNT_SET(packet, 1);
-		xfrd_write_soa_buffer(packet, zone->apex, zone->current_soa);
+		xfrd_write_soa_buffer(packet, zone->apex, zone->current_soa,
+			apex_compress);
 	}
 	if(zone->pkts[index].dest->key_options) {
 		xfrd_tsig_sign_request(packet, &zone->notify_tsig, zone->pkts[index].dest);
@@ -318,9 +320,17 @@ xfrd_notify_send_udp(struct notify_zone*
 		int fd;
 		socklen_t to_len = xfrd_acl_sockaddr_to(
 			zone->pkts[index].dest, &to);
-		if(zone->pkts[index].dest->is_ipv6)
+		if(zone->pkts[index].dest->is_ipv6
+		&& zone->notify_send6_handler.ev_fd != -1)
 			fd = zone->notify_send6_handler.ev_fd;
-		else	fd = zone->notify_send_handler.ev_fd;
+		else if (zone->notify_send_handler.ev_fd != -1)
+			fd = zone->notify_send_handler.ev_fd;
+		else {
+			log_msg(LOG_ERR, "xfrd notify: sendto %s failed %s",
+				zone->pkts[index].dest->ip_address_spec,
+				"invalid file descriptor");
+			return 0;
+		}
 		if(sendto(fd,
 			buffer_current(packet), buffer_remaining(packet), 0,
 			(struct sockaddr*)&to, to_len) == -1) {
Index: xfrd-tcp.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/xfrd-tcp.c,v
diff -u -p -r1.30 xfrd-tcp.c
--- xfrd-tcp.c	12 Apr 2024 15:53:34 -0000	1.30
+++ xfrd-tcp.c	3 Sep 2025 13:53:32 -0000
@@ -304,7 +304,7 @@ xfrd_tcp_pipeline_create(region_type* re
 	tp->unused = (uint16_t*)region_alloc_zero(region,
 		sizeof(tp->unused[0])*tp->pipe_num);
 	tp->tcp_r = xfrd_tcp_create(region, QIOBUFSZ);
-	tp->tcp_w = xfrd_tcp_create(region, 512);
+	tp->tcp_w = xfrd_tcp_create(region, QIOBUFSZ);
 	xfrd_tcp_pipeline_init(tp);
 	return tp;
 }
@@ -377,7 +377,8 @@ xfrd_tcp_pipeline_skip_id(struct xfrd_tc
 
 void
 xfrd_setup_packet(buffer_type* packet,
-	uint16_t type, uint16_t klass, const dname_type* dname, uint16_t qid)
+	uint16_t type, uint16_t klass, const dname_type* dname, uint16_t qid,
+	int* apex_compress)
 {
 	/* Set up the header */
 	buffer_clear(packet);
@@ -391,6 +392,8 @@ xfrd_setup_packet(buffer_type* packet,
 	buffer_skip(packet, QHEADERSZ);
 
 	/* The question record. */
+	if(apex_compress)
+		*apex_compress = buffer_position(packet);
 	buffer_write(packet, dname_name(dname), dname->name_size);
 	buffer_write_u16(packet, type);
 	buffer_write_u16(packet, klass);
@@ -469,11 +472,14 @@ xfrd_acl_sockaddr_frm(acl_options_type* 
 
 void
 xfrd_write_soa_buffer(struct buffer* packet,
-	const dname_type* apex, struct xfrd_soa* soa)
+	const dname_type* apex, struct xfrd_soa* soa, int apex_compress)
 {
 	size_t rdlength_pos;
 	uint16_t rdlength;
-	buffer_write(packet, dname_name(apex), apex->name_size);
+	if(apex_compress > 0 && apex_compress < (int)buffer_limit(packet) &&
+		apex->name_size > 1)
+		buffer_write_u16(packet, 0xc000 | apex_compress);
+	else	buffer_write(packet, dname_name(apex), apex->name_size);
 
 	/* already in network order */
 	buffer_write(packet, &soa->type, sizeof(soa->type));
@@ -482,9 +488,28 @@ xfrd_write_soa_buffer(struct buffer* pac
 	rdlength_pos = buffer_position(packet);
 	buffer_skip(packet, sizeof(rdlength));
 
-	/* uncompressed dnames */
-	buffer_write(packet, soa->prim_ns+1, soa->prim_ns[0]);
-	buffer_write(packet, soa->email+1, soa->email[0]);
+	/* compress dnames to apex if possible */
+	if(apex_compress > 0 && apex_compress < (int)buffer_limit(packet) &&
+		apex->name_size > 1 && is_dname_subdomain_of_case(
+		soa->prim_ns+1, soa->prim_ns[0], dname_name(apex),
+		apex->name_size)) {
+		if(soa->prim_ns[0] > apex->name_size)
+			buffer_write(packet, soa->prim_ns+1, soa->prim_ns[0]-
+				apex->name_size);
+		buffer_write_u16(packet, 0xc000 | apex_compress);
+	} else {
+		buffer_write(packet, soa->prim_ns+1, soa->prim_ns[0]);
+	}
+	if(apex_compress > 0 && apex_compress < (int)buffer_limit(packet) &&
+		apex->name_size > 1 && is_dname_subdomain_of_case(soa->email+1,
+		soa->email[0], dname_name(apex), apex->name_size)) {
+		if(soa->email[0] > apex->name_size)
+			buffer_write(packet, soa->email+1, soa->email[0]-
+				apex->name_size);
+		buffer_write_u16(packet, 0xc000 | apex_compress);
+	} else {
+		buffer_write(packet, soa->email+1, soa->email[0]);
+	}
 
 	buffer_write(packet, &soa->serial, sizeof(uint32_t));
 	buffer_write(packet, &soa->refresh, sizeof(uint32_t));
@@ -879,14 +904,6 @@ xfrd_tcp_open(struct xfrd_tcp_set* set, 
 	if (zone->master->tls_auth_options &&
 		zone->master->tls_auth_options->auth_domain_name) {
 #ifdef HAVE_TLS_1_3
-		if (!setup_ssl(tp, set, zone->master->tls_auth_options->auth_domain_name)) {
-			log_msg(LOG_ERR, "xfrd: Cannot setup TLS on pipeline for %s to %s",
-					zone->apex_str, zone->master->ip_address_spec);
-			close(fd);
-			xfrd_set_refresh_now(zone);
-			return 0;
-		}
-
 		/* Load client certificate (if provided) */
 		if (zone->master->tls_auth_options->client_cert &&
 		    zone->master->tls_auth_options->client_key) {
@@ -903,6 +920,29 @@ xfrd_tcp_open(struct xfrd_tcp_set* set, 
 			if (SSL_CTX_use_PrivateKey_file(set->ssl_ctx, zone->master->tls_auth_options->client_key, SSL_FILETYPE_PEM) != 1) {
 				log_msg(LOG_ERR, "xfrd tls: Unable to load private key from file %s", zone->master->tls_auth_options->client_key);
 			}
+
+			if (!SSL_CTX_check_private_key(set->ssl_ctx)) {
+				log_msg(LOG_ERR, "xfrd tls: Client private key from file %s does not match the certificate from file %s",
+				                 zone->master->tls_auth_options->client_key,
+				                 zone->master->tls_auth_options->client_cert);
+			}
+		/* If client certificate/private key loading has failed,
+		   client will not try to authenticate to the server but the connection
+		   will procceed and will be up to the server to allow or deny the
+		   unauthenticated connection. A server that does not enforce authentication
+		   (or a badly configured server?) might allow the transfer.
+		   XXX: Maybe we should close the connection now to make it obvious that
+		   there is something wrong from our side. Alternatively make it obvious
+		   to the operator that we're not being authenticated to the server.
+		*/
+		}
+
+		if (!setup_ssl(tp, set, zone->master->tls_auth_options->auth_domain_name)) {
+			log_msg(LOG_ERR, "xfrd: Cannot setup TLS on pipeline for %s to %s",
+					zone->apex_str, zone->master->ip_address_spec);
+			close(fd);
+			xfrd_set_refresh_now(zone);
+			return 0;
 		}
 
 		tp->handshake_done = 0;
@@ -978,20 +1018,28 @@ xfrd_tcp_setup_write_packet(struct xfrd_
 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "request full zone transfer "
 						"(AXFR) for %s to %s",
 			zone->apex_str, zone->master->ip_address_spec));
+		VERBOSITY(3, (LOG_INFO, "request full zone transfer "
+						"(AXFR) for %s to %s",
+			zone->apex_str, zone->master->ip_address_spec));
 
 		xfrd_setup_packet(tcp->packet, TYPE_AXFR, CLASS_IN, zone->apex,
-			zone->query_id);
+			zone->query_id, NULL);
 		xfrd_prepare_zone_xfr(zone, TYPE_AXFR);
 	} else {
+		int apex_compress = 0;
 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "request incremental zone "
 						"transfer (IXFR) for %s to %s",
 			zone->apex_str, zone->master->ip_address_spec));
+		VERBOSITY(3, (LOG_INFO, "request incremental zone "
+						"transfer (IXFR) for %s to %s",
+			zone->apex_str, zone->master->ip_address_spec));
 
 		xfrd_setup_packet(tcp->packet, TYPE_IXFR, CLASS_IN, zone->apex,
-			zone->query_id);
+			zone->query_id, &apex_compress);
 		xfrd_prepare_zone_xfr(zone, TYPE_IXFR);
 		NSCOUNT_SET(tcp->packet, 1);
-		xfrd_write_soa_buffer(tcp->packet, zone->apex, &zone->soa_disk);
+		xfrd_write_soa_buffer(tcp->packet, zone->apex, &zone->soa_disk,
+			apex_compress);
 	}
 	if(zone->master->key_options && zone->master->key_options->tsig_key) {
 		xfrd_tsig_sign_request(
Index: xfrd-tcp.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/xfrd-tcp.h,v
diff -u -p -r1.4 xfrd-tcp.h
--- xfrd-tcp.h	16 Mar 2022 10:14:51 -0000	1.4
+++ xfrd-tcp.h	3 Sep 2025 13:53:32 -0000
@@ -224,10 +224,11 @@ int conn_write(struct xfrd_tcp* conn);
 
 /* setup DNS packet for a query of this type */
 void xfrd_setup_packet(struct buffer* packet,
-        uint16_t type, uint16_t klass, const struct dname* dname, uint16_t qid);
+        uint16_t type, uint16_t klass, const struct dname* dname, uint16_t qid,
+	int* apex_compress);
 /* write soa in network format to the packet buffer */
 void xfrd_write_soa_buffer(struct buffer* packet,
-        const struct dname* apex, struct xfrd_soa* soa);
+        const struct dname* apex, struct xfrd_soa* soa, int apex_compress);
 /* use acl address to setup sockaddr struct, returns length of addr. */
 socklen_t xfrd_acl_sockaddr_to(struct acl_options* acl,
 #ifdef INET6
Index: xfrd.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/xfrd.c,v
diff -u -p -r1.33 xfrd.c
--- xfrd.c	12 Apr 2024 15:53:34 -0000	1.33
+++ xfrd.c	3 Sep 2025 13:53:32 -0000
@@ -131,6 +131,7 @@ xfrd_init(int socket, struct nsd* nsd, i
 	pid_t nsd_pid)
 {
 	region_type* region;
+	size_t i;
 
 	assert(xfrd == 0);
 	/* to setup signalhandling */
@@ -158,7 +159,6 @@ xfrd_init(int socket, struct nsd* nsd, i
 	xfrd->zonestat_safe = nsd->zonestatdesired;
 #endif
 	xfrd->activated_first = NULL;
-	xfrd->ipc_pass = buffer_create(xfrd->region, QIOBUFSZ);
 	xfrd->last_task = region_alloc(xfrd->region, sizeof(*xfrd->last_task));
 	udb_ptr_init(xfrd->last_task, xfrd->nsd->task[xfrd->nsd->mytask]);
 	assert(shortsoa || udb_base_get_userdata(xfrd->nsd->task[xfrd->nsd->mytask])->data == 0);
@@ -182,10 +182,22 @@ xfrd_init(int socket, struct nsd* nsd, i
 	if(event_add(&xfrd->ipc_handler, NULL) != 0)
 		log_msg(LOG_ERR, "xfrd ipc handler: event_add failed");
 	xfrd->ipc_handler_flags = EV_PERSIST|EV_READ;
-	xfrd->ipc_conn = xfrd_tcp_create(xfrd->region, QIOBUFSZ);
-	/* not reading using ipc_conn yet */
-	xfrd->ipc_conn->is_reading = 0;
-	xfrd->ipc_conn->fd = socket;
+	xfrd->notify_events = (struct event *) region_alloc_array_zero(
+		xfrd->region, nsd->child_count * 2, sizeof(struct event));
+	xfrd->notify_pipes = (struct xfrd_tcp *) region_alloc_array_zero(
+		xfrd->region, nsd->child_count * 2, sizeof(struct xfrd_tcp));
+	for(i = 0; i < 2 * nsd->child_count; i++) {
+		int fd = nsd->serve2xfrd_fd_recv[i];
+		xfrd->notify_pipes[i].fd = fd;
+		xfrd->notify_pipes[i].packet = buffer_create(xfrd->region, QIOBUFSZ);
+		event_set(&xfrd->notify_events[i], fd,
+				EV_PERSIST|EV_READ, xfrd_handle_notify, &xfrd->notify_pipes[i]);
+		if(event_base_set(xfrd->event_base, &xfrd->notify_events[i]) != 0)
+			log_msg( LOG_ERR
+			       , "xfrd notify_event: event_base_set failed");
+		if(event_add(&xfrd->notify_events[i], NULL) != 0)
+			log_msg(LOG_ERR, "xfrd notify_event: event_add failed");
+	}
 	xfrd->need_to_send_reload = 0;
 	xfrd->need_to_send_shutdown = 0;
 	xfrd->need_to_send_stats = 0;
@@ -272,6 +284,9 @@ xfrd_sig_process(void)
 	} else if(xfrd->nsd->signal_hint_reload_hup) {
 		log_msg(LOG_WARNING, "SIGHUP received, reloading...");
 		xfrd->nsd->signal_hint_reload_hup = 0;
+		if(xfrd->nsd->options->reload_config) {
+			xfrd_reload_config(xfrd);
+		}
 		if(xfrd->nsd->options->zonefiles_check) {
 			task_new_check_zonefiles(xfrd->nsd->task[
 				xfrd->nsd->mytask], xfrd->last_task, NULL);
@@ -316,6 +331,7 @@ xfrd_main(void)
 	xfrd->shutdown = 0;
 	while(!xfrd->shutdown)
 	{
+		/* xfrd_sig_process takes care of reading zones on SIGHUP */
 		xfrd_process_catalog_producer_zones();
 		xfrd_process_catalog_consumer_zones();
 		/* process activated zones before blocking in select again */
@@ -636,7 +652,7 @@ apply_xfr:
 		       (long long)xfr->xfrfilenumber, strerror(errno));
 
 	} else if(0 >= apply_ixfr_for_zone(xfrd->nsd, dbzone, df,
-			xfrd->nsd->options, NULL, NULL, xfr->xfrfilenumber)) {
+			xfrd->nsd->options, NULL, xfr->xfrfilenumber)) {
 		make_catalog_consumer_invalid(consumer_zone,
 			"error processing transfer file %lld",
 			(long long)xfr->xfrfilenumber);
@@ -1881,7 +1897,7 @@ xfrd_tsig_sign_request(buffer_type* pack
 static int
 xfrd_send_ixfr_request_udp(xfrd_zone_type* zone)
 {
-	int fd;
+	int fd, apex_compress = 0;
 
 	/* make sure we have a master to query the ixfr request to */
 	assert(zone->master);
@@ -1893,12 +1909,13 @@ xfrd_send_ixfr_request_udp(xfrd_zone_typ
 		return -1;
 	}
 	xfrd_setup_packet(xfrd->packet, TYPE_IXFR, CLASS_IN, zone->apex,
-		qid_generate());
+		qid_generate(), &apex_compress);
 	zone->query_id = ID(xfrd->packet);
 	xfrd_prepare_zone_xfr(zone, TYPE_IXFR);
 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "sent query with ID %d", zone->query_id));
         NSCOUNT_SET(xfrd->packet, 1);
-	xfrd_write_soa_buffer(xfrd->packet, zone->apex, &zone->soa_disk);
+	xfrd_write_soa_buffer(xfrd->packet, zone->apex, &zone->soa_disk,
+		apex_compress);
 	/* if we have tsig keys, sign the ixfr query */
 	if(zone->master->key_options && zone->master->key_options->tsig_key) {
 		xfrd_tsig_sign_request(
@@ -2467,10 +2484,20 @@ xfrd_handle_received_xfr_packet(xfrd_zon
 		zone->latest_xfr->msg_seq_nr,
 		buffer_begin(packet), buffer_limit(packet), xfrd->nsd,
 		zone->latest_xfr->xfrfilenumber);
-	VERBOSITY(3, (LOG_INFO,
-		"xfrd: zone %s written received XFR packet from %s with serial %u to "
-		"disk", zone->apex_str, zone->master->ip_address_spec,
-		(int)zone->latest_xfr->msg_new_serial));
+
+	if(verbosity < 4 || zone->latest_xfr->msg_seq_nr == 0)
+		; /* pass */
+
+	else if((verbosity >= 6)
+	     || (verbosity >= 5 && zone->latest_xfr->msg_seq_nr %  1000 == 0)
+	     || (verbosity >= 4 && zone->latest_xfr->msg_seq_nr % 10000 == 0)) {
+		VERBOSITY(4, (LOG_INFO,
+			"xfrd: zone %s written received XFR packet %u from %s "
+			"with serial %u to disk", zone->apex_str,
+			zone->latest_xfr->msg_seq_nr,
+			zone->master->ip_address_spec,
+			(int)zone->latest_xfr->msg_new_serial));
+	}
 	zone->latest_xfr->msg_seq_nr++;
 
 	xfrfile_size = xfrd_get_xfrfile_size(
Index: xfrd.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/xfrd.h,v
diff -u -p -r1.13 xfrd.h
--- xfrd.h	12 Apr 2024 15:53:34 -0000	1.13
+++ xfrd.h	3 Sep 2025 13:53:32 -0000
@@ -97,8 +97,9 @@ struct xfrd_state {
 	/* communication channel with server_main */
 	struct event ipc_handler;
 	int ipc_handler_flags;
-	struct xfrd_tcp *ipc_conn;
-	struct buffer* ipc_pass;
+	/* 2 * nsd->child_count communication channels from the serve childs */
+	struct event    *notify_events;
+	struct xfrd_tcp *notify_pipes;
 	/* sending ipc to server_main */
 	uint8_t need_to_send_shutdown;
 	uint8_t need_to_send_reload;
Index: zonec.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/zonec.c,v
diff -u -p -r1.31 zonec.c
--- zonec.c	29 Jun 2023 19:38:50 -0000	1.31
+++ zonec.c	3 Sep 2025 13:53:32 -0000
@@ -9,6 +9,7 @@
 
 #include "config.h"
 
+#include <inttypes.h>
 #include <assert.h>
 #include <fcntl.h>
 #include <ctype.h>
@@ -21,7 +22,6 @@
 #endif
 #include <unistd.h>
 #include <stdlib.h>
-#include <time.h>
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
@@ -40,1819 +40,9 @@
 #include "rdata.h"
 #include "region-allocator.h"
 #include "util.h"
-#include "zparser.h"
 #include "options.h"
 #include "nsec3.h"
-
-#define ILNP_MAXDIGITS 4
-#define ILNP_NUMGROUPS 4
-#define SVCB_MAX_COMMA_SEPARATED_VALUES 1000
-
-
-const dname_type *error_dname;
-domain_type *error_domain;
-
-static time_t startzonec = 0;
-static long int totalrrs = 0;
-
-extern uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE];
-extern uint16_t nsec_highest_rcode;
-
-
-/*
- * Allocate SIZE+sizeof(uint16_t) bytes and store SIZE in the first
- * element.  Return a pointer to the allocation.
- */
-static uint16_t *
-alloc_rdata(region_type *region, size_t size)
-{
-	uint16_t *result = region_alloc(region, sizeof(uint16_t) + size);
-	*result = size;
-	return result;
-}
-
-uint16_t *
-alloc_rdata_init(region_type *region, const void *data, size_t size)
-{
-	uint16_t *result = region_alloc(region, sizeof(uint16_t) + size);
-	*result = size;
-	memcpy(result + 1, data, size);
-	return result;
-}
-
-/*
- * These are parser function for generic zone file stuff.
- */
-uint16_t *
-zparser_conv_hex(region_type *region, const char *hex, size_t len)
-{
-	/* convert a hex value to wireformat */
-	uint16_t *r = NULL;
-	uint8_t *t;
-	int i;
-
-	if(len == 1 && hex[0] == '0') {
-		/* single 0 represents empty buffer */
-		return alloc_rdata(region, 0);
-	}
-	if (len % 2 != 0) {
-		zc_error_prev_line("number of hex digits must be a multiple of 2");
-	} else if (len > MAX_RDLENGTH * 2) {
-		zc_error_prev_line("hex data exceeds maximum rdata length (%d)",
-				   MAX_RDLENGTH);
-	} else {
-		/* the length part */
-		r = alloc_rdata(region, len/2);
-		t = (uint8_t *)(r + 1);
-
-		/* Now process octet by octet... */
-		while (*hex) {
-			*t = 0;
-			for (i = 16; i >= 1; i -= 15) {
-				if (isxdigit((unsigned char)*hex)) {
-					*t += hexdigit_to_int(*hex) * i;
-				} else {
-					zc_error_prev_line(
-						"illegal hex character '%c'",
-						(int) *hex);
-					return NULL;
-				}
-				++hex;
-			}
-			++t;
-		}
-	}
-	return r;
-}
-
-/* convert hex, precede by a 1-byte length */
-uint16_t *
-zparser_conv_hex_length(region_type *region, const char *hex, size_t len)
-{
-	uint16_t *r = NULL;
-	uint8_t *t;
-	int i;
-	if (len % 2 != 0) {
-		zc_error_prev_line("number of hex digits must be a multiple of 2");
-	} else if (len > 255 * 2) {
-		zc_error_prev_line("hex data exceeds 255 bytes");
-	} else {
-		uint8_t *l;
-
-		/* the length part */
-		r = alloc_rdata(region, len/2+1);
-		t = (uint8_t *)(r + 1);
-
-		l = t++;
-		*l = '\0';
-
-		/* Now process octet by octet... */
-		while (*hex) {
-			*t = 0;
-			for (i = 16; i >= 1; i -= 15) {
-				if (isxdigit((unsigned char)*hex)) {
-					*t += hexdigit_to_int(*hex) * i;
-				} else {
-					zc_error_prev_line(
-						"illegal hex character '%c'",
-						(int) *hex);
-					return NULL;
-				}
-				++hex;
-			}
-			++t;
-			++*l;
-		}
-	}
-	return r;
-}
-
-uint16_t *
-zparser_conv_time(region_type *region, const char *time)
-{
-	/* convert a time YYHM to wireformat */
-	uint16_t *r = NULL;
-	struct tm tm;
-
-	/* Try to scan the time... */
-	if (!strptime(time, "%Y%m%d%H%M%S", &tm)) {
-		zc_error_prev_line("date and time is expected");
-	} else {
-		uint32_t l = htonl(mktime_from_utc(&tm));
-		r = alloc_rdata_init(region, &l, sizeof(l));
-	}
-	return r;
-}
-
-uint16_t *
-zparser_conv_services(region_type *region, const char *protostr,
-		      char *servicestr)
-{
-	/*
-	 * Convert a protocol and a list of service port numbers
-	 * (separated by spaces) in the rdata to wireformat
-	 */
-	uint16_t *r = NULL;
-	uint8_t *p;
-	uint8_t bitmap[65536/8];
-	char sep[] = " ";
-	char *word;
-	int max_port = -8;
-	/* convert a protocol in the rdata to wireformat */
-	struct protoent *proto;
-
-	memset(bitmap, 0, sizeof(bitmap));
-
-	proto = getprotobyname(protostr);
-	if (!proto) {
-		proto = getprotobynumber(atoi(protostr));
-	}
-	if (!proto) {
-		zc_error_prev_line("unknown protocol '%s'", protostr);
-		return NULL;
-	}
-
-	for (word = strtok(servicestr, sep); word; word = strtok(NULL, sep)) {
-		struct servent *service;
-		int port;
-
-		service = getservbyname(word, proto->p_name);
-		if (service) {
-			/* Note: ntohs not ntohl!  Strange but true.  */
-			port = ntohs((uint16_t) service->s_port);
-		} else {
-			char *end;
-			port = strtol(word, &end, 10);
-			if (*end != '\0') {
-				zc_error_prev_line("unknown service '%s' for protocol '%s'",
-						   word, protostr);
-				continue;
-			}
-		}
-
-		if (port < 0 || port > 65535) {
-			zc_error_prev_line("bad port number %d", port);
-		} else {
-			set_bit(bitmap, port);
-			if (port > max_port)
-				max_port = port;
-		}
-	}
-
-	r = alloc_rdata(region, sizeof(uint8_t) + max_port / 8 + 1);
-	p = (uint8_t *) (r + 1);
-	*p = proto->p_proto;
-	memcpy(p + 1, bitmap, *r-1);
-
-	return r;
-}
-
-uint16_t *
-zparser_conv_serial(region_type *region, const char *serialstr)
-{
-	uint16_t *r = NULL;
-	uint32_t serial;
-	const char *t;
-
-	serial = strtoserial(serialstr, &t);
-	if (*t != '\0') {
-		zc_error_prev_line("serial is expected or serial too big");
-	} else {
-		serial = htonl(serial);
-		r = alloc_rdata_init(region, &serial, sizeof(serial));
-	}
-	return r;
-}
-
-uint16_t *
-zparser_conv_period(region_type *region, const char *periodstr)
-{
-	/* convert a time period (think TTL's) to wireformat) */
-	uint16_t *r = NULL;
-	uint32_t period;
-	const char *end;
-
-	/* Allocate required space... */
-	period = strtottl(periodstr, &end);
-	if (*end != '\0') {
-		zc_error_prev_line("time period is expected");
-	} else {
-		period = htonl(period);
-		r = alloc_rdata_init(region, &period, sizeof(period));
-	}
-	return r;
-}
-
-uint16_t *
-zparser_conv_short(region_type *region, const char *text)
-{
-	uint16_t *r = NULL;
-	uint16_t value;
-	char *end;
-
-	value = htons((uint16_t) strtol(text, &end, 10));
-	if (*end != '\0') {
-		zc_error_prev_line("integer value is expected");
-	} else {
-		r = alloc_rdata_init(region, &value, sizeof(value));
-	}
-	return r;
-}
-
-uint16_t *
-zparser_conv_byte(region_type *region, const char *text)
-{
-	uint16_t *r = NULL;
-	uint8_t value;
-	char *end;
-
-	value = (uint8_t) strtol(text, &end, 10);
-	if (*end != '\0') {
-		zc_error_prev_line("integer value is expected");
-	} else {
-		r = alloc_rdata_init(region, &value, sizeof(value));
-	}
-	return r;
-}
-
-uint16_t *
-zparser_conv_algorithm(region_type *region, const char *text)
-{
-	const lookup_table_type *alg;
-	uint8_t id;
-
-	alg = lookup_by_name(dns_algorithms, text);
-	if (alg) {
-		id = (uint8_t) alg->id;
-	} else {
-		char *end;
-		id = (uint8_t) strtol(text, &end, 10);
-		if (*end != '\0') {
-			zc_error_prev_line("algorithm is expected");
-			return NULL;
-		}
-	}
-
-	return alloc_rdata_init(region, &id, sizeof(id));
-}
-
-uint16_t *
-zparser_conv_certificate_type(region_type *region, const char *text)
-{
-	/* convert an algorithm string to integer */
-	const lookup_table_type *type;
-	uint16_t id;
-
-	type = lookup_by_name(dns_certificate_types, text);
-	if (type) {
-		id = htons((uint16_t) type->id);
-	} else {
-		char *end;
-		id = htons((uint16_t) strtol(text, &end, 10));
-		if (*end != '\0') {
-			zc_error_prev_line("certificate type is expected");
-			return NULL;
-		}
-	}
-
-	return alloc_rdata_init(region, &id, sizeof(id));
-}
-
-uint16_t *
-zparser_conv_a(region_type *region, const char *text)
-{
-	in_addr_t address;
-	uint16_t *r = NULL;
-
-	if (inet_pton(AF_INET, text, &address) != 1) {
-		zc_error_prev_line("invalid IPv4 address '%s'", text);
-	} else {
-		r = alloc_rdata_init(region, &address, sizeof(address));
-	}
-	return r;
-}
-
-uint16_t *
-zparser_conv_aaaa(region_type *region, const char *text)
-{
-	uint8_t address[IP6ADDRLEN];
-	uint16_t *r = NULL;
-
-	if (inet_pton(AF_INET6, text, address) != 1) {
-		zc_error_prev_line("invalid IPv6 address '%s'", text);
-	} else {
-		r = alloc_rdata_init(region, address, sizeof(address));
-	}
-	return r;
-}
-
-
-uint16_t *
-zparser_conv_ilnp64(region_type *region, const char *text)
-{
-	uint16_t *r = NULL;
-	int ngroups, num;
-	unsigned long hex;
-	const char *ch;
-	char digits[ILNP_MAXDIGITS+1];
-	unsigned int ui[ILNP_NUMGROUPS];
-	uint16_t a[ILNP_NUMGROUPS];
-
-	ngroups = 1; /* Always at least one group */
-	num = 0;
-	for (ch = text; *ch != '\0'; ch++) {
-		if (*ch == ':') {
-			if (num <= 0) {
-				zc_error_prev_line("ilnp64: empty group of "
-					"digits is not allowed");
-				return NULL;
-			}
-			digits[num] = '\0';
-			hex = (unsigned long) strtol(digits, NULL, 16);
-			num = 0;
-			ui[ngroups - 1] = hex;
-			if (ngroups >= ILNP_NUMGROUPS) {
-				zc_error_prev_line("ilnp64: more than %d groups "
-					"of digits", ILNP_NUMGROUPS);
-				return NULL;
-			}
-			ngroups++;
-		} else {
-			/* Our grammar is stricter than the one accepted by
-			 * strtol. */
-			if (!isxdigit((unsigned char)*ch)) {
-				zc_error_prev_line("ilnp64: invalid "
-					"(non-hexadecimal) character %c", *ch);
-				return NULL;
-			}
-			if (num >= ILNP_MAXDIGITS) {
-				zc_error_prev_line("ilnp64: more than %d digits "
-					"in a group", ILNP_MAXDIGITS);
-				return NULL;
-			}
-			digits[num++] = *ch;
-		}
-	}
-	if (num <= 0) {
-		zc_error_prev_line("ilnp64: empty group of digits is not "
-			"allowed");
-		return NULL;
-	}
-	digits[num] = '\0';
-	hex = (unsigned long) strtol(digits, NULL, 16);
-	ui[ngroups - 1] = hex;
-	if (ngroups < 4) {
-		zc_error_prev_line("ilnp64: less than %d groups of digits",
-			ILNP_NUMGROUPS);
-		return NULL;
-	}
-
-	a[0] = htons(ui[0]);
-	a[1] = htons(ui[1]);
-	a[2] = htons(ui[2]);
-	a[3] = htons(ui[3]);
-	r = alloc_rdata_init(region, a, sizeof(a));
-	return r;
-}
-
-static uint16_t *
-zparser_conv_eui48(region_type *region, const char *text)
-{
-	uint8_t nums[6];
-	uint16_t *r = NULL;
-	unsigned int a, b, c, d, e, f;
-	int l;
-
-	if (sscanf(text, "%2x-%2x-%2x-%2x-%2x-%2x%n",
-		&a, &b, &c, &d, &e, &f, &l) != 6 ||
-		l != (int)strlen(text)){
-		zc_error_prev_line("eui48: invalid rr");
-		return NULL;
-	}
-	nums[0] = (uint8_t)a;
-	nums[1] = (uint8_t)b;
-	nums[2] = (uint8_t)c;
-	nums[3] = (uint8_t)d;
-	nums[4] = (uint8_t)e;
-	nums[5] = (uint8_t)f;
-	r = alloc_rdata_init(region, nums, sizeof(nums));
-	return r;
-}
-
-static uint16_t *
-zparser_conv_eui64(region_type *region, const char *text)
-{
-	uint8_t nums[8];
-	uint16_t *r = NULL;
-	unsigned int a, b, c, d, e, f, g, h;
-	int l;
-	if (sscanf(text, "%2x-%2x-%2x-%2x-%2x-%2x-%2x-%2x%n",
-		&a, &b, &c, &d, &e, &f, &g, &h, &l) != 8 ||
-		l != (int)strlen(text)) {
-		zc_error_prev_line("eui64: invalid rr");
-		return NULL;
-	}
-	nums[0] = (uint8_t)a;
-	nums[1] = (uint8_t)b;
-	nums[2] = (uint8_t)c;
-	nums[3] = (uint8_t)d;
-	nums[4] = (uint8_t)e;
-	nums[5] = (uint8_t)f;
-	nums[6] = (uint8_t)g;
-	nums[7] = (uint8_t)h;
-	r = alloc_rdata_init(region, nums, sizeof(nums));
-	return r;
-}
-
-uint16_t *
-zparser_conv_eui(region_type *region, const char *text, size_t len)
-{
-	uint16_t *r = NULL;
-	int nnum, num;
-	const char* ch;
-
-	nnum = len/8;
-	num = 1;
-	for (ch = text; *ch != '\0'; ch++) {
-		if (*ch == '-') {
-			num++;
-		} else if (!isxdigit((unsigned char)*ch)) {
-			zc_error_prev_line("eui%u: invalid (non-hexadecimal) "
-				"character %c", (unsigned) len, *ch);
-			return NULL;
-		}
-	}
-	if (num != nnum) {
-		zc_error_prev_line("eui%u: wrong number of hex numbers",
-			(unsigned) len);
-		return NULL;
-	}
-
-	switch (len) {
-		case 48:
-			r = zparser_conv_eui48(region, text);
-			break;
-		case 64:
-			r = zparser_conv_eui64(region, text);
-		break;
-		default:
-			zc_error_prev_line("eui%u: invalid length",
-				(unsigned) len);
-			return NULL;
-			break;
-	}
-	return r;
-}
-
-uint16_t *
-zparser_conv_text(region_type *region, const char *text, size_t len)
-{
-	uint16_t *r = NULL;
-	uint8_t *p;
-
-	if (len > 255) {
-		zc_error_prev_line("text string is longer than 255 characters,"
-				   " try splitting it into multiple parts");
-		len = 255;
-	}
-	r = alloc_rdata(region, len + 1);
-	p = (uint8_t *) (r + 1);
-	*p = len;
-	memcpy(p + 1, text, len);
-	return r;
-}
-
-/* for CAA Value [RFC 6844] */
-uint16_t *
-zparser_conv_long_text(region_type *region, const char *text, size_t len)
-{
-	uint16_t *r = NULL;
-	if (len > MAX_RDLENGTH) {
-		zc_error_prev_line("text string is longer than max rdlen");
-		return NULL;
-	}
-	r = alloc_rdata_init(region, text, len);
-	return r;
-}
-
-/* for CAA Tag [RFC 6844] */
-uint16_t *
-zparser_conv_tag(region_type *region, const char *text, size_t len)
-{
-	uint16_t *r = NULL;
-	uint8_t *p;
-	const char* ptr;
-
-	if (len < 1) {
-		zc_error_prev_line("invalid tag: zero length");
-		return NULL;
-	}
-	if (len > 15) {
-		zc_error_prev_line("invalid tag %s: longer than 15 characters (%u)",
-			text, (unsigned) len);
-		return NULL;
-	}
-	for (ptr = text; *ptr; ptr++) {
-		if (!isdigit((unsigned char)*ptr) && !islower((unsigned char)*ptr)) {
-			zc_error_prev_line("invalid tag %s: contains invalid char %c",
-				text, *ptr);
-			return NULL;
-		}
-	}
-	r = alloc_rdata(region, len + 1);
-	p = (uint8_t *) (r + 1);
-	*p = len;
-	memmove(p + 1, text, len);
-	return r;
-}
-
-uint16_t *
-zparser_conv_dns_name(region_type *region, const uint8_t* name, size_t len)
-{
-	uint16_t* r = NULL;
-	uint8_t* p = NULL;
-	r = alloc_rdata(region, len);
-	p = (uint8_t *) (r + 1);
-	memcpy(p, name, len);
-
-	return r;
-}
-
-uint16_t *
-zparser_conv_b32(region_type *region, const char *b32)
-{
-	uint8_t buffer[B64BUFSIZE];
-	uint16_t *r = NULL;
-	int i;
-
-	if(strcmp(b32, "-") == 0) {
-		return alloc_rdata_init(region, "", 1);
-	}
-	i = b32_pton(b32, buffer+1, B64BUFSIZE-1);
-	if (i == -1 || i > 255) {
-		zc_error_prev_line("invalid base32 data");
-	} else {
-		buffer[0] = i; /* store length byte */
-		r = alloc_rdata_init(region, buffer, i+1);
-	}
-	return r;
-}
-
-uint16_t *
-zparser_conv_b64(region_type *region, const char *b64)
-{
-	uint8_t buffer[B64BUFSIZE];
-	uint16_t *r = NULL;
-	int i;
-
-	if(strcmp(b64, "0") == 0) {
-		/* single 0 represents empty buffer */
-		return alloc_rdata(region, 0);
-	}
-	i = __b64_pton(b64, buffer, B64BUFSIZE);
-	if (i == -1) {
-		zc_error_prev_line("invalid base64 data");
-	} else {
-		r = alloc_rdata_init(region, buffer, i);
-	}
-	return r;
-}
-
-uint16_t *
-zparser_conv_rrtype(region_type *region, const char *text)
-{
-	uint16_t *r = NULL;
-	uint16_t type = rrtype_from_string(text);
-
-	if (type == 0) {
-		zc_error_prev_line("unrecognized RR type '%s'", text);
-	} else {
-		type = htons(type);
-		r = alloc_rdata_init(region, &type, sizeof(type));
-	}
-	return r;
-}
-
-uint16_t *
-zparser_conv_nxt(region_type *region, uint8_t nxtbits[])
-{
-	/* nxtbits[] consists of 16 bytes with some zero's in it
-	 * copy every byte with zero to r and write the length in
-	 * the first byte
-	 */
-	uint16_t i;
-	uint16_t last = 0;
-
-	for (i = 0; i < 16; i++) {
-		if (nxtbits[i] != 0)
-			last = i + 1;
-	}
-
-	return alloc_rdata_init(region, nxtbits, last);
-}
-
-
-/* we potentially have 256 windows, each one is numbered. empty ones
- * should be discarded
- */
-uint16_t *
-zparser_conv_nsec(region_type *region,
-		  uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE])
-{
-	/* nsecbits contains up to 64K of bits which represent the
-	 * types available for a name. Walk the bits according to
-	 * nsec++ draft from jakob
-	 */
-	uint16_t *r;
-	uint8_t *ptr;
-	size_t i,j;
-	uint16_t window_count = 0;
-	uint16_t total_size = 0;
-	uint16_t window_max = 0;
-
-	/* The used windows.  */
-	int used[NSEC_WINDOW_COUNT];
-	/* The last byte used in each the window.  */
-	int size[NSEC_WINDOW_COUNT];
-
-	window_max = 1 + (nsec_highest_rcode / 256);
-
-	/* used[i] is the i-th window included in the nsec
-	 * size[used[0]] is the size of window 0
-	 */
-
-	/* walk through the 256 windows */
-	for (i = 0; i < window_max; ++i) {
-		int empty_window = 1;
-		/* check each of the 32 bytes */
-		for (j = 0; j < NSEC_WINDOW_BITS_SIZE; ++j) {
-			if (nsecbits[i][j] != 0) {
-				size[i] = j + 1;
-				empty_window = 0;
-			}
-		}
-		if (!empty_window) {
-			used[window_count] = i;
-			window_count++;
-		}
-	}
-
-	for (i = 0; i < window_count; ++i) {
-		total_size += sizeof(uint16_t) + size[used[i]];
-	}
-
-	r = alloc_rdata(region, total_size);
-	ptr = (uint8_t *) (r + 1);
-
-	/* now walk used and copy it */
-	for (i = 0; i < window_count; ++i) {
-		ptr[0] = used[i];
-		ptr[1] = size[used[i]];
-		memcpy(ptr + 2, &nsecbits[used[i]], size[used[i]]);
-		ptr += size[used[i]] + 2;
-	}
-
-	return r;
-}
-
-static uint16_t
-svcbparam_lookup_key(const char *key, size_t key_len)
-{
-	char buf[64];
-	char *endptr;
-	unsigned long int key_value;
-
-	if (key_len >= 4  && key_len <= 8 && !strncmp(key, "key", 3)) {
-		memcpy(buf, key + 3, key_len - 3);
-		buf[key_len - 3] = 0;
-		key_value = strtoul(buf, &endptr, 10);
-		if (endptr > buf	/* digits seen */
-		&& *endptr == 0		/* no non-digit chars after digits */
-		&&  key_value <= 65535)	/* no overflow */
-			return key_value;
-
-	} else switch (key_len) {
-	case sizeof("mandatory")-1:
-		if (!strncmp(key, "mandatory", sizeof("mandatory")-1))
-			return SVCB_KEY_MANDATORY;
-		if (!strncmp(key, "echconfig", sizeof("echconfig")-1))
-			return SVCB_KEY_ECH; /* allow "echconfig" as well as "ech" */
-		break;
-
-	case sizeof("alpn")-1:
-		if (!strncmp(key, "alpn", sizeof("alpn")-1))
-			return SVCB_KEY_ALPN;
-		if (!strncmp(key, "port", sizeof("port")-1))
-			return SVCB_KEY_PORT;
-		break;
-
-	case sizeof("no-default-alpn")-1:
-		if (!strncmp( key  , "no-default-alpn"
-		            , sizeof("no-default-alpn")-1))
-			return SVCB_KEY_NO_DEFAULT_ALPN;
-		break;
-
-	case sizeof("ipv4hint")-1:
-		if (!strncmp(key, "ipv4hint", sizeof("ipv4hint")-1))
-			return SVCB_KEY_IPV4HINT;
-		if (!strncmp(key, "ipv6hint", sizeof("ipv6hint")-1))
-			return SVCB_KEY_IPV6HINT;
-		break;
-	case sizeof("dohpath")-1:
-		if (!strncmp(key, "dohpath", sizeof("dohpath")-1))
-			return SVCB_KEY_DOHPATH;
-		break;
-	case sizeof("ech")-1:
-		if (!strncmp(key, "ech", sizeof("ech")-1))
-			return SVCB_KEY_ECH;
-		break;
-	default:
-		break;
-	}
-	if (key_len > sizeof(buf) - 1)
-		zc_error_prev_line("Unknown SvcParamKey");
-	else {
-		memcpy(buf, key, key_len);
-		buf[key_len] = 0;
-		zc_error_prev_line("Unknown SvcParamKey: %s", buf);
-	}
-	/* Although the returned value might be used by the caller,
-	 * the parser has erred, so the zone will not be loaded.
-	 */
-	return -1;
-}
-
-static uint16_t *
-zparser_conv_svcbparam_port_value(region_type *region, const char *val)
-{
-	unsigned long int port;
-	char *endptr;
-	uint16_t *r;
-
-	port = strtoul(val, &endptr, 10);
-	if (endptr > val	/* digits seen */
-	&& *endptr == 0		/* no non-digit chars after digits */
-	&&  port <= 65535) {	/* no overflow */
-
-		r = alloc_rdata(region, 3 * sizeof(uint16_t));
-		r[1] = htons(SVCB_KEY_PORT);
-		r[2] = htons(sizeof(uint16_t));
-		r[3] = htons(port);
-		return r;
-	}
-	zc_error_prev_line("Could not parse port SvcParamValue: \"%s\"", val);
-	return NULL;
-}
-
-static uint16_t *
-zparser_conv_svcbparam_ipv4hint_value(region_type *region, const char *val)
-{
-	uint16_t *r;
-	int count;
-	char ip_str[INET_ADDRSTRLEN+1];
-	char *next_ip_str;
-	uint32_t *ip_wire_dst;
-	size_t i;
-
-	for (i = 0, count = 1; val[i]; i++) {
-		if (val[i] == ',')
-			count += 1;
-		if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
-			zc_error_prev_line("Too many IPV4 addresses in ipv4hint");
-			return NULL;
-		}
-	}
-
-	/* count == number of comma's in val + 1, so the actual number of IPv4
-	 * addresses in val
-	 */
-	r = alloc_rdata(region, 2 * sizeof(uint16_t) + IP4ADDRLEN * count);
-	r[1] = htons(SVCB_KEY_IPV4HINT);
-	r[2] = htons(IP4ADDRLEN * count);
-	ip_wire_dst = (void *)&r[3];
-
-	while (count) {
-		if (!(next_ip_str = strchr(val, ','))) {
-			if (inet_pton(AF_INET, val, ip_wire_dst) != 1)
-				break;
-
-			assert(count == 1);
-
-		} else if (next_ip_str - val >= (int)sizeof(ip_str))
-			break;
-
-		else {
-			memcpy(ip_str, val, next_ip_str - val);
-			ip_str[next_ip_str - val] = 0;
-			if (inet_pton(AF_INET, ip_str, ip_wire_dst) != 1) {
-				val = ip_str; /* to use in error reporting below */
-				break;
-			}
-
-			val = next_ip_str + 1;
-		}
-		ip_wire_dst++;
-		count--;
-	}
-	if (count)
-		zc_error_prev_line("Could not parse ipv4hint SvcParamValue: %s", val);
-
-	return r;
-}
-
-static uint16_t *
-zparser_conv_svcbparam_ipv6hint_value(region_type *region, const char *val)
-{
-	uint16_t *r;
-	int i, count;
-	char ip6_str[INET6_ADDRSTRLEN+1];
-	char *next_ip6_str;
-	uint8_t *ipv6_wire_dst;
-
-	for (i = 0, count = 1; val[i]; i++) {
-		if (val[i] == ',')
-			count += 1;
-		if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
-			zc_error_prev_line("Too many IPV6 addresses in ipv6hint");
-			return NULL;
-		}
-	}
-
-	/* count == number of comma's in val + 1 
-	 * so actually the number of IPv6 addresses in val
-	 */
-	r = alloc_rdata(region, 2 * sizeof(uint16_t) + IP6ADDRLEN * count);
-	r[1] = htons(SVCB_KEY_IPV6HINT);
-	r[2] = htons(IP6ADDRLEN * count);
-	ipv6_wire_dst = (void *)&r[3];
-
-	while (count) {
-		if (!(next_ip6_str = strchr(val, ','))) {
-			if ((inet_pton(AF_INET6, val, ipv6_wire_dst) != 1))
-				break;
-
-			assert(count == 1);
-
-		} else if (next_ip6_str - val >= (int)sizeof(ip6_str))
-			break;
-
-		else {
-			memcpy(ip6_str, val, next_ip6_str - val);
-			ip6_str[next_ip6_str - val] = 0;
-			if (inet_pton(AF_INET6, ip6_str, ipv6_wire_dst) != 1) {
-				val = ip6_str; /* for error reporting below */
-				break;
-			}
-
-			val = next_ip6_str + 1; /* skip the comma */
-		}
-		ipv6_wire_dst += IP6ADDRLEN;
-		count--;
-	}
-	if (count)
-		zc_error_prev_line("Could not parse ipv6hint SvcParamValue: %s", val);
-
-	return r;
-}
-
-static int
-network_uint16_cmp(const void *a, const void *b)
-{
-	return ((int)read_uint16(a)) - ((int)read_uint16(b));
-}
-
-static uint16_t *
-zparser_conv_svcbparam_mandatory_value(region_type *region,
-		const char *val, size_t val_len)
-{
-	uint16_t *r;
-	size_t i, count;
-	char* next_key;
-	uint16_t* key_dst;
-
-	for (i = 0, count = 1; val[i]; i++) {
-		if (val[i] == ',')
-			count += 1;
-		if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
-			zc_error_prev_line("Too many keys in mandatory");
-			return NULL;
-		}
-	}
-
-	r = alloc_rdata(region, (2 + count) * sizeof(uint16_t));
-	r[1] = htons(SVCB_KEY_MANDATORY);
-	r[2] = htons(sizeof(uint16_t) * count);
-	key_dst = (void *)&r[3];
-
-	for(;;) {
-		if (!(next_key = strchr(val, ','))) {
-			*key_dst = htons(svcbparam_lookup_key(val, val_len));
-			break;	
-		} else {
-			*key_dst = htons(svcbparam_lookup_key(val, next_key - val));
-		}
-
-		val_len -= next_key - val + 1;
-		val = next_key + 1; /* skip the comma */
-		key_dst += 1;
-	}
-
-	/* In draft-ietf-dnsop-svcb-https-04 Section 7:
-	 *
-	 *     In wire format, the keys are represented by their numeric
-	 *     values in network byte order, concatenated in ascending order.
-	 */
-	qsort((void *)&r[3], count, sizeof(uint16_t), network_uint16_cmp);
-
-	return r;
-}
-
-static uint16_t *
-zparser_conv_svcbparam_ech_value(region_type *region, const char *b64)
-{
-	uint8_t buffer[B64BUFSIZE];
-	uint16_t *r = NULL;
-	int wire_len;
-
-	if(strcmp(b64, "0") == 0) {
-		/* single 0 represents empty buffer */
-		return alloc_rdata(region, 0);
-	}
-	wire_len = __b64_pton(b64, buffer, B64BUFSIZE);
-	if (wire_len == -1) {
-		zc_error_prev_line("invalid base64 data in ech");
-	} else {
-		r = alloc_rdata(region, 2 * sizeof(uint16_t) + wire_len);
-		r[1] = htons(SVCB_KEY_ECH);
-		r[2] = htons(wire_len);
-		memcpy(&r[3], buffer, wire_len);
-	}
-
-	return r;
-}
-
-static const char* parse_alpn_next_unescaped_comma(const char *val)
-{
-	while (*val) {
-		/* Only return when the comma is not escaped*/
-		if (*val == '\\'){
-			++val;
-			if (!*val)
-				break;
-		} else if (*val == ',')
-				return val;
-
-		val++;
-	}
-	return NULL;
-}
-
-static size_t
-parse_alpn_copy_unescaped(uint8_t *dst, const char *src, size_t len)
-{
-	uint8_t *orig_dst = dst;
-
-	while (len) {
-		if (*src == '\\') {
-			src++;
-			len--;
-			if (!len)
-				break;
-		}
-		*dst++ = *src++;
-		len--;
-	}
-	return (size_t)(dst - orig_dst);
-}
-
-static uint16_t *
-zparser_conv_svcbparam_alpn_value(region_type *region,
-		const char *val, size_t val_len)
-{
-	uint8_t     unescaped_dst[65536];
-	uint8_t    *dst = unescaped_dst;
-	const char *next_str;
-	size_t      str_len;
-	size_t      dst_len;
-	uint16_t   *r = NULL;
-
-	if (val_len > sizeof(unescaped_dst)) {
-		zc_error_prev_line("invalid alpn");
-		return r;
-	}
-	while (val_len) {
-		size_t dst_len;
-
-		str_len = (next_str = parse_alpn_next_unescaped_comma(val))
-		        ? (size_t)(next_str - val) : val_len;
-
-		if (str_len > 255) {
-			zc_error_prev_line("alpn strings need to be"
-					   " smaller than 255 chars");
-			return r;
-		}
-		dst_len = parse_alpn_copy_unescaped(dst + 1, val, str_len);
-		*dst++ = dst_len;
-		 dst  += dst_len;
-
-		if (!next_str)
-			break;
-
-		/* skip the comma for the next iteration */
-		val_len -= next_str - val + 1;
-		val = next_str + 1;
-	}
-	dst_len = dst - unescaped_dst;
-	r = alloc_rdata(region, 2 * sizeof(uint16_t) + dst_len);
-	r[1] = htons(SVCB_KEY_ALPN);
-	r[2] = htons(dst_len);
-	memcpy(&r[3], unescaped_dst, dst_len);
-	return r;
-}
-
-static uint16_t *
-zparser_conv_svcbparam_key_value(region_type *region,
-    const char *key, size_t key_len, const char *val, size_t val_len)
-{
-	uint16_t svcparamkey = svcbparam_lookup_key(key, key_len);
-	uint16_t *r;
-
-	switch (svcparamkey) {
-	case SVCB_KEY_PORT:
-		return zparser_conv_svcbparam_port_value(region, val);
-	case SVCB_KEY_IPV4HINT:
-		return zparser_conv_svcbparam_ipv4hint_value(region, val);
-	case SVCB_KEY_IPV6HINT:
-		return zparser_conv_svcbparam_ipv6hint_value(region, val);
-	case SVCB_KEY_MANDATORY:
-		return zparser_conv_svcbparam_mandatory_value(region, val, val_len);
-	case SVCB_KEY_NO_DEFAULT_ALPN:
-		if(zone_is_slave(parser->current_zone->opts))
-			zc_warning_prev_line("no-default-alpn should not have a value");
-		else
-			zc_error_prev_line("no-default-alpn should not have a value");
-		break;
-	case SVCB_KEY_ECH:
-		return zparser_conv_svcbparam_ech_value(region, val);
-	case SVCB_KEY_ALPN:
-		return zparser_conv_svcbparam_alpn_value(region, val, val_len);
-	case SVCB_KEY_DOHPATH:
-		/* fallthrough */
-	default:
-		break;
-	}
-	r = alloc_rdata(region, 2 * sizeof(uint16_t) + val_len);
-	r[1] = htons(svcparamkey);
-	r[2] = htons(val_len);
-	memcpy(r + 3, val, val_len);
-	return r;
-}
-
-uint16_t *
-zparser_conv_svcbparam(region_type *region, const char *key, size_t key_len
-                                          , const char *val, size_t val_len)
-{
-	const char *eq;
-	uint16_t *r;
-	uint16_t svcparamkey;
-
-	/* Form <key>="<value>" (or at least with quoted value) */
-	if (val && val_len) {
-		/* Does key end with '=' */
-		if (key_len && key[key_len - 1] == '=')
-			return zparser_conv_svcbparam_key_value(
-			    region, key, key_len - 1, val, val_len);
-
-		zc_error_prev_line( "SvcParam syntax error in param: %s\"%s\""
-		                  , key, val);
-	}
-	assert(val == NULL);
-	if ((eq = memchr(key, '=', key_len))) {
-		size_t new_key_len = eq - key;
-
-		if (key_len - new_key_len - 1 > 0)
-			return zparser_conv_svcbparam_key_value(region,
-			    key, new_key_len, eq+1, key_len - new_key_len - 1);
-		key_len = new_key_len;
-	}
-	/* Some SvcParamKeys require values */
-	svcparamkey = svcbparam_lookup_key(key, key_len);
-	switch (svcparamkey) {
-		case SVCB_KEY_MANDATORY:
-		case SVCB_KEY_ALPN:
-		case SVCB_KEY_PORT:
-		case SVCB_KEY_IPV4HINT:
-		case SVCB_KEY_IPV6HINT:
-		case SVCB_KEY_DOHPATH:
-			if(zone_is_slave(parser->current_zone->opts))
-				zc_warning_prev_line("value expected for SvcParam: %s", key);
-			else
-				zc_error_prev_line("value expected for SvcParam: %s", key);
-			break;
-		default:
-			break;
-	}
-	/* SvcParam is only a SvcParamKey */
-	r = alloc_rdata(region, 2 * sizeof(uint16_t));
-	r[1] = htons(svcparamkey);
-	r[2] = 0;
-	return r;
-}
-
-/* Parse an int terminated in the specified range. */
-static int
-parse_int(const char *str,
-	  char **end,
-	  int *result,
-	  const char *name,
-	  int min,
-	  int max)
-{
-	*result = (int) strtol(str, end, 10);
-	if (*result < min || *result > max) {
-		zc_error_prev_line("%s must be within the range [%d .. %d]",
-				   name,
-				   min,
-				   max);
-		return 0;
-	} else {
-		return 1;
-	}
-}
-
-/* RFC1876 conversion routines */
-static unsigned int poweroften[10] = {1, 10, 100, 1000, 10000, 100000,
-				1000000,10000000,100000000,1000000000};
-
-/*
- * Converts ascii size/precision X * 10**Y(cm) to 0xXY.
- * Sets the given pointer to the last used character.
- *
- */
-static uint8_t
-precsize_aton (char *cp, char **endptr)
-{
-	unsigned int mval = 0, cmval = 0;
-	uint8_t retval = 0;
-	int exponent;
-	int mantissa;
-
-	while (isdigit((unsigned char)*cp))
-		mval = mval * 10 + hexdigit_to_int(*cp++);
-
-	if (*cp == '.') {	/* centimeters */
-		cp++;
-		if (isdigit((unsigned char)*cp)) {
-			cmval = hexdigit_to_int(*cp++) * 10;
-			if (isdigit((unsigned char)*cp)) {
-				cmval += hexdigit_to_int(*cp++);
-			}
-		}
-	}
-
-	if(mval >= poweroften[7]) {
-		assert(poweroften[7] != 0);
-		/* integer overflow possible for *100 */
-		mantissa = mval / poweroften[7];
-		exponent = 9; /* max */
-	}
-	else {
-		cmval = (mval * 100) + cmval;
-
-		for (exponent = 0; exponent < 9; exponent++)
-			if (cmval < poweroften[exponent+1])
-				break;
-
-		assert(poweroften[exponent] != 0);
-		mantissa = cmval / poweroften[exponent];
-	}
-	if (mantissa > 9)
-		mantissa = 9;
-
-	retval = (mantissa << 4) | exponent;
-
-	if (*cp == 'm') cp++;
-
-	*endptr = cp;
-
-	return (retval);
-}
-
-/*
- * Parses a specific part of rdata.
- *
- * Returns:
- *
- *	number of elements parsed
- *	zero on error
- *
- */
-uint16_t *
-zparser_conv_loc(region_type *region, char *str)
-{
-	uint16_t *r;
-	uint32_t *p;
-	int i;
-	int deg, min, secs;	/* Secs is stored times 1000.  */
-	uint32_t lat = 0, lon = 0, alt = 0;
-	/* encoded defaults: version=0 sz=1m hp=10000m vp=10m */
-	uint8_t vszhpvp[4] = {0, 0x12, 0x16, 0x13};
-	char *start;
-	double d;
-
-	for(;;) {
-		deg = min = secs = 0;
-
-		/* Degrees */
-		if (*str == '\0') {
-			zc_error_prev_line("unexpected end of LOC data");
-			return NULL;
-		}
-
-		if (!parse_int(str, &str, &deg, "degrees", 0, 180))
-			return NULL;
-		if (!isspace((unsigned char)*str)) {
-			zc_error_prev_line("space expected after degrees");
-			return NULL;
-		}
-		++str;
-
-		/* Minutes? */
-		if (isdigit((unsigned char)*str)) {
-			if (!parse_int(str, &str, &min, "minutes", 0, 60))
-				return NULL;
-			if (!isspace((unsigned char)*str)) {
-				zc_error_prev_line("space expected after minutes");
-				return NULL;
-			}
-			++str;
-		}
-
-		/* Seconds? */
-		if (isdigit((unsigned char)*str)) {
-			start = str;
-			if (!parse_int(str, &str, &i, "seconds", 0, 60)) {
-				return NULL;
-			}
-
-			if (*str == '.' && !parse_int(str + 1, &str, &i, "seconds fraction", 0, 999)) {
-				return NULL;
-			}
-
-			if (!isspace((unsigned char)*str)) {
-				zc_error_prev_line("space expected after seconds");
-				return NULL;
-			}
-			/* No need for precision specifiers, it's a double */
-			if (sscanf(start, "%lf", &d) != 1) {
-				zc_error_prev_line("error parsing seconds");
-			}
-
-			if (d < 0.0 || d > 60.0) {
-				zc_error_prev_line("seconds not in range 0.0 .. 60.0");
-			}
-
-			secs = (int) (d * 1000.0 + 0.5);
-			++str;
-		}
-
-		switch(*str) {
-		case 'N':
-		case 'n':
-			lat = ((uint32_t)1<<31) + (deg * 3600000 + min * 60000 + secs);
-			break;
-		case 'E':
-		case 'e':
-			lon = ((uint32_t)1<<31) + (deg * 3600000 + min * 60000 + secs);
-			break;
-		case 'S':
-		case 's':
-			lat = ((uint32_t)1<<31) - (deg * 3600000 + min * 60000 + secs);
-			break;
-		case 'W':
-		case 'w':
-			lon = ((uint32_t)1<<31) - (deg * 3600000 + min * 60000 + secs);
-			break;
-		default:
-			zc_error_prev_line("invalid latitude/longtitude: '%c'", *str);
-			return NULL;
-		}
-		++str;
-
-		if (lat != 0 && lon != 0)
-			break;
-
-		if (!isspace((unsigned char)*str)) {
-			zc_error_prev_line("space expected after latitude/longitude");
-			return NULL;
-		}
-		++str;
-	}
-
-	/* Altitude */
-	if (*str == '\0') {
-		zc_error_prev_line("unexpected end of LOC data");
-		return NULL;
-	}
-
-	if (!isspace((unsigned char)*str)) {
-		zc_error_prev_line("space expected before altitude");
-		return NULL;
-	}
-	++str;
-
-	start = str;
-
-	/* Sign */
-	if (*str == '+' || *str == '-') {
-		++str;
-	}
-
-	/* Meters of altitude... */
-	if(strtol(str, &str, 10) == LONG_MAX) {
-		zc_error_prev_line("altitude too large, number overflow");
-		return NULL;
-	}
-	switch(*str) {
-	case ' ':
-	case '\0':
-	case 'm':
-		break;
-	case '.':
-		if (!parse_int(str + 1, &str, &i, "altitude fraction", 0, 99)) {
-			return NULL;
-		}
-		if (!isspace((unsigned char)*str) && *str != '\0' && *str != 'm') {
-			zc_error_prev_line("altitude fraction must be a number");
-			return NULL;
-		}
-		break;
-	default:
-		zc_error_prev_line("altitude must be expressed in meters");
-		return NULL;
-	}
-	if (!isspace((unsigned char)*str) && *str != '\0')
-		++str;
-
-	if (sscanf(start, "%lf", &d) != 1) {
-		zc_error_prev_line("error parsing altitude");
-	}
-
-	alt = (uint32_t) (10000000.0 + d * 100 + 0.5);
-
-	if (!isspace((unsigned char)*str) && *str != '\0') {
-		zc_error_prev_line("unexpected character after altitude");
-		return NULL;
-	}
-
-	/* Now parse size, horizontal precision and vertical precision if any */
-	for(i = 1; isspace((unsigned char)*str) && i <= 3; i++) {
-		vszhpvp[i] = precsize_aton(str + 1, &str);
-
-		if (!isspace((unsigned char)*str) && *str != '\0') {
-			zc_error_prev_line("invalid size or precision");
-			return NULL;
-		}
-	}
-
-	/* Allocate required space... */
-	r = alloc_rdata(region, 16);
-	p = (uint32_t *) (r + 1);
-
-	memmove(p, vszhpvp, 4);
-	write_uint32(p + 1, lat);
-	write_uint32(p + 2, lon);
-	write_uint32(p + 3, alt);
-
-	return r;
-}
-
-/*
- * Convert an APL RR RDATA element.
- */
-uint16_t *
-zparser_conv_apl_rdata(region_type *region, char *str)
-{
-	int negated = 0;
-	uint16_t address_family;
-	uint8_t prefix;
-	uint8_t maximum_prefix;
-	uint8_t length;
-	uint8_t address[IP6ADDRLEN];
-	char *colon = strchr(str, ':');
-	char *slash = strchr(str, '/');
-	int af;
-	int rc;
-	uint16_t rdlength;
-	uint16_t *r;
-	uint8_t *t;
-	char *end;
-	long p;
-
-	if (!colon) {
-		zc_error("address family separator is missing");
-		return NULL;
-	}
-	if (!slash) {
-		zc_error("prefix separator is missing");
-		return NULL;
-	}
-
-	*colon = '\0';
-	*slash = '\0';
-
-	if (*str == '!') {
-		negated = 1;
-		++str;
-	}
-
-	if (strcmp(str, "1") == 0) {
-		address_family = htons(1);
-		af = AF_INET;
-		length = sizeof(in_addr_t);
-		maximum_prefix = length * 8;
-	} else if (strcmp(str, "2") == 0) {
-		address_family = htons(2);
-		af = AF_INET6;
-		length = IP6ADDRLEN;
-		maximum_prefix = length * 8;
-	} else {
-		zc_error("invalid address family '%s'", str);
-		return NULL;
-	}
-
-	rc = inet_pton(af, colon + 1, address);
-	if (rc == 0) {
-		zc_error("invalid address '%s'", colon + 1);
-		return NULL;
-	} else if (rc == -1) {
-		zc_error("inet_pton failed: %s", strerror(errno));
-		return NULL;
-	}
-
-	/* Strip trailing zero octets.	*/
-	while (length > 0 && address[length - 1] == 0)
-		--length;
-
-
-	p = strtol(slash + 1, &end, 10);
-	if (p < 0 || p > maximum_prefix) {
-		zc_error("prefix not in the range 0 .. %d", maximum_prefix);
-		return NULL;
-	} else if (*end != '\0') {
-		zc_error("invalid prefix '%s'", slash + 1);
-		return NULL;
-	}
-	prefix = (uint8_t) p;
-
-	rdlength = (sizeof(address_family) + sizeof(prefix) + sizeof(length)
-		    + length);
-	r = alloc_rdata(region, rdlength);
-	t = (uint8_t *) (r + 1);
-
-	memcpy(t, &address_family, sizeof(address_family));
-	t += sizeof(address_family);
-	memcpy(t, &prefix, sizeof(prefix));
-	t += sizeof(prefix);
-	memcpy(t, &length, sizeof(length));
-	if (negated) {
-		*t |= APL_NEGATION_MASK;
-	}
-	t += sizeof(length);
-	memcpy(t, address, length);
-
-	return r;
-}
-
-/*
- * Below some function that also convert but not to wireformat
- * but to "normal" (int,long,char) types
- */
-
-uint32_t
-zparser_ttl2int(const char *ttlstr, int* error)
-{
-	/* convert a ttl value to a integer
-	 * return the ttl in a int
-	 * -1 on error
-	 */
-
-	uint32_t ttl;
-	const char *t;
-
-	ttl = strtottl(ttlstr, &t);
-	if (*t != 0) {
-		zc_error_prev_line("invalid TTL value: %s",ttlstr);
-		*error = 1;
-	}
-
-	return ttl;
-}
-
-
-void
-zadd_rdata_wireformat(uint16_t *data)
-{
-	if (parser->current_rr.rdata_count >= MAXRDATALEN) {
-		zc_error_prev_line("too many rdata elements");
-	} else {
-		parser->current_rr.rdatas[parser->current_rr.rdata_count].data
-			= data;
-		++parser->current_rr.rdata_count;
-	}
-}
-
-/**
- * Used for TXT RR's to grow with undefined number of strings.
- */
-void
-zadd_rdata_txt_wireformat(uint16_t *data, int first)
-{
-	rdata_atom_type *rd;
-	if (parser->current_rr.rdata_count >= MAXRDATALEN) {
-		zc_error_prev_line("too many rdata txt elements");
-		return;
-	}
-	
-	/* First STR in str_seq, allocate 65K in first unused rdata
-	 * else find last used rdata */
-	if (first) {
-		rd = &parser->current_rr.rdatas[parser->current_rr.rdata_count];
-		if ((rd->data = (uint16_t *) region_alloc(parser->rr_region,
-			sizeof(uint16_t) + 65535 * sizeof(uint8_t))) == NULL) {
-			zc_error_prev_line("Could not allocate memory for TXT RR");
-			return;
-		}
-		parser->current_rr.rdata_count++;
-		rd->data[0] = 0;
-	}
-	else
-		rd = &parser->current_rr.rdatas[parser->current_rr.rdata_count-1];
-	
-	if ((size_t)rd->data[0] + (size_t)data[0] > 65535) {
-		zc_error_prev_line("too large rdata element");
-		return;
-	}
-	
-	memcpy((uint8_t *)rd->data + 2 + rd->data[0], data + 1, data[0]);
-	rd->data[0] += data[0];
-}
-
-/**
- * Clean up after last call of zadd_rdata_txt_wireformat
- */
-void
-zadd_rdata_txt_clean_wireformat()
-{
-	uint16_t *tmp_data;
-	rdata_atom_type *rd = &parser->current_rr.rdatas[parser->current_rr.rdata_count-1];
-	if(!rd || !rd->data)
-		return; /* previous syntax failure */
-	if ((tmp_data = (uint16_t *) region_alloc(parser->region, 
-		((size_t)rd->data[0]) + ((size_t)2))) != NULL) {
-		memcpy(tmp_data, rd->data, rd->data[0] + 2);
-		/* rd->data of u16+65535 freed when rr_region is freed */
-		rd->data = tmp_data;
-	}
-	else {
-		/* We could not get memory in non-volatile region */
-		zc_error_prev_line("could not allocate memory for rdata");
-		return;
-	}
-}
-
-static int
-svcparam_key_cmp(const void *a, const void *b)
-{
-	return ((int)read_uint16(rdata_atom_data(*(rdata_atom_type *)a)))
-	     - ((int)read_uint16(rdata_atom_data(*(rdata_atom_type *)b)));
-}
-
-void
-zadd_rdata_svcb_check_wireformat()
-{
-	size_t i;
-	uint8_t paramkeys[65536];
-	int prev_key = - 1;
-	int key = 0;
-	size_t size;
-	uint16_t *mandatory_values;
-
-	if (parser->current_rr.rdata_count <= 2) {
-		if (!parser->error_occurred)
-			zc_error_prev_line("invalid SVCB or HTTPS rdata");
-		return;
-	} else for (i = 2; i < parser->current_rr.rdata_count; i++) {
-		if (parser->current_rr.rdatas[i].data == NULL
-		||  rdata_atom_data(parser->current_rr.rdatas[i]) == NULL
-		||  rdata_atom_size(parser->current_rr.rdatas[i]) < 4) {
-			if (!parser->error_occurred)
-				zc_error_prev_line("invalid SVCB or HTTPS rdata");
-			return;
-		}
-	}
-	/* After this point, all rdatas do have data larger than 4 bytes.
-	 * So we may assume a uint16_t SVCB key followed by uint16_t length
-	 * in each rdata in the remainder of this function.
-	 */
-	memset(paramkeys, 0, sizeof(paramkeys));
-	/* 
-	 * In draft-ietf-dnsop-svcb-https-04 Section 7:
-	 * In wire format, the keys are represented by their numeric values in
-	 * network byte order, concatenated in ascending order.
-	 *
-	 * svcparam_key_cmp assumes the rdatas to have a SVCB key, which is
-	 * safe because we checked.
-	 *
-	 */
-	qsort( (void *)&parser->current_rr.rdatas[2]
-	     , parser->current_rr.rdata_count - 2
-	     , sizeof(rdata_atom_type)
-	     , svcparam_key_cmp
-	     );
-
-	for (i = 2; i < parser->current_rr.rdata_count; i++) {
-		assert(parser->current_rr.rdatas[i].data);
-		assert(rdata_atom_data(parser->current_rr.rdatas[i]));
-		assert(rdata_atom_size(parser->current_rr.rdatas[i]) >= sizeof(uint16_t));
-		 
-		key = read_uint16(rdata_atom_data(parser->current_rr.rdatas[i]));
-
-		/* In draft-ietf-dnsop-svcb-https-04 Section 7:
-		 *
-		 *     Keys (...) MUST NOT appear more than once.
-		 * 
-		 * If they key has already been seen, we have a duplicate
-		 */
-		if (!paramkeys[key])
-			/* keep track of keys that are present */
-			paramkeys[key] = 1;
-
-		else if (key < SVCPARAMKEY_COUNT) {
-			if(zone_is_slave(parser->current_zone->opts))
-				zc_warning_prev_line(
-					"Duplicate key found: %s",
-					svcparamkey_strs[key]);
-			else {
-				zc_error_prev_line(
-					"Duplicate key found: %s",
-					svcparamkey_strs[key]);
-			}
-		} else if(zone_is_slave(parser->current_zone->opts))
-			zc_warning_prev_line(
-					"Duplicate key found: key%d", key);
-		else
-			zc_error_prev_line(
-					"Duplicate key found: key%d", key);
-	}
-	/* Checks when a mandatory key is present */
-	if (!paramkeys[SVCB_KEY_MANDATORY])
-		return;
-
-	size = rdata_atom_size(parser->current_rr.rdatas[2]);
-	assert(size >= 4);
-	mandatory_values = (void*)rdata_atom_data(parser->current_rr.rdatas[2]);
-	mandatory_values += 2; /* skip the key type and length */
-
-	if (size % 2)
-		zc_error_prev_line("mandatory rdata must be a multiple of shorts");
-		
-	else for (i = 0; i < (size - 4)/2; i++) {
-		key = ntohs(mandatory_values[i]);
-
-		if (paramkeys[key])
-			; /* pass */
-
-		else if (key < SVCPARAMKEY_COUNT) {
-			if(zone_is_slave(parser->current_zone->opts))
-				zc_warning_prev_line("mandatory SvcParamKey: %s is missing "
-						     "the record", svcparamkey_strs[key]);
-			else
-				zc_error_prev_line("mandatory SvcParamKey: %s is missing "
-						   "the record", svcparamkey_strs[key]);
-		} else {
-			if(zone_is_slave(parser->current_zone->opts))
-				zc_warning_prev_line("mandatory SvcParamKey: key%d is missing "
-						     "the record", key);
-			else
-				zc_error_prev_line("mandatory SvcParamKey: key%d is missing "
-						   "the record", key);
-		}
-
-		/* In draft-ietf-dnsop-svcb-https-04 Section 8
-		 * automatically mandatory MUST NOT appear in its own value-list
-		 */
-		if (key == SVCB_KEY_MANDATORY) {
-			if(zone_is_slave(parser->current_zone->opts))
-				zc_warning_prev_line("mandatory MUST not be included"
-						     " as mandatory parameter");
-			else
-				zc_error_prev_line("mandatory MUST not be included"
-						   " as mandatory parameter");
-		}
-		if (key == prev_key) {
-			if(zone_is_slave(parser->current_zone->opts))
-				zc_warning_prev_line("Keys inSvcParam mandatory "
-				                   "MUST NOT appear more than once.");
-			else
-				zc_error_prev_line("Keys in SvcParam mandatory "
-				                   "MUST NOT appear more than once.");
-		}
-		prev_key = key;
-	}
-}
-
-void
-zadd_rdata_domain(domain_type *domain)
-{
-	if (parser->current_rr.rdata_count >= MAXRDATALEN) {
-		zc_error_prev_line("too many rdata elements");
-	} else {
-		parser->current_rr.rdatas[parser->current_rr.rdata_count].domain
-			= domain;
-		domain->usage ++; /* new reference to domain */
-		++parser->current_rr.rdata_count;
-	}
-}
-
-void
-parse_unknown_rdata(uint16_t type, uint16_t *wireformat)
-{
-	buffer_type packet;
-	uint16_t size;
-	ssize_t rdata_count;
-	ssize_t i;
-	rdata_atom_type *rdatas;
-
-	if (wireformat) {
-		size = *wireformat;
-	} else {
-		return;
-	}
-
-	buffer_create_from(&packet, wireformat + 1, *wireformat);
-	rdata_count = rdata_wireformat_to_rdata_atoms(parser->region,
-						      parser->db->domains,
-						      type,
-						      size,
-						      &packet,
-						      &rdatas);
-	if (rdata_count == -1) {
-		zc_error_prev_line("bad unknown RDATA");
-		return;
-	}
-
-	for (i = 0; i < rdata_count; ++i) {
-		if (rdata_atom_is_domain(type, i)) {
-			zadd_rdata_domain(rdatas[i].domain);
-		} else {
-			zadd_rdata_wireformat(rdatas[i].data);
-		}
-	}
-	region_recycle(parser->region, rdatas,
-		rdata_count*sizeof(rdata_atom_type));
-}
-
+#include "zone.h"
 
 /*
  * Compares two rdata arrays.
@@ -1864,42 +54,40 @@ parse_unknown_rdata(uint16_t type, uint1
  *
  */
 static int
-zrdatacmp(uint16_t type, rr_type *a, rr_type *b)
+zrdatacmp(uint16_t type, const union rdata_atom *rdatas, size_t rdata_count, rr_type *b)
 {
-	int i = 0;
-
-	assert(a);
+	assert(rdatas);
 	assert(b);
 
 	/* One is shorter than another */
-	if (a->rdata_count != b->rdata_count)
+	if (rdata_count != b->rdata_count)
 		return 1;
 
 	/* Compare element by element */
-	for (i = 0; i < a->rdata_count; ++i) {
+	for (size_t i = 0; i < rdata_count; ++i) {
 		if (rdata_atom_is_domain(type, i)) {
-			if (rdata_atom_domain(a->rdatas[i])
+			if (rdata_atom_domain(rdatas[i])
 			    != rdata_atom_domain(b->rdatas[i]))
 			{
 				return 1;
 			}
 		} else if(rdata_atom_is_literal_domain(type, i)) {
-			if (rdata_atom_size(a->rdatas[i])
+			if (rdata_atom_size(rdatas[i])
 			    != rdata_atom_size(b->rdatas[i]))
 				return 1;
-			if (!dname_equal_nocase(rdata_atom_data(a->rdatas[i]),
+			if (!dname_equal_nocase(rdata_atom_data(rdatas[i]),
 				   rdata_atom_data(b->rdatas[i]),
-				   rdata_atom_size(a->rdatas[i])))
+				   rdata_atom_size(rdatas[i])))
 				return 1;
 		} else {
-			if (rdata_atom_size(a->rdatas[i])
+			if (rdata_atom_size(rdatas[i])
 			    != rdata_atom_size(b->rdatas[i]))
 			{
 				return 1;
 			}
-			if (memcmp(rdata_atom_data(a->rdatas[i]),
+			if (memcmp(rdata_atom_data(rdatas[i]),
 				   rdata_atom_data(b->rdatas[i]),
-				   rdata_atom_size(a->rdatas[i])) != 0)
+				   rdata_atom_size(rdatas[i])) != 0)
 			{
 				return 1;
 			}
@@ -1911,52 +99,55 @@ zrdatacmp(uint16_t type, rr_type *a, rr_
 }
 
 /*
- *
- * Opens a zone file.
- *
- * Returns:
- *
- *	- pointer to the parser structure
- *	- NULL on error and errno set
- *
+ * Find rrset type for any zone
  */
-static int
-zone_open(const char *filename, uint32_t ttl, uint16_t klass,
-	  const dname_type *origin)
+static rrset_type*
+domain_find_rrset_any(domain_type *domain, uint16_t type)
 {
-	/* Open the zone file... */
-	if (strcmp(filename, "-") == 0) {
-		yyin = stdin;
-		filename = "<stdin>";
-		warn_if_directory("zonefile from stdin", yyin, filename);
-	} else {
-		if (!(yyin = fopen(filename, "r"))) {
-			return 0;
+	rrset_type *result = domain->rrsets;
+	while (result) {
+		if (rrset_rrtype(result) == type) {
+			return result;
 		}
-		warn_if_directory("zonefile", yyin, filename);
+		result = result->next;
 	}
-
-	zparser_init(filename, ttl, klass, origin);
-
-	return 1;
+	return NULL;
 }
 
-
-void
-set_bitnsec(uint8_t bits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE],
-	    uint16_t index)
+/*
+ * Check for DNAME type. Nothing is allowed below it
+ */
+static int
+check_dname(zone_type* zone)
 {
-	/*
-	 * The bits are counted from left to right, so bit #0 is the
-	 * left most bit.
-	 */
-	uint8_t window = index / 256;
-	uint8_t bit = index % 256;
+	domain_type* domain;
+	for(domain = zone->apex; domain && domain_is_subdomain(domain,
+		zone->apex); domain=domain_next(domain))
+	{
+		if(domain->is_existing) {
+			/* there may not be DNAMEs above it */
+			domain_type* parent = domain->parent;
+#ifdef NSEC3
+			if(domain_has_only_NSEC3(domain, NULL))
+				continue;
+#endif
+			while(parent) {
+				if(domain_find_rrset_any(parent, TYPE_DNAME)) {
+					log_msg(LOG_ERR, "While checking node %s,",
+						domain_to_string(domain));
+					log_msg(LOG_ERR, "DNAME at %s has data below it. "
+						"This is not allowed (rfc 2672).",
+						domain_to_string(parent));
+					return 0;
+				}
+				parent = parent->parent;
+			}
+		}
+	}
 
-	bits[window][bit / 8] |= (1 << (7 - bit % 8));
+	return 1;
 }
 
-
 static int
 has_soa(domain_type* domain)
 {
@@ -1968,226 +159,239 @@ has_soa(domain_type* domain)
 	return 0;
 }
 
-int
-process_rr(void)
-{
-	zone_type *zone = parser->current_zone;
-	rr_type *rr = &parser->current_rr;
-	rrset_type *rrset;
-	size_t max_rdlength;
-	int i;
-	rrtype_descriptor_type *descriptor
-		= rrtype_descriptor_by_type(rr->type);
-
-	/* We only support IN class */
-	if (rr->klass != CLASS_IN) {
-		if(zone_is_slave(zone->opts))
-			zc_warning_prev_line("only class IN is supported");
-		else
-			zc_error_prev_line("only class IN is supported");
-		return 0;
-	}
+struct zonec_state {
+	struct namedb *database;
+	struct domain_table *domains;
+	struct region *rr_region;
+	struct zone *zone;
+	struct domain *domain;
+	size_t errors;
+	size_t records;
+};
+
+int32_t zonec_accept(
+	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 rr *rr;
+	struct rrset *rrset;
+	const struct dname *dname;
+	struct domain *domain;
+	struct buffer buffer;
+	int priority;
+	union rdata_atom *rdatas;
+	ssize_t rdata_count;
+	struct zonec_state *state = (struct zonec_state *)user_data;
 
-	/* Make sure the maximum RDLENGTH does not exceed 65535 bytes.	*/
-	max_rdlength = rdata_maximum_wireformat_size(
-		descriptor, rr->rdata_count, rr->rdatas);
-
-	if (max_rdlength > MAX_RDLENGTH) {
-		zc_error_prev_line("maximum rdata length exceeds %d octets", MAX_RDLENGTH);
-		return 0;
-	}
+	assert(state);
 
-	/* We cannot print invalid owner names,
-	 * so error on that before it is used in printing other errors.
-	 */
-	if (rr->owner == error_domain
-	||  domain_dname(rr->owner) == error_dname) {
-		zc_error_prev_line("invalid owner name");
-		return 0;
-	}
+	// emulate packet buffer to leverage rdata_wireformat_to_rdata_atoms
+	buffer_create_from(&buffer, rdata, rdlength);
+
+	priority = parser->options.secondary ? ZONE_WARNING : ZONE_ERROR;
+	// limit to IN class
+	if (class != CLASS_IN)
+		zone_log(parser, priority, "only class IN is supported");
+
+	dname = dname_make(state->rr_region, owner->octets, 1);
+	assert(dname);
+	domain = domain_table_insert(state->domains, dname);
+	assert(domain);
+
+	rdatas = NULL;
+	rdata_count = rdata_wireformat_to_rdata_atoms(
+		state->database->region, state->domains, type, rdlength, &buffer, &rdatas);
+	// number of atoms must not exceed maximum of 65535 (all empty strings)
+	assert(rdata_count >= 0);
+	assert(rdata_count <= MAX_RDLENGTH);
 
 	/* we have the zone already */
-	assert(zone);
-	if (rr->type == TYPE_SOA) {
-		if (rr->owner != zone->apex) {
+	if (type == TYPE_SOA) {
+		if (domain != state->zone->apex) {
 			char s[MAXDOMAINLEN*5];
-			snprintf(s, sizeof(s), "%s", domain_to_string(zone->apex));
-			zc_error_prev_line(
-				"SOA record with invalid domain name, '%s' is not '%s'", domain_to_string(rr->owner), s);
-			return 0;
-		}
-		if(has_soa(rr->owner)) {
-			if(zone_is_slave(zone->opts))
-				zc_warning_prev_line("this SOA record was already encountered");
-			else
-				zc_error_prev_line("this SOA record was already encountered");
-			return 0;
+			snprintf(s, sizeof(s), "%s", domain_to_string(domain));
+			zone_log(parser, priority, "SOA record with invalid domain name, '%s' is not '%s'",
+				domain_to_string(state->zone->apex), s);
+		} else if (has_soa(domain)) {
+			zone_log(parser, priority, "this SOA record was already encountered");
 		}
-		rr->owner->is_apex = 1;
+		domain->is_apex = 1;
 	}
 
-	if (!domain_is_subdomain(rr->owner, zone->apex))
-	{
+	if (!domain_is_subdomain(domain, state->zone->apex)) {
 		char s[MAXDOMAINLEN*5];
-		snprintf(s, sizeof(s), "%s", domain_to_string(zone->apex));
-		if(zone_is_slave(zone->opts))
-			zc_warning_prev_line("out of zone data: %s is outside the zone for fqdn %s", domain_to_string(rr->owner), s);
-		else
-			zc_error_prev_line("out of zone data: %s is outside the zone for fqdn %s", domain_to_string(rr->owner), s);
-		return 0;
+		snprintf(s, sizeof(s), "%s", domain_to_string(state->zone->apex));
+		zone_log(parser, priority, "out of zone data: %s is outside the zone for fqdn %s",
+		         s, domain_to_string(domain));
+		if (!parser->options.secondary) {
+			region_free_all(state->rr_region);
+			return ZONE_SEMANTIC_ERROR;
+		}
 	}
 
 	/* Do we have this type of rrset already? */
-	rrset = domain_find_rrset(rr->owner, zone, rr->type);
+	rrset = domain_find_rrset(domain, state->zone, type);
 	if (!rrset) {
-		rrset = (rrset_type *) region_alloc(parser->region,
-						    sizeof(rrset_type));
-		rrset->zone = zone;
-		rrset->rr_count = 1;
-		rrset->rrs = (rr_type *) region_alloc(parser->region,
-						      sizeof(rr_type));
-		rrset->rrs[0] = *rr;
+		rrset = region_alloc(state->database->region, sizeof(*rrset));
+		rrset->zone = state->zone;
+		rrset->rr_count = 0;
+		rrset->rrs = region_alloc(state->database->region, sizeof(*rr));
+
+		switch (type) {
+			case TYPE_CNAME:
+				if (!domain_find_non_cname_rrset(domain, state->zone))
+					break;
+				zone_log(parser, priority, "CNAME and other data at the same name");
+				break;
+			case TYPE_RRSIG:
+			case TYPE_NXT:
+			case TYPE_SIG:
+			case TYPE_NSEC:
+			case TYPE_NSEC3:
+				break;
+			default:
+				if (!domain_find_rrset(domain, state->zone, TYPE_CNAME))
+					break;
+				zone_log(parser, priority, "CNAME and other data at the same name");
+				break;
+		}
 
 		/* Add it */
-		domain_add_rrset(rr->owner, rrset);
+		domain_add_rrset(domain, rrset);
 	} else {
-		rr_type* o;
-		if (rr->type != TYPE_RRSIG && rrset->rrs[0].ttl != rr->ttl) {
-			zc_warning_prev_line(
-				"%s TTL %u does not match the TTL %u of the %s RRset",
-				domain_to_string(rr->owner), (unsigned)rr->ttl,
-				(unsigned)rrset->rrs[0].ttl,
-				rrtype_to_string(rr->type));
+		struct rr *rrs;
+		if (type != TYPE_RRSIG && ttl != rrset->rrs[0].ttl) {
+			zone_log(parser, ZONE_WARNING, "%s TTL %"PRIu32" does not match TTL %u of %s RRset",
+				domain_to_string(domain), ttl, rrset->rrs[0].ttl,
+					rrtype_to_string(type));
 		}
 
 		/* Search for possible duplicates... */
-		for (i = 0; i < rrset->rr_count; i++) {
-			if (!zrdatacmp(rr->type, rr, &rrset->rrs[i])) {
-				break;
-			}
-		}
-
-		/* Discard the duplicates... */
-		if (i < rrset->rr_count) {
-			/* add rdatas to recycle bin. */
-			size_t i;
-			for (i = 0; i < rr->rdata_count; i++) {
-				if(!rdata_atom_is_domain(rr->type, i))
-					region_recycle(parser->region, rr->rdatas[i].data,
-						rdata_atom_size(rr->rdatas[i])
-						+ sizeof(uint16_t));
+		for (int i = 0; i < rrset->rr_count; i++) {
+			if (zrdatacmp(type, rdatas, rdata_count, &rrset->rrs[i]) != 0)
+				continue;
+			/* Discard the duplicates... */
+			for (size_t j = 0; j < (size_t)rdata_count; j++) {
+				size_t size;
+				if (rdata_atom_is_domain(type, j))
+					continue;
+				size = rdata_atom_size(rdatas[j]) + sizeof(uint16_t);
+				region_recycle(state->database->region, rdatas[j].data, size);
 			}
-			region_recycle(parser->region, rr->rdatas,
-				sizeof(rdata_atom_type)*rr->rdata_count);
+			region_recycle(state->database->region, rdatas, sizeof(*rdatas) * rdata_count);
+			region_free_all(state->rr_region);
 			return 0;
 		}
-		if(rrset->rr_count == 65535) {
-			zc_error_prev_line("too many RRs for domain RRset");
-			return 0;
+
+		switch (type) {
+			case TYPE_CNAME:
+				zone_log(parser, priority, "multiple CNAMEs at the same name");
+				break;
+			case TYPE_DNAME:
+				zone_log(parser, priority, "multiple DNAMEs at the same name");
+				break;
+			default:
+				break;
 		}
 
 		/* Add it... */
-		o = rrset->rrs;
-		rrset->rrs = (rr_type *) region_alloc_array(parser->region,
-			(rrset->rr_count + 1), sizeof(rr_type));
-		memcpy(rrset->rrs, o, (rrset->rr_count) * sizeof(rr_type));
-		region_recycle(parser->region, o,
-			(rrset->rr_count) * sizeof(rr_type));
-		rrset->rrs[rrset->rr_count] = *rr;
-		++rrset->rr_count;
+		rrs = rrset->rrs;
+		rrset->rrs = region_alloc_array(state->database->region, rrset->rr_count + 1, sizeof(*rr));
+		memcpy(rrset->rrs, rrs, rrset->rr_count * sizeof(*rr));
+		region_recycle(state->database->region, rrs, rrset->rr_count * sizeof(*rr));
 	}
 
-	if(rr->type == TYPE_DNAME && rrset->rr_count > 1) {
-		if(zone_is_slave(zone->opts))
-			zc_warning_prev_line("multiple DNAMEs at the same name");
-		else
-			zc_error_prev_line("multiple DNAMEs at the same name");
-	}
-	if(rr->type == TYPE_CNAME && rrset->rr_count > 1) {
-		if(zone_is_slave(zone->opts))
-			zc_warning_prev_line("multiple CNAMEs at the same name");
-		else
-			zc_error_prev_line("multiple CNAMEs at the same name");
-	}
-	if((rr->type == TYPE_DNAME && domain_find_rrset(rr->owner, zone, TYPE_CNAME))
-	 ||(rr->type == TYPE_CNAME && domain_find_rrset(rr->owner, zone, TYPE_DNAME))) {
-		if(zone_is_slave(zone->opts))
-			zc_warning_prev_line("DNAME and CNAME at the same name");
-		else
-			zc_error_prev_line("DNAME and CNAME at the same name");
-	}
-	if(domain_find_rrset(rr->owner, zone, TYPE_CNAME) &&
-		domain_find_non_cname_rrset(rr->owner, zone)) {
-		if(zone_is_slave(zone->opts))
-			zc_warning_prev_line("CNAME and other data at the same name");
-		else
-			zc_error_prev_line("CNAME and other data at the same name");
-	}
+	rr = &rrset->rrs[rrset->rr_count++];
+	rr->owner = domain;
+	rr->rdatas = rdatas;
+	rr->ttl = ttl;
+	rr->type = type;
+	rr->klass = class;
+	rr->rdata_count = rdata_count;
 
 	/* Check we have SOA */
-	if(rr->owner == zone->apex)
-		apex_rrset_checks(parser->db, rrset, rr->owner);
+	if (rr->owner == state->zone->apex)
+		apex_rrset_checks(state->database, rrset, rr->owner);
 
-	if(parser->line % ZONEC_PCT_COUNT == 0 && time(NULL) > startzonec + ZONEC_PCT_TIME) {
-		struct stat buf;
-		startzonec = time(NULL);
-		buf.st_size = 0;
-		fstat(fileno(yyin), &buf);
-		if(buf.st_size == 0) buf.st_size = 1;
-		VERBOSITY(1, (LOG_INFO, "parse %s %d %%",
-			parser->current_zone->opts->name,
-			(int)((uint64_t)ftell(yyin)*(uint64_t)100/(uint64_t)buf.st_size)));
-	}
-	++totalrrs;
-	return 1;
+	state->records++;
+	region_free_all(state->rr_region);
+	return 0;
 }
 
-/*
- * Find rrset type for any zone
- */
-static rrset_type*
-domain_find_rrset_any(domain_type *domain, uint16_t type)
-{
-	rrset_type *result = domain->rrsets;
-	while (result) {
-		if (rrset_rrtype(result) == type) {
-			return result;
-		}
-		result = result->next;
-	}
-	return NULL;
+static int32_t zonec_include(
+  zone_parser_t *parser,
+  const char *file,
+  const char *path,
+  void *user_data)
+{
+	char **paths;
+	struct zonec_state *state;
+	struct namedb *database;
+	struct zone *zone;
+
+	(void)parser;
+	(void)file;
+
+	state = (struct zonec_state *)user_data;
+	database = state->database;
+	zone = state->zone;
+
+	assert((zone->includes.count == 0) == (zone->includes.paths == NULL));
+
+	for (size_t i=0; i < zone->includes.count; i++)
+		if (strcmp(path, zone->includes.paths[i]) == 0)
+			return 0;
+
+	paths = region_alloc_array(
+		database->region, zone->includes.count + 1, sizeof(*paths));
+	if (zone->includes.count) {
+		const size_t size = zone->includes.count * sizeof(*paths);
+		memcpy(paths, zone->includes.paths, size);
+		region_recycle(database->region, zone->includes.paths, size);
+	}
+	paths[zone->includes.count] = region_strdup(database->region, path);
+	zone->includes.count++;
+	zone->includes.paths = paths;
+	return 0;
 }
 
-/*
- * Check for DNAME type. Nothing is allowed below it
- */
-static void
-check_dname(zone_type* zone)
-{
-	domain_type* domain;
-	for(domain = zone->apex; domain && domain_is_subdomain(domain,
-		zone->apex); domain=domain_next(domain))
-	{
-		if(domain->is_existing) {
-			/* there may not be DNAMEs above it */
-			domain_type* parent = domain->parent;
-#ifdef NSEC3
-			if(domain_has_only_NSEC3(domain, NULL))
-				continue;
-#endif
-			while(parent) {
-				if(domain_find_rrset_any(parent, TYPE_DNAME)) {
-					zc_error("While checking node %s,",
-						domain_to_string(domain));
-					zc_error("DNAME at %s has data below it. "
-						"This is not allowed (rfc 2672).",
-						domain_to_string(parent));
-					return;
-				}
-				parent = parent->parent;
-			}
-		}
+static void zonec_log(
+	zone_parser_t *parser,
+	uint32_t category,
+	const char *file,
+	size_t line,
+	const char *message,
+	void *user_data)
+{
+	int priority;
+	struct zonec_state *state = (struct zonec_state *)user_data;
+
+	assert(state);
+	(void)parser;
+
+	switch (category) {
+	case ZONE_INFO:
+		priority = LOG_INFO;
+		break;
+	case ZONE_WARNING:
+		priority = LOG_WARNING;
+		break;
+	default:
+		priority = LOG_ERR;
+		state->errors++;
+		break;
 	}
+
+	if (file)
+		log_msg(priority, "%s:%zu: %s", file, line, message);
+	else
+		log_msg(priority, "%s", message);
 }
 
 /*
@@ -2195,178 +399,65 @@ check_dname(zone_type* zone)
  * nsd_options can be NULL if no config file is passed.
  */
 unsigned int
-zonec_read(const char* name, const char* zonefile, zone_type* zone)
-{
-	const dname_type *dname;
-
-	totalrrs = 0;
-	startzonec = time(NULL);
-	parser->errors = 0;
-
-	dname = dname_parse(parser->rr_region, name);
-	if (!dname) {
-		zc_error("incorrect zone name '%s'", name);
-		return 1;
-	}
-
-	/* Open the zone file */
-	if (!zone_open(zonefile, 3600, CLASS_IN, dname)) {
-		zc_error("cannot open '%s': %s", zonefile, strerror(errno));
-		return 1;
-	}
-	parser->current_zone = zone;
+zonec_read(
+	struct namedb *database,
+	struct domain_table *domains,
+	const char *name,
+	const char *zonefile,
+	struct zone *zone)
+{
+	const struct dname *origin;
+	zone_parser_t parser;
+	zone_options_t options;
+	zone_name_buffer_t name_buffer;
+	zone_rdata_buffer_t rdata_buffer;
+	struct zonec_state state;
+	zone_buffers_t buffers = { 1, &name_buffer, &rdata_buffer };
+
+	state.database = database;
+	state.domains = domains;
+	state.rr_region = region_create(xalloc, free);
+	state.zone = zone;
+	state.domain = NULL;
+	state.errors = 0;
+	state.records = 0;
+
+	origin = domain_dname(zone->apex);
+	memset(&options, 0, sizeof(options));
+	options.origin.octets = dname_name(origin);
+	options.origin.length = origin->name_size;
+	options.default_ttl = DEFAULT_TTL;
+	options.default_class = CLASS_IN;
+	options.secondary = zone_is_slave(zone->opts) != 0;
+	options.pretty_ttls = true; /* non-standard, for backwards compatibility */
+	options.log.callback = &zonec_log;
+	options.accept.callback = &zonec_accept;
+	options.include.callback = &zonec_include;
 
 	/* Parse and process all RRs.  */
-	yyparse();
-
-	/* remove origin if it was unused */
-	if(parser->origin != error_domain)
-		domain_table_deldomain(parser->db, parser->origin);
-	/* rr_region has been emptied by now */
-	dname = dname_parse(parser->rr_region, name);
-
-	/* check if zone file contained a correct SOA record */
-	if (!parser->current_zone) {
-		zc_error("zone configured as '%s' has no content.", name);
-	} else if(!parser->current_zone->soa_rrset ||
-		parser->current_zone->soa_rrset->rr_count == 0) {
-		zc_error("zone configured as '%s' has no SOA record.", name);
-	} else if(dname_compare(domain_dname(
-		parser->current_zone->soa_rrset->rrs[0].owner), dname) != 0) {
-		zc_error("zone configured as '%s', but SOA has owner '%s'.",
-			name, domain_to_string(
-			parser->current_zone->soa_rrset->rrs[0].owner));
-	}
-	region_free_all(parser->rr_region);
-
-	parser_flush();
-	fclose(yyin);
-	if(!zone_is_slave(zone->opts))
-		check_dname(zone);
-
-	parser->filename = NULL;
-	return parser->errors;
-}
-
-
-/*
- * setup parse
- */
-void
-zonec_setup_parser(namedb_type* db)
-{
-	region_type* rr_region = region_create(xalloc, free);
-	parser = zparser_create(db->region, rr_region, db);
-	assert(parser);
-	/* Unique pointers used to mark errors.	 */
-	error_dname = (dname_type *) region_alloc(db->region, 1);
-	error_domain = (domain_type *) region_alloc(db->region, 1);
-	/* Open the network database */
-	setprotoent(1);
-	setservent(1);
-}
-
-/** desetup parse */
-void
-zonec_desetup_parser(void)
-{
-	if(parser) {
-		endservent();
-		endprotoent();
-		region_destroy(parser->rr_region);
-		/* removed when parser->region(=db->region) is destroyed:
-		 * region_recycle(parser->region, (void*)error_dname, 1);
-		 * region_recycle(parser->region, (void*)error_domain, 1); */
-		/* clear memory for exit, but this is not portable to
-		 * other versions of lex. yylex_destroy(); */
-#ifdef MEMCLEAN /* OS collects memory pages */
-		yylex_destroy();
-#endif
+	if (zone_parse(&parser, &options, &buffers, zonefile, &state) != 0) {
+		region_destroy(state.rr_region);
+		return state.errors;
+	}
+
+	/* Check if zone file contained a correct SOA record */
+	if (!zone) {
+		log_msg(LOG_ERR, "zone configured as '%s' has no content.", name);
+		state.errors++;
+	} else if (!zone->soa_rrset || zone->soa_rrset->rr_count == 0) {
+		log_msg(LOG_ERR, "zone configured as '%s' has no SOA record", name);
+		state.errors++;
+	} else if (dname_compare(domain_dname(zone->soa_rrset->rrs[0].owner), origin) != 0) {
+		log_msg(LOG_ERR, "zone configured as '%s', but SOA has owner '%s'",
+		        name, domain_to_string(zone->soa_rrset->rrs[0].owner));
+		state.errors++;
 	}
-}
-
-static domain_table_type* orig_domains = NULL;
-static region_type* orig_region = NULL;
-static region_type* orig_dbregion = NULL;
-
-/** setup for string parse */
-void
-zonec_setup_string_parser(region_type* region, domain_table_type* domains)
-{
-	assert(parser); /* global parser must be setup */
-	orig_domains = parser->db->domains;
-	orig_region = parser->region;
-	orig_dbregion = parser->db->region;
-	parser->region = region;
-	parser->db->region = region;
-	parser->db->domains = domains;
-	zparser_init("string", 3600, CLASS_IN, domain_dname(domains->root));
-}
-
-/** desetup string parse */
-void
-zonec_desetup_string_parser(void)
-{
-	parser->region = orig_region;
-	parser->db->domains = orig_domains;
-	parser->db->region = orig_dbregion;
-}
 
-/** parse a string into temporary storage */
-int
-zonec_parse_string(region_type* region, domain_table_type* domains,
-	zone_type* zone, char* str, domain_type** parsed, int* num_rrs)
-{
-	int errors;
-	zonec_setup_string_parser(region, domains);
-	parser->current_zone = zone;
-	parser->errors = 0;
-	totalrrs = 0;
-	startzonec = time(NULL)+100000; /* disable */
-	parser_push_stringbuf(str);
-	yyparse();
-	parser_pop_stringbuf();
-	errors = parser->errors;
-	*num_rrs = totalrrs;
-	if(*num_rrs == 0)
-		*parsed = NULL;
-	else	*parsed = parser->prev_dname;
-	/* remove origin if it was not used during the parse */
-	if(parser->origin != error_domain)
-		domain_table_deldomain(parser->db, parser->origin);
-	region_free_all(parser->rr_region);
-	zonec_desetup_string_parser();
-	parser_flush();
-	return errors;
-}
+	if(!zone_is_slave(zone->opts) && !check_dname(zone))
+		state.errors++;
 
-/** check SSHFP type for failures and emit warnings */
-void check_sshfp(void)
-{
-	uint8_t hash;
-	uint16_t size;
-	if(parser->current_rr.rdata_count < 3)
-		return; /* cannot check it, too few rdata elements */
-	if(!parser->current_rr.rdatas[0].data ||
-		!parser->current_rr.rdatas[1].data ||
-		!parser->current_rr.rdatas[2].data ||
-		!parser->current_rr.owner)
-		return; /* cannot check, NULLs (due to earlier errors) */
-	if(rdata_atom_size(parser->current_rr.rdatas[1]) != 1)
-		return; /* wrong size of the hash type rdata element */
-	hash = rdata_atom_data(parser->current_rr.rdatas[1])[0];
-	size = rdata_atom_size(parser->current_rr.rdatas[2]);
-	if(hash == 1 && size != 20) {
-		zc_warning_prev_line("SSHFP %s of type SHA1 has hash of "
-			"wrong length, %d bytes, should be 20",
-			domain_to_string(parser->current_rr.owner),
-			(int)size);
-	} else if(hash == 2 && size != 32) {
-		zc_warning_prev_line("SSHFP %s of type SHA256 has hash of "
-			"wrong length, %d bytes, should be 32",
-			domain_to_string(parser->current_rr.owner),
-			(int)size);
-	}
+	region_destroy(state.rr_region);
+	return state.errors;
 }
 
 void
Index: zonec.h
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/zonec.h,v
diff -u -p -r1.13 zonec.h
--- zonec.h	29 Jun 2023 19:38:50 -0000	1.13
+++ zonec.h	3 Sep 2025 13:53:32 -0000
@@ -12,10 +12,6 @@
 
 #include "namedb.h"
 
-#define	MAXTOKENSLEN	512		/* Maximum number of tokens per entry */
-#define	B64BUFSIZE	65535		/* Buffer size for b64 conversion */
-#define	ROOT		(const uint8_t *)"\001"
-
 #define NSEC_WINDOW_COUNT     256
 #define NSEC_WINDOW_BITS_COUNT 256
 #define NSEC_WINDOW_BITS_SIZE  (NSEC_WINDOW_BITS_COUNT / 8)
@@ -27,124 +23,17 @@
 
 #define LINEBUFSZ 1024
 
-struct lex_data {
-    size_t   len;		/* holds the label length */
-    char    *str;		/* holds the data */
-};
-
 #define DEFAULT_TTL 3600
 
-/* administration struct */
-typedef struct zparser zparser_type;
-struct zparser {
-	region_type *region;	/* Allocate for parser lifetime data.  */
-	region_type *rr_region;	/* Allocate RR lifetime data.  */
-	namedb_type *db;
-
-	const char *filename;
-	uint32_t default_ttl;
-	uint16_t default_class;
-	zone_type *current_zone;
-	domain_type *origin;
-	domain_type *prev_dname;
-
-	int error_occurred;
-	unsigned int errors;
-	unsigned int line;
-
-	rr_type current_rr;
-	rdata_atom_type *temporary_rdatas;
-};
-
-extern zparser_type *parser;
-
-/* used in zonec.lex */
-extern FILE *yyin;
-
-/*
- * Used to mark bad domains and domain names.  Do not dereference
- * these pointers!
- */
-extern const dname_type *error_dname;
-extern domain_type *error_domain;
-
-int yyparse(void);
-int yylex(void);
-int yylex_destroy(void);
-/*int yyerror(const char *s);*/
-void yyrestart(FILE *);
-
-void zc_warning(const char *fmt, ...) ATTR_FORMAT(printf, 1, 2);
-void zc_warning_prev_line(const char *fmt, ...) ATTR_FORMAT(printf, 1, 2);
-void zc_error(const char *fmt, ...) ATTR_FORMAT(printf, 1, 2);
-void zc_error_prev_line(const char *fmt, ...) ATTR_FORMAT(printf, 1, 2);
-
-void parser_push_stringbuf(char* str);
-void parser_pop_stringbuf(void);
-void parser_flush(void);
-
-int process_rr(void);
-uint16_t *zparser_conv_hex(region_type *region, const char *hex, size_t len);
-uint16_t *zparser_conv_hex_length(region_type *region, const char *hex, size_t len);
-uint16_t *zparser_conv_time(region_type *region, const char *time);
-uint16_t *zparser_conv_services(region_type *region, const char *protostr, char *servicestr);
-uint16_t *zparser_conv_serial(region_type *region, const char *periodstr);
-uint16_t *zparser_conv_period(region_type *region, const char *periodstr);
-uint16_t *zparser_conv_short(region_type *region, const char *text);
-uint16_t *zparser_conv_long(region_type *region, const char *text);
-uint16_t *zparser_conv_byte(region_type *region, const char *text);
-uint16_t *zparser_conv_a(region_type *region, const char *text);
-uint16_t *zparser_conv_aaaa(region_type *region, const char *text);
-uint16_t *zparser_conv_ilnp64(region_type *region, const char *text);
-uint16_t *zparser_conv_eui(region_type *region, const char *text, size_t len);
-uint16_t *zparser_conv_text(region_type *region, const char *text, size_t len);
-uint16_t *zparser_conv_long_text(region_type *region, const char *text, size_t len);
-uint16_t *zparser_conv_tag(region_type *region, const char *text, size_t len);
-uint16_t *zparser_conv_dns_name(region_type *region, const uint8_t* name, size_t len);
-uint16_t *zparser_conv_b32(region_type *region, const char *b32);
-uint16_t *zparser_conv_b64(region_type *region, const char *b64);
-uint16_t *zparser_conv_rrtype(region_type *region, const char *rr);
-uint16_t *zparser_conv_nxt(region_type *region, uint8_t nxtbits[]);
-uint16_t *zparser_conv_nsec(region_type *region, uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE]);
-uint16_t *zparser_conv_loc(region_type *region, char *str);
-uint16_t *zparser_conv_algorithm(region_type *region, const char *algstr);
-uint16_t *zparser_conv_certificate_type(region_type *region,
-					const char *typestr);
-uint16_t *zparser_conv_apl_rdata(region_type *region, char *str);
-uint16_t *zparser_conv_svcbparam(region_type *region,
-	const char *key, size_t key_len, const char *value, size_t value_len);
-
-void parse_unknown_rdata(uint16_t type, uint16_t *wireformat);
-
-uint32_t zparser_ttl2int(const char *ttlstr, int* error);
-void zadd_rdata_wireformat(uint16_t *data);
-void zadd_rdata_txt_wireformat(uint16_t *data, int first);
-void zadd_rdata_txt_clean_wireformat(void);
-void zadd_rdata_svcb_check_wireformat(void);
-void zadd_rdata_domain(domain_type *domain);
-
-void set_bitnsec(uint8_t  bits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE],
-		 uint16_t index);
-uint16_t *alloc_rdata_init(region_type *region, const void *data, size_t size);
-
-/* zparser.y */
-zparser_type *zparser_create(region_type *region, region_type *rr_region,
-			     namedb_type *db);
-void zparser_init(const char *filename, uint32_t ttl, uint16_t klass,
-		  const dname_type *origin);
-
-/* parser start and stop to parse a zone */
-void zonec_setup_parser(namedb_type* db);
-void zonec_desetup_parser(void);
 /* parse a zone into memory. name is origin. zonefile is file to read.
  * returns number of errors; failure may have read a partial zone */
-unsigned int zonec_read(const char *name, const char *zonefile, zone_type* zone);
-/* parse a string into the region. and with given domaintable. global parser
- * is restored afterwards. zone needs apex set. returns last domain name
- * parsed and the number rrs parse. return number of errors, 0 is success.
- * The string must end with a newline after the RR. */
-int zonec_parse_string(region_type* region, domain_table_type* domains,
-	zone_type* zone, char* str, domain_type** parsed, int* num_rrs);
+unsigned int zonec_read(
+	struct namedb *database,
+	struct domain_table *domains,
+	const char *name,
+	const char *zonefile,
+	struct zone *zone);
+
 /** check SSHFP type for failures and emit warnings */
 void check_sshfp(void);
 void apex_rrset_checks(struct namedb* db, rrset_type* rrset,
Index: dnstap/dnstap.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/dnstap/dnstap.c,v
diff -u -p -r1.6 dnstap.c
--- dnstap/dnstap.c	29 Jun 2023 19:38:50 -0000	1.6
+++ dnstap/dnstap.c	3 Sep 2025 13:53:32 -0000
@@ -404,7 +404,11 @@ dt_tls_writer_open(void* obj)
 		log_crypto_err("SSL verification failed");
 		return fstrm_res_failure;
 	}
+#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
+	x = SSL_get1_peer_certificate(dtw->ssl);
+#else
 	x = SSL_get_peer_certificate(dtw->ssl);
+#endif
 	if(!x) {
 		log_crypto_err("Server presented no peer certificate");
 		return fstrm_res_failure;
Index: dnstap/dnstap_collector.c
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/dnstap/dnstap_collector.c,v
diff -u -p -r1.5 dnstap_collector.c
--- dnstap/dnstap_collector.c	20 Dec 2023 17:29:02 -0000	1.5
+++ dnstap/dnstap_collector.c	3 Sep 2025 13:53:32 -0000
@@ -430,6 +430,9 @@ void dt_collector_start(struct dt_collec
 #ifdef HAVE_SETPROCTITLE
 		setproctitle("dnstap_collector");
 #endif
+#ifdef USE_LOG_PROCESS_ROLE
+                log_set_process_role("dnstap_collector");
+#endif
 		/* Free serve process specific memory pages */
 #ifdef RATELIMIT
 		rrl_mmap_deinit_keep_mmap();
Index: doc/ChangeLog
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/doc/ChangeLog,v
diff -u -p -r1.18 ChangeLog
--- doc/ChangeLog	12 Apr 2024 15:53:34 -0000	1.18
+++ doc/ChangeLog	3 Sep 2025 13:53:32 -0000
@@ -1,9 +1,237 @@
+5 December 2024: Willem
+	- The tag for the 4.11.0rc1 release became the release 4.11.0,
+	  on 12 December 2024, the code repository continues with 4.11.1 under
+	  development.
+
+4 December 2024: Willem
+	- Merge #407: Better balanced verbosity levels for logging.
+
+26 November 2024: Jeroen
+	- Fix #405: Fix typo in documentation. Thanks to @uplime for reporting.
+
+19 November 2024: Willem
+	- Merge #409: Writing of NSAP-PTR, GPOS and HIP RR types
+
+16 November 2024: Willem
+	- Merge #408: NINFO, RKEY, RESINFO, WALLET, CLA and TA RR types
+
+12 November 2024: Willem
+	- Merge #406: ohttp and tls-supported-groups SvcParam support
+
+7 November 2024: Willem
+	- Merge #398: RFC 9660 The DNS Zone Version (ZONEVERSION) Option
+
+4 November 2024: Wouter
+	- Fix Makefile for parallel build failure around bison rule.
+
+28 October 2024: Jeroen
+	- Merge #404: Introducing Sphinx substitution in code blocks.
+	  As well as other fixes with Sphinx build.
+	- Merge #391: Update Copyright lines in help output
+	- Merge #395: Explain zonefile example better
+	- Merge #394: Fix doc path (fixes "Edit on GitHub" button in the docs)
+	- Many thanks to Melroy van den Berg for the documentation improvements
+	  and fixes in PRs #391, #394, #395 and #404
+
+28 October 2024: Jeroen
+	- Fix #396: Treat a mismatch in RRset TTLs as a warning.
+
+24 October 2024: Wouter
+	- Fix #392: Inconsistent documentation about control-interface.
+	- Merge #395: Explain the zonefile example better.
+	- Merge #394: Fix the path to use doc/manual/.
+	- Fix analyzer issue in do_print_cookie_secrets to check for failure.
+
+23 October 2024: Willem
+	- Fix #196 and merge #378: Updated cookie secrets management.
+	  The default cookie secret file location can be set at compile time
+	  with the --with-cookiesecretsfile=path option to configure. The
+	  default location is changed to {dbdir}/cookiesecrets.txt. The
+	  previous default location will be checked at startup when there is
+	  no cookie secrets file at the new default location.
+	  A staging cookie can now also be configured in the configuration
+	  file and secrets configured in the configuration file now take
+	  precedence over those read from file.
+	  All DNS related setting in the configuration file will be reevaluated
+	  and effectuated after nsd-control reconfig.
+	- Merge #390: Apply non-xfr tasks before xfr tasks.
+	  This fixes an issue where non-xfr tasks are lost when they are
+	  batch processed together with non-xfr tasks.
+	  This merge also changes that notifies are passed on from the serve
+	  processes to the xfrd directly instead of via main. This was
+	  necessary to allow applying the non-xfr tasks without forking a
+	  backup-main for the sole purpose of forwarding notifies.
+
+23 October 2024: Wouter
+	- Merge #391: Update copyright lines (in version output).
+
+18 October 2024: Wouter
+	- Fix ci to update macos-12 to the macos-15 runner image.
+
+25 September 2024: Wouter
+	- Fix #383: log timestamps in ISO8601 format with timezone.
+	  This adds the option `log-time-iso: yes` that logs in ISO8601
+	  format.
+	- Fix to initialize log_time_iso value in options.
+
+23 August 2024: Jeroen
+	- Fix #57: Track $INCLUDEs in zone files
+
+23 August 2024: Wouter
+	- Merge #376: Point the user towards tcpdump for logging individual
+	  queries.
+
+22 August 2024: Wouter
+	- Add cross platform freebsd, openbsd and netbsd to github ci.
+	- Update simdzone to include fix for netbsd double bswap declarations,
+	  and also semantic checks for DS and ZONEMD. And CFLAGS has -march
+	  prepended to fix detection.
+
+19 August 2024: Wouter
+	- Fix title underline and declaration after statement warnings.
+
+15 August 2024: Jeroen
+	- Support reloading configuration on SIGHUP.
+
+2 August 2024: Jeroen
+	- Update simdzone to fix detection of AVX2 support.
+
+31 July 2024: Jeroen
+	- Merge #367: Specify protocols used in NOTIFY.
+	- Merge #368: Various manpage fixes.
+
+30 July 2024: Jeroen
+	- Merge #366: Be consistent in naming primary/secondary zones.
+
+25 July 2024: Jeroen
+	- Merge #358 from @jas4711: Fix Hurd build error due to log_Err.
+
+23 July 2024: Jeroen
+	- Update simdzone.
+	- Tag for 4.10.1rc2.
+
+23 July 2024: Jeroen
+	- Update simdzone.
+	- Tag for 4.10.1rc1.
+
+22 July 2024: Jeroen
+	- Initialize tls_auth_port and tls_auth_xfr_only options.
+
+22 July 2024: Wouter
+	- Fix link of xfr-inspect for libssl dependency.
+
+19 July 2024: Willem
+	- Do not log ACL mismatch on followed CNAMEs.
+
+18 July 2024: Jeroen
+	- Fix propagation of Makefile targets to simdzone.
+
+18 July 2024: Wouter
+	- Merge #337 from bilias: Mutual TLS-AUTH.
+
+16 July 2024: Wouter
+	- Merge #352 from orlitzky: contrib: add OpenRC service script, config
+	  file, and tmpfiles entry.
+
+12 July 2024: Jeroen
+	- Use MAKE variable rather than make command.
+	  On platforms where make is not the same make executing the Makefile,
+	  build issues might arise. The MAKE variable expands to the command
+	  used on the command line.
+
+11 July 2024: Jeroen
+	- Serialize WKS RRs using numeric values rather than names.
+	  simdzone does not use getprotobyname and getservbyname. Secondary
+	  zones received via IXFR/AXFR might fail to load because WKS RRs
+	  used "unsupported" protocols or services.
+
+8 July 2024: Wouter
+	- Merge #349: log file name before loading.
+
+5 July 2024: Wouter
+	- Fix #347: Adjust verbosity for TLS (+TCP) to be 5.
+	- Merge #348: Move TLS logging to verbosity level 5.
+	- For #347: Also adjust verbosity of log message for remaining TCP
+	  connections.
+
+27 June 2024: Wouter
+	- Fix #344: Update simdzone.
+
+26 June 2024: Wouter
+	- Fix test script from making spurious output.
+	- Fix cpu_affinity and socket_partitioning tests for --enable-log-role.
+
+24 June 2024: Wouter
+	- Add unit test for #343 relative include in zone file.
+
+20 June 2024: Wouter
+	- Merge #341: Fix allow-query wording in nsd.conf.5.in.
+
+17 June 2024: Wouter
+	- Fix for OpenSSL 3.0 deprecated functions.
+
+11 June 2024: Wouter
+	- Fix #334: RFC8482 behavior documentation.
+
+23 May 2024: Wouter
+	- Fix for #317, document more text on pidfile permissions.
+
+29 April 2024: Wouter
+	- Fix incorrect punctuation of log messages.
+
+25 April 2024: Wouter
+	- The tag for the 4.10.0rc1 release became the release 4.10.0,
+	  on 13 June 2024, the code repository continues with 4.10.1 under
+	  development. The release contains the additional fix to change
+	  simdzone version.
+	- Bump simdzone to include minor fixes. This is included in 4.10.0.
+
+25 April 2024: Jeroen
+	- Bump simdzone to fix OpenBSD build issues.
+	- Tag for 4.10.0rc1.
+
+24 April 2024: Wouter
+	- Fix that the reload handler for sigchild uses signal_add, and
+	  also that the signal handler is restored when done.
+	- Fix that when server verify is done it resets the sigchild handler.
+	- Fix makedist.sh for simdzone inclusion.
+	- Fix makedist.sh to remove simdzone git tracking information and
+	  scripting temporaries from tarball.
+	- Fix error output of makedist.sh.
+
+23 April 2024: Wouter
+	- Fix #329: TCP accept queues number.
+
+22 April 2024: Jeroen
+	- Use simdzone version with name parser fix.
+
+16 April 2024: Jeroen
+	- Replace Flex+Bison based zone parser with simdzone.
+
+15 April 2024: Wouter
+	- Unit test for dname subdomain test used by xfrd-tcp.c.
+
+9 April 2024: Wouter
+	- Fix IXFR requests upstream for zones with a long name. Thanks for
+	  the report to Yuuki Wakisaka from Internet Initiative Japan Inc.
+
+8 April 2024: Wouter
+	- For #317: Modify nsd service script to stop NSD from creating a
+	  pid file that systemd is not using.
+	- Fix #324: Clarify the purpose of contrib/bug390.patch.
+
 4 April 2024: Jeroen
 	- Use rooted temporary path in makedist.sh.
+	- Tag for 4.9.1.
 
 3 April 2024: Jeroen
 	- Replace multiple strcat and strcpy by snprintf.
 	- Tag for 4.9.0.
+
+27 March 2024: Wouter
+	- Fix that when the server truncates the pidfile, it does not follow
+	  symbolic links.
+	- Fix #317: nsd should not chown its PID file.
 
 26 March 2024: Jeroen
 	- Test if debug is available in do-tests.
Index: doc/README
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/doc/README,v
diff -u -p -r1.8 README
--- doc/README	12 Apr 2024 15:53:34 -0000	1.8
+++ doc/README	3 Sep 2025 13:53:32 -0000
@@ -21,7 +21,7 @@
 
 1.0 Introduction
 
-This is NSD Name Server Daemon (NSD) version 4.9.1.
+This is NSD Name Server Daemon (NSD) version 4.11.0.
 
 The NLnet Labs Name Server Daemon (NSD) is an authoritative RFC compliant 
 DNS nameserver. It was first conceived to allow for more genetic 
@@ -57,7 +57,7 @@ and uses a simple configuration file 'ns
 
 1.2 Quick build and install
 
-Step 1: Unpack the source with gtar -xzvf nsd-4.9.1.tar.gz
+Step 1: Unpack the source with gtar -xzvf nsd-4.11.0.tar.gz
 
 Step 2: Create user nsd or any other unprivileged user of your
         choice. In case of later make sure to use
@@ -111,9 +111,9 @@ Step 11: If desired add 'nsd-control wri
 Use your favorite combination of tar and gnu zip to unpack the source,
 for example
 
-$ gtar -xzvf nsd-4.9.1.tar.gz
+$ gtar -xzvf nsd-4.11.0.tar.gz
 
-will unpack the source into the ./nsd-4.9.1 directory...
+will unpack the source into the ./nsd-4.11.0 directory...
 
 
 2.2 Configuring NSD
@@ -920,4 +920,4 @@ larger and regular donations please cont
 see http://www.nlnetlabs.nl/labs/contributors/.
 
 
-$Id: README,v 1.8 2024/04/12 15:53:34 florian Exp $
+$Id$
Index: doc/RELNOTES
===================================================================
RCS file: /cvs/src/usr.sbin/nsd/doc/RELNOTES,v
diff -u -p -r1.16 RELNOTES
--- doc/RELNOTES	12 Apr 2024 15:53:34 -0000	1.16
+++ doc/RELNOTES	3 Sep 2025 13:53:32 -0000
@@ -1,5 +1,123 @@
 NSD RELEASE NOTES
 
+4.11.0
+================
+FEATURES:
+	- Support reloading configuration on SIGHUP.
+	- Fix #383: log timestamps in ISO8601 format with timezone.
+	  This adds the option `log-time-iso: yes` that logs in ISO8601
+	  format.
+	- Updated cookie secrets management.
+	  The default cookie secret file location can be set at compile time
+	  with the --with-cookiesecretsfile=path option to configure. The
+	  default location is changed to {dbdir}/cookiesecrets.txt. The
+	  previous default location will be checked at startup when there is
+	  no cookie secrets file at the new default location.
+	  A staging cookie can now also be configured in the configuration
+	  file and secrets configured in the configuration file now take
+	  precedence over those read from file.
+	  All DNS related setting in the configuration file will be reevaluated
+	  and effectuated after nsd-control reconfig.
+	- Merge #398: RFC 9660 The DNS Zone Version (ZONEVERSION) Option
+	- Merge #406: ohttp and tls-supported-groups SvcParam suppor
+	- Merge #408: NINFO, RKEY, RESINFO, WALLET, CLA and TA RR types
+	- Merge #409: Writing of NSAP-PTR, GPOS and HIP RR types
+	- Merge #407: Better balanced verbosity levels for logging.
+
+BUG FIXES:
+	- Fix title underline and declaration after statement warnings.
+	- Add cross platform freebsd, openbsd and netbsd to github ci.
+	- Update simdzone to include fix for netbsd double bswap declarations,
+	  and also semantic checks for DS and ZONEMD. And CFLAGS has -march
+	  prepended to fix detection.
+	- Merge #376: Point the user towards tcpdump for logging individual
+	  queries.
+	- Track $INCLUDEs in zone files.
+	- Fix ci to update macos-12 to the macos-15 runner image.
+	- Merge #390: Apply non-xfr tasks before xfr tasks.
+	  This fixes an issue where non-xfr tasks are lost when they are
+	  batch processed together with non-xfr tasks.
+	  This merge also changes that notifies are passed on from the serve
+	  processes to the xfrd directly instead of via main. This was
+	  necessary to allow applying the non-xfr tasks without forking a
+	  backup-main for the sole purpose of forwarding notifies.
+	- Merge #391: Update copyright lines (in version output).
+	- Fix #392: Inconsistent documentation about control-interface.
+	- Merge #395: Explain the zonefile example better.
+	- Merge #394: Fix the path to use doc/manual/.
+	- Fix analyzer issue in do_print_cookie_secrets to check for failure.
+	- Merge #404: Introducing Sphinx substitution in code blocks.
+	  As well as other fixes with Sphinx build.
+	- Update Copyright lines in help output
+	- Merge #395: Explain zonefile example better
+	- Merge #394: Fix doc path (fixes "Edit on GitHub" button in the docs)
+	- Fix Makefile for parallel build failure around bison rule.
+	- Fix #405: Fix typo in documentation.
+	- Treat a mismatch in RRset TTLs as a warning.
+
+4.10.1
+================
+FEATURES:
+	- Merge #352 from orlitzky: contrib: add OpenRC service script, config
+	  file, and tmpfiles entry.
+	- Merge #337 from bilias: Mutual TLS-AUTH.
+
+BUG FIXES:
+	- Fix incorrect punctuation of log messages.
+	- Fix for #317, document more text on pidfile permissions.
+	- Fix #334: RFC8482 behavior documentation.
+	- Fix for OpenSSL 3.0 deprecated functions.
+	- Merge #341: Fix allow-query wording in nsd.conf.5.in.
+	- Fix test script from making spurious output.
+	- Fix cpu_affinity and socket_partitioning tests for --enable-log-role.
+	- Fix #344: Update simdzone.
+	- Fix #347: Adjust verbosity for TLS (+TCP) to be 5.
+	- Merge #348: Move TLS logging to verbosity level 5.
+	- For #347: Also adjust verbosity of log message for remaining TCP
+	  connections.
+	- Merge #349: log file name before loading.
+	- Use MAKE variable rather than make command directly in Makefile.
+	- Serialize WKS RRs using numeric values rather than names.
+	- Fix propagation of Makefile targets to simdzone.
+	- Do not log ACL mismatch on followed CNAMEs.
+	- Fix link of xfr-inspect for libssl dependency.
+	- Initialize tls_auth_port and tls_auth_xfr_only options.
+	- Merge #358: Fix Hurd build error due to log_err.
+	- Update simdzone to fix detection of AVX2 support.
+
+4.10.0
+================
+FEATURES:
+	- Merge #278: Replace Flex+Bison based zone parser with simdzone.
+	  Performance of loading zones and IXFRs is greatly improved by using
+	  the simdzone project by NLnet Labs. The optimized presentation format
+	  parser leverages SIMD instructions in modern CPUs to improve throughput.
+	  Right now SSE4.2 and AVX2 instruction sets are supported, other
+	  instruction sets will use the fallback implementation, which still is
+	  a decent improvement over the Flex+Bison based parser.
+
+BUG FIXES:
+	- Fix that when the server truncates the pidfile, it does not follow
+	  symbolic links.
+	- Fix #317: nsd should not chown its PID file.
+	- For #317: Modify nsd service script to stop NSD from creating a
+	  pid file that systemd is not using.
+	- Fix #324: Clarify the purpose of contrib/bug390.patch.
+	- Fix IXFR requests upstream for zones with a long name. Thanks for
+	  the report to Yuuki Wakisaka from Internet Initiative Japan Inc.
+	- Unit test for dname subdomain test used by xfrd-tcp.c.
+	- Fix #329: TCP accept queues number.
+	- Fix that the reload handler for sigchild uses signal_add, and
+	  also that the signal handler is restored when done.
+	- Fix that when server verify is done it resets the sigchild handler.
+	- Fix makedist.sh for simdzone inclusion.
+	- Fix makedist.sh to remove simdzone git tracking information and
+	  scripting temporaries from tarball.
+	- Fix error output of makedist.sh.
+	- Use simdzone version with name parser fix.
+	- Bump simdzone version to fix OpenBSD build issues.
+	- Bump simdzone to include minor fixes.
+
 4.9.1
 ================
 BUG FIXES:
Index: simdzone/CHANGELOG.md
===================================================================
RCS file: simdzone/CHANGELOG.md
diff -N simdzone/CHANGELOG.md
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/CHANGELOG.md	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,61 @@
+# Changelog
+
+All notable changes to simdzone will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [0.2.0] - 2024-12-12
+
+### Added
+
+- Add semantic checks for DS and ZONEMD digests (NLnetLabs/nsd#205).
+- Support registering a callback for $INCLUDE entries (NLnetLabs/nsd#229).
+- Add tls-supported-groups SvcParam support.
+- Check iana registries for unimplemented (new) RR types and SvcParamKeys.
+- Add support for NINFO, RKEY, RESINFO, WALLET, CLA and TA RR types.
+
+### Fixed
+
+- Prepend -march to CFLAGS to fix architecture detection (NLnetLabs/nsd#372).
+- Fix propagation of implicit TTLs (NLnetLabs/nsd#375).
+- Fix detection of Westmere architecture by checking for CLMUL too.
+- Fix compilation on NetBSD (#233).
+- Fix reading specialized symbolic links (NLnetLabs/nsd#380).
+
+## [0.1.1] - 2024-07-19
+
+### Added
+
+- Test to verify configure.ac and Makefile.in are correct.
+- Add support for reading from stdin if filename is "-".
+- Add support for building with Oracle Developer Studio 12.6.
+- Add support for "time" service for Well-Know Services (WKS) RR.
+
+### Fixed
+
+- Fix makefile dependencies.
+- Fix makefile to use source directory for build dependencies.
+- Fix changelog to reflect v0.1.0 release.
+- Update makefile to not use target-specific variables.
+- Fix makefile clean targets.
+- Fix state keeping in fallback scanner for contiguous and quoted.
+- Fix bug in name scanner.
+- Fix type mnemonic parsing in fallback parser.
+- Fix endian.h to include machine/endian.h on OpenBSD releases before 5.6.
+- Fix use after free on buffer resize.
+- Fix parsing of numeric protocols in WKS RRs.
+- Make devclean target depend on realclean target.
+- Fix detection of AVX2 support by checking generic AVX support by the
+  processor and operating system (#222).
+
+### Changed
+
+- Make relative includes relative to current working directory.
+- Split Autoconf and CMake compiler tests for supported SIMD instructions.
+
+## [0.1.0] - 2024-04-16
+
+### Added
+
+- Initial release.
Index: simdzone/CONTRIBUTING.md
===================================================================
RCS file: simdzone/CONTRIBUTING.md
diff -N simdzone/CONTRIBUTING.md
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/CONTRIBUTING.md	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,75 @@
+# Contributing to simdzone
+
+The simdzone library is open source and made available under the permissive
+3-clause BSD license.
+
+Contributions are very welcome!
+
+> The original specification in [RFC1035][1] is rather ambiguous and does not
+> cover additions from later RFCs. See [SYNTAX.md](SYNTAX.md) for a quick
+> summary of the format and interpretation in simdzone.
+
+[1]: https://datatracker.ietf.org/doc/html/rfc1035#section-5
+
+## Reference data
+
+1. [Zone Data for .se and .nu][2] can be obtained via a DNS zone transfer.
+
+2. The [Centralized Zone Data Service (CZDS)][3] provides access to zone data
+   for participating gTLDs.
+
+   > Downloading zone data via the browser can be problematic. The
+   > [The CZDS API client in Java][4] can be used as a workaround.
+
+3. The *Hint and Zone Files* can be obtained from Internet Assigned Numbers
+   Authority (IANA) [Root Zone Management][5] page.
+
+[2]: https://internetstiftelsen.se/en/zone-data/
+[3]: https://czds.icann.org/
+[4]: https://github.com/icann/czds-api-client-java/
+[5]: https://www.iana.org/domains/root
+
+## Source layout
+
+`include` contains only headers required for consumption of the library.
+
+`src` contains the implementation and internal headers.
+
+The layout of `src` is (of course) inspired by the layout in simdjson. The
+structure is intentionally simple and without (too much) hierarchy, but as
+simdzone has very architecture specific code to maximize performance, there
+are some caveats.
+
+Processors may support multiple instruction sets. e.g. x86\_64 may support
+SSE4.2, AVX2 and AVX-512 instruction sets depending on the processor family.
+The preferred implementation is automatically selected at runtime. As a result,
+code may need to be compiled more than once. To improve code reuse, shared
+logic resides in headers rather than source files and is declared static to
+avoid name clashes. Bits and pieces are then mixed and matched in a
+`src/<arch>/parser.c` compilation target to allow for multiple implementations
+to co-exist.
+
+Sources and headers common to all architectures that do not implement parsing
+for a specific data-type reside directly under `src`. Code specific to an
+architecture resides in a directory under `src`, e.g. `haswell` or `fallback`.
+`src/generic` contains scanner and parser code common to all implementations,
+but leans towards code shared by SIMD implementations.
+
+For example, SIMD-optimized scanner code resides in `src/generic/scanner.h`,
+abstractions for intrinsics reside in e.g. `src/haswell/simd.h` and `lex(...)`,
+which is used by all implementations, is implemented in `src/lexer.h`.
+A fallback scanner is implemented in `src/fallback/scanner.h`.
+
+A SIMD-optimized type parser is implemented in `src/generic/type.h`, a fallback
+type parser is implemented in `src/fallback/type.h`. Future versions are
+expected to add more optimized parsers for specific data types, even parsers
+that are tied to a specific instruction set. The layout accommodates these
+scenarios. e.g. an AVX2 optimized parser may reside in `src/haswell/<type>.h`,
+an SSE4.2 optimized parser may reside in `src/westmere/<type>.h`, etc.
+
+## Symbol visibility
+
+All exported symbols, identifiers, etc must be prefixed with `zone_`, or
+`ZONE_` for macros. Non-exported symbols are generally not prefixed. e.g.
+`lex(...)` and `scan(...)` are declared static and as such are not required to
+be prefixed.
Index: simdzone/FORMAT.md
===================================================================
RCS file: simdzone/FORMAT.md
diff -N simdzone/FORMAT.md
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/FORMAT.md	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,311 @@
+# Zone files
+
+Zone files are text files that contain resource records (RRs) in text form.
+Zones can be defined by expressing them in the form of a list of RRs.
+
+Zone files were originally specified in RFC1035 Section 5, but the DNS
+has seen many additions since and the specification is rather ambiguous.
+Consequently, various name servers implement slightly different dialects. This
+document aims to clarify the format by listing (some of) the relevant
+specifications and then proceed to explain why certain design decisions were
+made in simdzone.
+
+* [RFC 1034 Section 3.6.1][rfc1034#3.6.1]
+* [RFC 1035 Section 5][rfc1035#5]
+* [RFC 2065 Section 4.5][rfc2065#4.5]
+* [RFC 2181 Section 8][rfc2181#8]
+* [RFC 2308 Section 4][rfc2308#4]
+* [RFC 3597 Section 5][rfc3597#5]
+* [RFC 9460 Section 2.1][rfc9460#2.1]
+
+
+## Clarification (work-in-progress)
+
+> NOTE: BIND behavior is more-or-less considered the de facto standard.
+
+Historically, master files where edited by hand, which is reflected in the
+syntax. Consider the format a tabular serialization format with provisions
+for convenient editing. i.e. the owner, class and ttl fields may be omitted
+(provided the line starts with \<blank\> for the owner) and $INCLUDE directives
+can be used for templating.
+
+The format is NOT context-free. The field following the owner (if specified)
+may represent either a type, class or ttl and a symbolic constant, e.g. A
+or NS, may have a different meaning if specified as an RDATA field.
+
+The DNS is intentionally extensible. The specification is not explicit about
+how that affects syntax, but it explains why no specific notation for
+data-types can be enforced by RFC 1035. To make it easier for data-types to
+be added at a later stage the syntax cannot enforce a certain notation (or
+the scanner would need to be revised). Consequently, the scanner only
+identifies items (or fields) and structural characters, which can be
+expressed as either a contiguous set of characters without interior spaces,
+or as a quoted string.
+
+The format allows for including structural characters in fields by means of
+escaping the actual character or enclosing the field in quotes. The example
+provided by the specification here is using ASCII dots in domain name labels.
+The dot is normally a label separator, replaced by the length of the label
+on the wire. If a domain name includes an actual ASCII dot, the character
+must be escaped in the textual representation (`\X` or `\DDD`).
+
+Note that ASCII dot characters strictly speaking do not have to be escaped
+in a quoted string. RFC 1035 clearly states labels in domain names are
+expressed as character strings. However, behavior differs across
+implementations, so support for quoted labels is best dropped (see below).
+
+RFC 1035 states both \<contiguous\> and \<quoted\> are \<character-string\>.
+Meaning, items can be either \<contiguous\> or \<quoted\>. Wether a specific
+item is interpreted as a \<character-string\> depends on type of value for
+that item. E.g., TTLs are decimal integers and therefore cannot be expressed
+as \<quoted\> as it is not a \<character-string\>. Similarly, base64
+sequences are encoded binary blobs, not \<character-string\>s and therefore
+cannot be expressed as such. Escape sequences are valid only in
+\<character-string\>s.
+
+* Mnemonics are NOT character strings.
+
+  > BIND does not accept quoted fields for A or NS RDATA. TTL values in SOA
+  > RDATA, base64 Signature in DNSKEY RDATA, as well as type, class and TTL
+  > header fields all result in a syntax error too if quoted.
+
+* Some integer fields allow for using mnemonics too. E.g., the algorithm
+  field in RRSIG records.
+
+* RFC 1035 states: A freestanding @ denotes the current origin.
+  There has been discussion in which locations @ is interpreted as the origin.
+  e.g. how is a freestanding @ be interpreted in the RDATA section of a TXT RR.
+  Note that there is no mention of text expansion in the original text. A
+  freestanding @ denotes the origin. As such, it stands to reason that it's
+  use is limited to locations where domain names are expressed, which also
+  happens to be the most practical way to implement the functionality.
+
+  > This also seems to be the behavior that other name servers implement (at
+  > least BIND and PowerDNS). The BIND manual states: "When used in the label
+  > (or name) field, the asperand or at-sign (@) symbol represents the current
+  > origin. At the start of the zone file, it is the \<zone\_name\>, followed
+  > by a trailing dot (.).
+
+  > It may also make sense to interpret a quoted freestanding @ differently
+  > than a non-quoted one. At least, BIND throws an error if a quoted
+  > freestanding @ is encountered in the RDATA sections for CNAME and NS RRs.
+  > However, a quoted freestanding @ is accepted and interpreted as origin
+  > if specified as the OWNER.
+
+  > Found mentions of what happens when a zone that uses freestanding @ in
+  > RDATA is written to disk. Of course, this particular scenario rarely occurs
+  > as it does not need to be written to disk when loaded on a primary and no
+  > file exists if received over AXFR/IXFR. However, it may make sense to
+  > implement optimistic compression of this form, and make it configurable.
+
+* Class and type names are mutually exclusive in practice.
+  RFC1035 states: The RR begins with optional TTL and class fields, ...
+  Therefore, if a type name matches a class name, the parser cannot distinguish
+  between the two in text representation and must resort to generic notation
+  (RFC3597) or, depending on the RDATA format for the record type, a
+  look-ahead may be sufficient. Realistically, it is highly likely that because
+  of this, no type name will ever match a class name.
+
+  > This means both can reside in the same table.
+
+* The encoding is non-ASCII. Some characters have special meaning, but users
+  are technically allowed to put in non-printable octets outside the ASCII
+  range without custom encoding. Of course, this rarely occurs in practice
+  and users are encouraged to use the \DDD encoding for "special".
+
+* Parenthesis may not be nested.
+
+* $ORIGIN must be an absolute domain.
+
+* Escape sequences must NOT be unescaped in the scanner as is common with
+  programming languages like C that have a preprocessor. Instead, the
+  original text is necessary in the parsing stage to distinguish between
+  label separators (dots).
+
+* RFC 1035 specifies that the current origin should be restored after an
+  $INCLUDE, but it is silent on whether the current domain name should also be
+  restored. BIND 9 restores both of them. This could be construed as a
+  deviation from RFC 1035, a feature, or both.
+
+* RFC 1035 states: and text literals can contain CRLF within the text.
+  BIND, however, does not allow newlines in text (escaped or not). For
+  performance reasons, we may adopt the same behavior as that would relieve
+  the need to keep track of possibly embedded newlines.
+
+* From: http://www.zytrax.com/books/dns/ch8/include.html (mentioned in chat)
+  > Source states: The RFC is silent on the topic of embedded `$INCLUDE`s in
+  > `$INCLUDE`d files - BIND 9 documentation is similarly silent. Assume they
+  > are not permitted.
+
+  All implementations, including BIND, allow for embedded `$INCLUDE`s.
+  The current implementation is such that (embedded) includes are allowed by
+  default. However, `$INCLUDE` directives can be disabled, which is useful
+  when parsing from an untrusted source. There is also protection against
+  cyclic includes.
+
+  > There is no maximum to the amount of embedded includes (yet). NSD limits
+  > the number of includes to 10 by default (compile option). For security, it
+  > must be possible to set a hard limit.
+
+* Default values for TTLs can be quite complicated.
+
+  A [commit to ldns](https://github.com/NLnetLabs/ldns/commit/cb101c9) by
+  @wtoorop nicely sums it up in code.
+
+  RFC 1035 section 5.1:
+  > Omitted class and TTL values are default to the last explicitly stated
+  > values.
+
+  This behavior is updated by RFC 2308 section 4:
+  > All resource records appearing after the directive, and which do not
+  > explicitly include a TTL value, have their TTL set to the TTL given
+  > in the $TTL directive.  SIG records without a explicit TTL get their
+  > TTL from the "original TTL" of the SIG record [RFC 2065 Section 4.5].
+
+  The TTL rules for `SIG` RRs stated in RFC 2065 Section 4.5:
+  > If the original TTL, which applies to the type signed, is the same as
+  > the TTL of the SIG RR itself, it may be omitted.  The date field
+  > which follows it is larger than the maximum possible TTL so there is
+  > no ambiguity.
+
+  The same applies applies to `RRSIG` RRs, although not stated as explicitly
+  in RFC 4034 Section 3:
+  > The TTL value of an RRSIG RR MUST match the TTL value of the RRset it
+  > covers.  This is an exception to the [RFC2181] rules for TTL values
+  > of individual RRs within a RRset: individual RRSIG RRs with the same
+  > owner name will have different TTL values if the RRsets they cover
+  > have different TTL values.
+
+  Logic spanning RRs must not be handled during deserialization. The order in
+  which RRs appear in the zone file is not relevant and keeping a possibly
+  infinite backlog of RRs to handle it "automatically" is inefficient. As
+  the name server retains RRs in a database already it seems most elegant to
+  signal the TTL value was omitted and a default was used so that it may be
+  updated in some post processing step.
+
+  [RFC 2181 Section 8][rfc2181#8] contains additional notes on the maximum
+  value for TTLs. During deserialization, any value exceeding 2147483647 is
+  considered an error in primary mode, or a warning in secondary mode.
+  [RFC 8767 Section 4][rfc8767#4] updates the text, but the update does not
+  update handling during deserialization.
+
+  [RFC 2181 Section 5][rfc2181#5.2] states the TTLs of all RRs in an RRSet
+  must be the same. As with default values for `SIG` and `RRSIG` RRs, this
+  must NOT be handled during deserialization. Presumably, the application
+  should transparently fix TTLs (NLnetLabs/nsd#178).
+
+* Do NOT allow for quoted labels in domain names.
+  [RFC 1035 Section 5][rfc1035#5] states:
+  > The labels in the domain name are expressed as character strings and
+  > separated by dots.
+
+  [RFC 1035 section 5][rfc1035#5] states:
+  > \<character-string\> is expressed in one or two ways: as a contiguous set
+  > of characters without interior spaces, or as string beginning with a " and
+  > ending with a ".
+
+  However, quoted labels in domain names are very uncommon and implementations
+  handle quoted names both in OWNER and RDATA very differently. The Flex+Bison
+  based parser used in NSD before was the only parser that got it right.
+
+  * BIND
+    * owner: yes, interpreted as quoted
+      ```
+      dig @127.0.0.1 A quoted.example.com.
+      ```
+      ```
+      quoted.example.com.  xxx  IN  A  x.x.x.x
+      ```
+    * rdata: no, syntax error (even with `check-names master ignored;`)
+  * Knot
+    * owner: no, syntax error
+    * rdata: no, syntax error
+  * PowerDNS
+    * owner: no, not interpreted as quoted
+      ```
+      pdnsutil list-zone example.com.
+      ```
+      ```
+      "quoted".example.com  xxx  IN  A  x.x.x.x
+      ```
+    * rdata: no, not interpreted as quoted
+      ```
+      dig @127.0.0.1 NS example.com.
+      ```
+      ```
+      example.com.  xxx  IN  NS  \"quoted.example.com.\".example.com.
+      ```
+
+  > [libzscanner](https://github.com/CZ-NIC/knot/tree/master/src/libzscanner),
+  > the (standalone) zone parser used by Knot seems mosts consistent.
+
+  Drop support for quoted labels or domain names for consistent behavior.
+
+* Should any domain names that are not valid host names as specified by
+  RFC 1123 section 2, i.e. use characters not in the preferred naming syntax
+  as specified by RFC 1035 section 2.3.1, be accepted? RFC 2181 section 11 is
+  very specific on this topic, but it merely states that labels may contain
+  characters outside the set on the wire, it does not address what is, or is
+  not, allowed in zone files.
+
+  BIND's zone parser throws a syntax error for any name that is not a valid
+  hostname unless `check-names master ignored;` is specified. Knot
+  additionally accepts `-`, `_` and `/` according to
+  [NOTES](https://github.com/CZ-NIC/knot/blob/master/src/libzscanner/NOTES).
+
+  * [RFC1035 Section 2.3.1][rfc1035#2.3.1]
+  * [RFC1123 Section 2][rfc1123#2]
+  * [RFC2181 Section 11][rfc2181#11]
+
+* RFC 1035 specifies two control directives "$INCLUDE" and "$ORIGIN". RFC 2308
+  specifies the "$TTL" directive. BIND additionally implements the "$DATE" and
+  "$GENERATE" directives. Since "$" (dollar sign) is not reserved, both
+  "$DATE" and "$GENERATE" (and "$TTL" before RFC2308) are considered valid
+  domain names in other implementations (based on what is accepted for domain
+  names, see earlier points). It seems "$" is better considered a reserved
+  character (possibly limiting its special status to the start of the
+  line), to allow for reliable extensibility in the future.
+
+  > BIND seems to already throw an error if "$" is encountered, see
+  > `lib/dns/master.c`. Presumably, the "$DATE" directive is written when the
+  > zone is written to disk(?) In the code it is referred to as
+  > __dump_time__ and later used to calculate __ttl_offset__.
+
+* BIND10 had a nice writeup on zone files, kindly provided by Shane Kerr.
+  [Zone File Loading Requirements on Wayback Machine](https://web.archive.org/web/20140928215002/http://bind10.isc.org:80/wiki/ZoneLoadingRequirements)
+
+* `TYPE0` is sometimes used for debugging and therefore may occur in type
+  bitmaps or as unknown RR type.
+
+* `pdns/master/regression-tests/zones/test.com` contains regression tests
+  that may be useful for testing simdzone.
+
+* Some implementations (Knot, possibly PowerDNS) will silently split-up
+  strings longer than 255 characters. Others (BIND, simdzone) will throw a
+  syntax error.
+
+* How do we handle the corner case where the first record does not have a TTL
+  when the file does not define a zone? (from @shane-kerr).
+
+  At this point in time, the application provides a default TTL value before
+  parsing. Whether that is the right approach is unclear, but it is what NSD
+  did before.
+
+* Leading zeroes in integers appear to be allowed judging by the zone file
+  generated for the [socket10kxfr][socket10kxfr.pre#L64] test in NSD. BIND
+  and Knot parsed it without problems too.
+
+[rfc1034#3.6.1]: https://datatracker.ietf.org/doc/html/rfc1034#section-3.6.1
+[rfc1035#5]: https://datatracker.ietf.org/doc/html/rfc1035#section-5
+[rfc1035#2.3.1]: https://datatracker.ietf.org/doc/html/rfc1035#section-2.3.1
+[rfc1123#2]: https://datatracker.ietf.org/doc/html/rfc1123#section-2
+[rfc2065#4.5]: https://datatracker.ietf.org/doc/html/rfc2065#section-4.5
+[rfc2181#5.2]: https://datatracker.ietf.org/doc/html/rfc2181#section-5.2
+[rfc2181#8]: https://datatracker.ietf.org/doc/html/rfc2181#section-8
+[rfc2181#11]: https://datatracker.ietf.org/doc/html/rfc2181#section-11
+[rfc2308#4]: https://datatracker.ietf.org/doc/html/rfc2308#section-4
+[rfc3597#5]: https://datatracker.ietf.org/doc/html/rfc3597#section-5
+[rfc8767#4]: https://www.rfc-editor.org/rfc/rfc8767#section-4
+[rfc9460#2.1]: https://datatracker.ietf.org/doc/html/rfc9460#section-2.1
+
+[socket10kxfr.pre#L64]: https://github.com/NLnetLabs/nsd/blob/86a6961f2ca64f169d7beece0ed8a5e1dd1cd302/tpkg/long/socket10kxfr.tdir/socket10kxfr.pre#L64
Index: simdzone/LICENSE
===================================================================
RCS file: simdzone/LICENSE
diff -N simdzone/LICENSE
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/LICENSE	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2022, NLnet Labs.
+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. 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.
Index: simdzone/Makefile.in
===================================================================
RCS file: simdzone/Makefile.in
diff -N simdzone/Makefile.in
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/Makefile.in	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,84 @@
+#
+# Makefile.in -- one file to make them all
+#
+# Copyright (c) 2022-2024, NLnet Labs. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+WESTMERE = @HAVE_WESTMERE@
+HASWELL = @HAVE_HASWELL@
+
+CC = @CC@
+CPPFLAGS = @CPPFLAGS@ -Iinclude -I$(SOURCE)/include -Isrc -I$(SOURCE)/src -I.
+CFLAGS = @CFLAGS@
+DEPFLAGS = @DEPFLAGS@
+VPATH = @srcdir@
+
+SOURCE = @srcdir@
+
+SOURCES = src/zone.c src/fallback/parser.c
+OBJECTS = $(SOURCES:.c=.o)
+
+WESTMERE_SOURCES = src/westmere/parser.c
+WESTMERE_OBJECTS = $(WESTMERE_SOURCES:.c=.o)
+
+HASWELL_SOURCES = src/haswell/parser.c
+HASWELL_OBJECTS = $(HASWELL_SOURCES:.c=.o)
+
+NO_OBJECTS =
+
+DEPENDS = $(SOURCES:.c=.d) $(WESTMERE_SOURCES:.c=.d) $(HASWELL_SOURCES:.c=.d)
+
+# The export header automatically defines visibility macros. These macros are
+# required for standalone builds on Windows. I.e., exported functions must be
+# declared with __declspec(dllexport) for dynamic link libraries (.dll) and
+# __declspec(dllimport) for statically linked libraries (.lib). Define dummy
+# macros for compatibility.
+EXPORT_HEADER = include/zone/export.h
+
+.PHONY: all clean
+
+all: libzone.a
+
+clean:
+	@rm -f .depend
+	@rm -f libzone.a $(OBJECTS) $(EXPORT_HEADER)
+
+distclean: clean
+	@rm -f Makefile config.h config.log config.status
+
+realclean: distclean
+	@rm -rf autom4te*
+
+maintainer-clean: realclean
+
+devclean: realclean
+	@rm -rf config.h.in configure
+
+libzone.a: $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS)
+	$(AR) rcs libzone.a $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS)
+
+$(EXPORT_HEADER):
+	@mkdir -p include/zone
+	@echo "#define ZONE_EXPORT" > $(EXPORT_HEADER)
+
+$(WESTMERE_OBJECTS): $(EXPORT_HEADER) .depend
+	@mkdir -p src/westmere
+	$(CC) $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) -march=westmere -o $@ -c $(SOURCE)/$(@:.o=.c)
+
+$(HASWELL_OBJECTS): $(EXPORT_HEADER) .depend
+	@mkdir -p src/haswell
+	$(CC) $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) -march=haswell -o $@ -c $(SOURCE)/$(@:.o=.c)
+
+$(OBJECTS): $(EXPORT_HEADER) .depend
+	@mkdir -p src/fallback
+	$(CC) $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) -o $@ -c $(SOURCE)/$(@:.o=.c)
+	@touch $@
+
+.depend:
+	@cat /dev/null > $@
+	@for x in $(DEPENDS:.d=); do echo "$${x}.o: $(SOURCE)/$${x}.c $${x}.d" >> $@; done
+
+-include .depend
+$(DEPENDS):
+-include $(DEPENDS)
Index: simdzone/README.md
===================================================================
RCS file: simdzone/README.md
diff -N simdzone/README.md
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/README.md	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,95 @@
+![Build Status](https://github.com/NLnetLabs/simdzone/actions/workflows/build-test.yml/badge.svg)
+[![Coverity Status](https://scan.coverity.com/projects/27509/badge.svg)](https://scan.coverity.com/projects/nlnetlabs-simdzone)
+[![Mastodon Follow](https://img.shields.io/mastodon/follow/109262826617293067?domain=https%3A%2F%2Ffosstodon.org&style=social)](https://fosstodon.org/@nlnetlabs)
+
+# simdzone: Parsing zone files really fast
+
+Fast and standards compliant DNS presentation format parser.
+
+DNS resource records (RRs) can be expressed in text form using the
+presentation format. The format is most frequently used to define a zone in
+master files, more commonly known as zone files, and is best considered a
+tabular serialization format with provisions for convenient editing.
+
+The format is originally defined in [RFC1035 section 5][rfc1035-section-5] and
+[RFC1034 section 3.6.1][rfc1034-section-3-6-1], but as the DNS is
+intentionally extensible, the format has been extended over time too.
+
+This project provides a lightning fast presentation format deserializer (and
+serializer eventually) for other projects to leverage. Learn more about
+simdzone by reading the [documentation](https://simdzone.docs.nlnetlabs.nl/).
+
+## Research paper
+
+* Jeroen Koekkoek and Daniel Lemire, [Parsing Millions of DNS Records per Second](https://arxiv.org/abs/2411.12035), Software: Practice and Experience (to appear)
+
+
+
+
+## Motivation
+Zone files can become quite large (.com ~24G, .se ~1.3G) and the parser in
+NSD left something to be desired. simdjson demonstrates that applying SIMD
+instructions for parsing structured text can significantly boost performance.
+simdzone, whose name is a play on [simdjson][simdjson], aims to achieve a
+similar performance boost for parsing zone data.
+
+> Currently SSE4.2 and AVX2 are supported, a fallback is used otherwise.
+
+> simdzone copies some code from the [simdjson][simdjson] project, with
+> permission to use and distribute it under the terms of
+> [The 3-Clause BSD License][bsd-3-clause].
+
+[rfc1035-section-5]: https://datatracker.ietf.org/doc/html/rfc1035#section-5
+[rfc1034-section-3-6-1]: https://datatracker.ietf.org/doc/html/rfc1034#section-3.6.1
+[nsd]: https://nlnetlabs.nl/projects/nsd/about/
+[simdjson]: https://github.com/simdjson/simdjson
+[bsd-3-clause]: https://opensource.org/license/bsd-3-clause/
+
+## Results
+Running `zone-bench` on my system (Intel Core i7-1065G7) against an older
+`.com` zone file of 12482791271 bytes under Linux (Fedora 39).
+
+clang version 17.0.6, release mode:
+```
+$ time ./zone-bench parse ../../zones/com.zone
+Selected target haswell
+Parsed 341535548 records
+
+real    0m13.533s
+user    0m12.355s
+sys     0m1.160s
+```
+
+There are bound to be bugs and quite possibly smarter ways of implementing
+some operations, but the results are promising.
+
+## Compiling
+Make sure the following tools are installed:
+  * C toolchain (the set of tools to compile C code)
+  * [cmocka](https://cmocka.org/) (if configured with `-DBUILD_TESTING=on`)
+  * [Doxygen](https://www.doxygen.nl/) (if configured with `-DBUILD_DOCUMENTATION=on`)
+  * [Sphinx](https://www.sphinx-doc.org/en/master/) (if configured with `-DBUILD_DOCUMENTATION=on`)
+
+To compile in release mode:
+```
+$ cd zone-parser
+$ mkdir build
+$ cd build
+$ cmake -DCMAKE_BUILD_TYPE=Release ..
+$ cmake --build .
+```
+
+To compile in debug mode with testing:
+```
+$ cd zone-parser
+$ mkdir build
+$ cd build
+$ cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=on ..
+$ cmake --build .
+```
+
+## Contributing
+Contributions in any way, shape or form are very welcome! Please see
+[CONTRIBUTING.md](CONTRIBUTING.md) to find out how you can help.
+
+Design decisions and notes on the [FORMAT](FORMAT.md).
Index: simdzone/config.guess
===================================================================
RCS file: simdzone/config.guess
diff -N simdzone/config.guess
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/config.guess	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,1754 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright 1992-2022 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268 # see below for rationale
+
+timestamp='2022-01-09'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General 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/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2022 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+# Just in case it came from the environment.
+GUESS=
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+    # prevent multiple calls if $tmp is already set
+    test "$tmp" && return 0
+    : "${TMPDIR=/tmp}"
+    # shellcheck disable=SC2039,SC3028
+    { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+	{ test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
+	{ tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+	{ echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
+    dummy=$tmp/dummy
+    case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+	,,)    echo "int x;" > "$dummy.c"
+	       for driver in cc gcc c89 c99 ; do
+		   if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+		       CC_FOR_BUILD=$driver
+		       break
+		   fi
+	       done
+	       if test x"$CC_FOR_BUILD" = x ; then
+		   CC_FOR_BUILD=no_compiler_found
+	       fi
+	       ;;
+	,,*)   CC_FOR_BUILD=$CC ;;
+	,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+    esac
+}
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if test -f /.attbin/uname ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case $UNAME_SYSTEM in
+Linux|GNU|GNU/*)
+	LIBC=unknown
+
+	set_cc_for_build
+	cat <<-EOF > "$dummy.c"
+	#include <features.h>
+	#if defined(__UCLIBC__)
+	LIBC=uclibc
+	#elif defined(__dietlibc__)
+	LIBC=dietlibc
+	#elif defined(__GLIBC__)
+	LIBC=gnu
+	#else
+	#include <stdarg.h>
+	/* First heuristic to detect musl libc.  */
+	#ifdef __DEFINED_va_list
+	LIBC=musl
+	#endif
+	#endif
+	EOF
+	cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
+	eval "$cc_set_libc"
+
+	# Second heuristic to detect musl libc.
+	if [ "$LIBC" = unknown ] &&
+	   command -v ldd >/dev/null &&
+	   ldd --version 2>&1 | grep -q ^musl; then
+		LIBC=musl
+	fi
+
+	# If the system lacks a compiler, then just pick glibc.
+	# We could probably try harder.
+	if [ "$LIBC" = unknown ]; then
+		LIBC=gnu
+	fi
+	;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
+    *:NetBSD:*:*)
+	# NetBSD (nbsd) targets should (where applicable) match one or
+	# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	#
+	# Note: NetBSD doesn't particularly care about the vendor
+	# portion of the name.  We always set it to "unknown".
+	UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+	    /sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+	    /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+	    echo unknown)`
+	case $UNAME_MACHINE_ARCH in
+	    aarch64eb) machine=aarch64_be-unknown ;;
+	    armeb) machine=armeb-unknown ;;
+	    arm*) machine=arm-unknown ;;
+	    sh3el) machine=shl-unknown ;;
+	    sh3eb) machine=sh-unknown ;;
+	    sh5el) machine=sh5le-unknown ;;
+	    earmv*)
+		arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+		endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+		machine=${arch}${endian}-unknown
+		;;
+	    *) machine=$UNAME_MACHINE_ARCH-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently (or will in the future) and ABI.
+	case $UNAME_MACHINE_ARCH in
+	    earm*)
+		os=netbsdelf
+		;;
+	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+		set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep -q __ELF__
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+		os=netbsd
+		;;
+	esac
+	# Determine ABI tags.
+	case $UNAME_MACHINE_ARCH in
+	    earm*)
+		expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+		abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+		;;
+	esac
+	# The OS release
+	# Debian GNU/NetBSD machines have a different userland, and
+	# thus, need a distinct triplet. However, they do not need
+	# kernel version information, so it can be replaced with a
+	# suitable tag, in the style of linux-gnu.
+	case $UNAME_VERSION in
+	    Debian*)
+		release='-gnu'
+		;;
+	    *)
+		release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+		;;
+	esac
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	GUESS=$machine-${os}${release}${abi-}
+	;;
+    *:Bitrig:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE
+	;;
+    *:OpenBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE
+	;;
+    *:SecBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE
+	;;
+    *:LibertyBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE
+	;;
+    *:MidnightBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE
+	;;
+    *:ekkoBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE
+	;;
+    *:SolidBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE
+	;;
+    *:OS108:*:*)
+	GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE
+	;;
+    macppc:MirBSD:*:*)
+	GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE
+	;;
+    *:MirBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE
+	;;
+    *:Sortix:*:*)
+	GUESS=$UNAME_MACHINE-unknown-sortix
+	;;
+    *:Twizzler:*:*)
+	GUESS=$UNAME_MACHINE-unknown-twizzler
+	;;
+    *:Redox:*:*)
+	GUESS=$UNAME_MACHINE-unknown-redox
+	;;
+    mips:OSF1:*.*)
+	GUESS=mips-dec-osf1
+	;;
+    alpha:OSF1:*:*)
+	# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+	trap '' 0
+	case $UNAME_RELEASE in
+	*4.0)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+		;;
+	*5.*)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+		;;
+	esac
+	# According to Compaq, /usr/sbin/psrinfo has been available on
+	# OSF/1 and Tru64 systems produced since 1995.  I hope that
+	# covers most systems running today.  This code pipes the CPU
+	# types through head -n 1, so we only detect the type of CPU 0.
+	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+	case $ALPHA_CPU_TYPE in
+	    "EV4 (21064)")
+		UNAME_MACHINE=alpha ;;
+	    "EV4.5 (21064)")
+		UNAME_MACHINE=alpha ;;
+	    "LCA4 (21066/21068)")
+		UNAME_MACHINE=alpha ;;
+	    "EV5 (21164)")
+		UNAME_MACHINE=alphaev5 ;;
+	    "EV5.6 (21164A)")
+		UNAME_MACHINE=alphaev56 ;;
+	    "EV5.6 (21164PC)")
+		UNAME_MACHINE=alphapca56 ;;
+	    "EV5.7 (21164PC)")
+		UNAME_MACHINE=alphapca57 ;;
+	    "EV6 (21264)")
+		UNAME_MACHINE=alphaev6 ;;
+	    "EV6.7 (21264A)")
+		UNAME_MACHINE=alphaev67 ;;
+	    "EV6.8CB (21264C)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.8AL (21264B)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.8CX (21264D)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.9A (21264/EV69A)")
+		UNAME_MACHINE=alphaev69 ;;
+	    "EV7 (21364)")
+		UNAME_MACHINE=alphaev7 ;;
+	    "EV7.9 (21364A)")
+		UNAME_MACHINE=alphaev79 ;;
+	esac
+	# A Pn.n version is a patched version.
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+	GUESS=$UNAME_MACHINE-dec-osf$OSF_REL
+	;;
+    Amiga*:UNIX_System_V:4.0:*)
+	GUESS=m68k-unknown-sysv4
+	;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	GUESS=$UNAME_MACHINE-unknown-amigaos
+	;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+	GUESS=$UNAME_MACHINE-unknown-morphos
+	;;
+    *:OS/390:*:*)
+	GUESS=i370-ibm-openedition
+	;;
+    *:z/VM:*:*)
+	GUESS=s390-ibm-zvmoe
+	;;
+    *:OS400:*:*)
+	GUESS=powerpc-ibm-os400
+	;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	GUESS=arm-acorn-riscix$UNAME_RELEASE
+	;;
+    arm*:riscos:*:*|arm*:RISCOS:*:*)
+	GUESS=arm-unknown-riscos
+	;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	GUESS=hppa1.1-hitachi-hiuxmpp
+	;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	case `(/bin/universe) 2>/dev/null` in
+	    att) GUESS=pyramid-pyramid-sysv3 ;;
+	    *)   GUESS=pyramid-pyramid-bsd   ;;
+	esac
+	;;
+    NILE*:*:*:dcosx)
+	GUESS=pyramid-pyramid-svr4
+	;;
+    DRS?6000:unix:4.0:6*)
+	GUESS=sparc-icl-nx6
+	;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+	case `/usr/bin/uname -p` in
+	    sparc) GUESS=sparc-icl-nx7 ;;
+	esac
+	;;
+    s390x:SunOS:*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL
+	;;
+    sun4H:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-hal-solaris2$SUN_REL
+	;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-sun-solaris2$SUN_REL
+	;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+	GUESS=i386-pc-auroraux$UNAME_RELEASE
+	;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+	set_cc_for_build
+	SUN_ARCH=i386
+	# If there is a compiler, see if it is configured for 64-bit objects.
+	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+	# This test works for both compilers.
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+		(CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \
+		grep IS_64BIT_ARCH >/dev/null
+	    then
+		SUN_ARCH=x86_64
+	    fi
+	fi
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=$SUN_ARCH-pc-solaris2$SUN_REL
+	;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-sun-solaris3$SUN_REL
+	;;
+    sun4*:SunOS:*:*)
+	case `/usr/bin/arch -k` in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
+	GUESS=sparc-sun-sunos$SUN_REL
+	;;
+    sun3*:SunOS:*:*)
+	GUESS=m68k-sun-sunos$UNAME_RELEASE
+	;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+	case `/bin/arch` in
+	    sun3)
+		GUESS=m68k-sun-sunos$UNAME_RELEASE
+		;;
+	    sun4)
+		GUESS=sparc-sun-sunos$UNAME_RELEASE
+		;;
+	esac
+	;;
+    aushp:SunOS:*:*)
+	GUESS=sparc-auspex-sunos$UNAME_RELEASE
+	;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+	GUESS=m68k-milan-mint$UNAME_RELEASE
+	;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+	GUESS=m68k-hades-mint$UNAME_RELEASE
+	;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+	GUESS=m68k-unknown-mint$UNAME_RELEASE
+	;;
+    m68k:machten:*:*)
+	GUESS=m68k-apple-machten$UNAME_RELEASE
+	;;
+    powerpc:machten:*:*)
+	GUESS=powerpc-apple-machten$UNAME_RELEASE
+	;;
+    RISC*:Mach:*:*)
+	GUESS=mips-dec-mach_bsd4.3
+	;;
+    RISC*:ULTRIX:*:*)
+	GUESS=mips-dec-ultrix$UNAME_RELEASE
+	;;
+    VAX*:ULTRIX*:*:*)
+	GUESS=vax-dec-ultrix$UNAME_RELEASE
+	;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	GUESS=clipper-intergraph-clix$UNAME_RELEASE
+	;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	set_cc_for_build
+	sed 's/^	//' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	$CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+	  dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+	  SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+	    { echo "$SYSTEM_NAME"; exit; }
+	GUESS=mips-mips-riscos$UNAME_RELEASE
+	;;
+    Motorola:PowerMAX_OS:*:*)
+	GUESS=powerpc-motorola-powermax
+	;;
+    Motorola:*:4.3:PL8-*)
+	GUESS=powerpc-harris-powermax
+	;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+	GUESS=powerpc-harris-powermax
+	;;
+    Night_Hawk:Power_UNIX:*:*)
+	GUESS=powerpc-harris-powerunix
+	;;
+    m88k:CX/UX:7*:*)
+	GUESS=m88k-harris-cxux7
+	;;
+    m88k:*:4*:R4*)
+	GUESS=m88k-motorola-sysv4
+	;;
+    m88k:*:3*:R3*)
+	GUESS=m88k-motorola-sysv3
+	;;
+    AViiON:dgux:*:*)
+	# DG/UX returns AViiON for all architectures
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
+	then
+	    if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
+	       test "$TARGET_BINARY_INTERFACE"x = x
+	    then
+		GUESS=m88k-dg-dgux$UNAME_RELEASE
+	    else
+		GUESS=m88k-dg-dguxbcs$UNAME_RELEASE
+	    fi
+	else
+	    GUESS=i586-dg-dgux$UNAME_RELEASE
+	fi
+	;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	GUESS=m88k-dolphin-sysv3
+	;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	GUESS=m88k-motorola-sysv3
+	;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	GUESS=m88k-tektronix-sysv3
+	;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	GUESS=m68k-tektronix-bsd
+	;;
+    *:IRIX*:*:*)
+	IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'`
+	GUESS=mips-sgi-irix$IRIX_REL
+	;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	GUESS=romp-ibm-aix    # uname -m gives an 8 hex-code CPU id
+	;;                    # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	GUESS=i386-ibm-aix
+	;;
+    ia64:AIX:*:*)
+	if test -x /usr/bin/oslevel ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+	fi
+	GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV
+	;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		set_cc_for_build
+		sed 's/^		//' << EOF > "$dummy.c"
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+		then
+			GUESS=$SYSTEM_NAME
+		else
+			GUESS=rs6000-ibm-aix3.2.5
+		fi
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		GUESS=rs6000-ibm-aix3.2.4
+	else
+		GUESS=rs6000-ibm-aix3.2
+	fi
+	;;
+    *:AIX:*:[4567])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if test -x /usr/bin/lslpp ; then
+		IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \
+			   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+	else
+		IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+	fi
+	GUESS=$IBM_ARCH-ibm-aix$IBM_REV
+	;;
+    *:AIX:*:*)
+	GUESS=rs6000-ibm-aix
+	;;
+    ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+	GUESS=romp-ibm-bsd4.4
+	;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	GUESS=romp-ibm-bsd$UNAME_RELEASE    # 4.3 with uname added to
+	;;                                  # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	GUESS=rs6000-bull-bosx
+	;;
+    DPX/2?00:B.O.S.:*:*)
+	GUESS=m68k-bull-sysv3
+	;;
+    9000/[34]??:4.3bsd:1.*:*)
+	GUESS=m68k-hp-bsd
+	;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	GUESS=m68k-hp-bsd4.4
+	;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+	case $UNAME_MACHINE in
+	    9000/31?)            HP_ARCH=m68000 ;;
+	    9000/[34]??)         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+		if test -x /usr/bin/getconf; then
+		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+		    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+		    case $sc_cpu_version in
+		      523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+		      528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+		      532)                      # CPU_PA_RISC2_0
+			case $sc_kernel_bits in
+			  32) HP_ARCH=hppa2.0n ;;
+			  64) HP_ARCH=hppa2.0w ;;
+			  '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20
+			esac ;;
+		    esac
+		fi
+		if test "$HP_ARCH" = ""; then
+		    set_cc_for_build
+		    sed 's/^		//' << EOF > "$dummy.c"
+
+		#define _HPUX_SOURCE
+		#include <stdlib.h>
+		#include <unistd.h>
+
+		int main ()
+		{
+		#if defined(_SC_KERNEL_BITS)
+		    long bits = sysconf(_SC_KERNEL_BITS);
+		#endif
+		    long cpu  = sysconf (_SC_CPU_VERSION);
+
+		    switch (cpu)
+			{
+			case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+			case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+			case CPU_PA_RISC2_0:
+		#if defined(_SC_KERNEL_BITS)
+			    switch (bits)
+				{
+				case 64: puts ("hppa2.0w"); break;
+				case 32: puts ("hppa2.0n"); break;
+				default: puts ("hppa2.0"); break;
+				} break;
+		#else  /* !defined(_SC_KERNEL_BITS) */
+			    puts ("hppa2.0"); break;
+		#endif
+			default: puts ("hppa1.0"); break;
+			}
+		    exit (0);
+		}
+EOF
+		    (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+		    test -z "$HP_ARCH" && HP_ARCH=hppa
+		fi ;;
+	esac
+	if test "$HP_ARCH" = hppa2.0w
+	then
+	    set_cc_for_build
+
+	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+	    # generating 64-bit code.  GNU and HP use different nomenclature:
+	    #
+	    # $ CC_FOR_BUILD=cc ./config.guess
+	    # => hppa2.0w-hp-hpux11.23
+	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+	    # => hppa64-hp-hpux11.23
+
+	    if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+		grep -q __LP64__
+	    then
+		HP_ARCH=hppa2.0w
+	    else
+		HP_ARCH=hppa64
+	    fi
+	fi
+	GUESS=$HP_ARCH-hp-hpux$HPUX_REV
+	;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+	GUESS=ia64-hp-hpux$HPUX_REV
+	;;
+    3050*:HI-UX:*:*)
+	set_cc_for_build
+	sed 's/^	//' << EOF > "$dummy.c"
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	$CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+		{ echo "$SYSTEM_NAME"; exit; }
+	GUESS=unknown-hitachi-hiuxwe2
+	;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+	GUESS=hppa1.1-hp-bsd
+	;;
+    9000/8??:4.3bsd:*:*)
+	GUESS=hppa1.0-hp-bsd
+	;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	GUESS=hppa1.0-hp-mpeix
+	;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+	GUESS=hppa1.1-hp-osf
+	;;
+    hp8??:OSF1:*:*)
+	GUESS=hppa1.0-hp-osf
+	;;
+    i*86:OSF1:*:*)
+	if test -x /usr/sbin/sysversion ; then
+	    GUESS=$UNAME_MACHINE-unknown-osf1mk
+	else
+	    GUESS=$UNAME_MACHINE-unknown-osf1
+	fi
+	;;
+    parisc*:Lites*:*:*)
+	GUESS=hppa1.1-hp-lites
+	;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	GUESS=c1-convex-bsd
+	;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	GUESS=c34-convex-bsd
+	;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	GUESS=c38-convex-bsd
+	;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	GUESS=c4-convex-bsd
+	;;
+    CRAY*Y-MP:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=ymp-cray-unicos$CRAY_REL
+	;;
+    CRAY*[A-Z]90:*:*:*)
+	echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*TS:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=t90-cray-unicos$CRAY_REL
+	;;
+    CRAY*T3E:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=alphaev5-cray-unicosmk$CRAY_REL
+	;;
+    CRAY*SV1:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=sv1-cray-unicos$CRAY_REL
+	;;
+    *:UNICOS/mp:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=craynv-cray-unicosmp$CRAY_REL
+	;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+	FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+	GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+	;;
+    5000:UNIX_System_V:4.*:*)
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+	FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+	GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+	;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE
+	;;
+    sparc*:BSD/OS:*:*)
+	GUESS=sparc-unknown-bsdi$UNAME_RELEASE
+	;;
+    *:BSD/OS:*:*)
+	GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE
+	;;
+    arm:FreeBSD:*:*)
+	UNAME_PROCESSOR=`uname -p`
+	set_cc_for_build
+	if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+	    | grep -q __ARM_PCS_VFP
+	then
+	    FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	    GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi
+	else
+	    FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	    GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf
+	fi
+	;;
+    *:FreeBSD:*:*)
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	case $UNAME_PROCESSOR in
+	    amd64)
+		UNAME_PROCESSOR=x86_64 ;;
+	    i386)
+		UNAME_PROCESSOR=i586 ;;
+	esac
+	FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL
+	;;
+    i*:CYGWIN*:*)
+	GUESS=$UNAME_MACHINE-pc-cygwin
+	;;
+    *:MINGW64*:*)
+	GUESS=$UNAME_MACHINE-pc-mingw64
+	;;
+    *:MINGW*:*)
+	GUESS=$UNAME_MACHINE-pc-mingw32
+	;;
+    *:MSYS*:*)
+	GUESS=$UNAME_MACHINE-pc-msys
+	;;
+    i*:PW*:*)
+	GUESS=$UNAME_MACHINE-pc-pw32
+	;;
+    *:SerenityOS:*:*)
+        GUESS=$UNAME_MACHINE-pc-serenity
+        ;;
+    *:Interix*:*)
+	case $UNAME_MACHINE in
+	    x86)
+		GUESS=i586-pc-interix$UNAME_RELEASE
+		;;
+	    authenticamd | genuineintel | EM64T)
+		GUESS=x86_64-unknown-interix$UNAME_RELEASE
+		;;
+	    IA64)
+		GUESS=ia64-unknown-interix$UNAME_RELEASE
+		;;
+	esac ;;
+    i*:UWIN*:*)
+	GUESS=$UNAME_MACHINE-pc-uwin
+	;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+	GUESS=x86_64-pc-cygwin
+	;;
+    prep*:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=powerpcle-unknown-solaris2$SUN_REL
+	;;
+    *:GNU:*:*)
+	# the GNU system
+	GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'`
+	GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'`
+	GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL
+	;;
+    *:GNU/*:*:*)
+	# other systems with GNU libc and userland
+	GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"`
+	GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
+	;;
+    *:Minix:*:*)
+	GUESS=$UNAME_MACHINE-unknown-minix
+	;;
+    aarch64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    aarch64_be:Linux:*:*)
+	UNAME_MACHINE=aarch64_be
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+	esac
+	objdump --private-headers /bin/sh | grep -q ld.so.1
+	if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    arm*:Linux:*:*)
+	set_cc_for_build
+	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+	    | grep -q __ARM_EABI__
+	then
+	    GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	else
+	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+		| grep -q __ARM_PCS_VFP
+	    then
+		GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi
+	    else
+		GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf
+	    fi
+	fi
+	;;
+    avr32*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    cris:Linux:*:*)
+	GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+	;;
+    crisv32:Linux:*:*)
+	GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+	;;
+    e2k:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    frv:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    hexagon:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    i*86:Linux:*:*)
+	GUESS=$UNAME_MACHINE-pc-linux-$LIBC
+	;;
+    ia64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    k1om:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    m32r*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    m68*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+	set_cc_for_build
+	IS_GLIBC=0
+	test x"${LIBC}" = xgnu && IS_GLIBC=1
+	sed 's/^	//' << EOF > "$dummy.c"
+	#undef CPU
+	#undef mips
+	#undef mipsel
+	#undef mips64
+	#undef mips64el
+	#if ${IS_GLIBC} && defined(_ABI64)
+	LIBCABI=gnuabi64
+	#else
+	#if ${IS_GLIBC} && defined(_ABIN32)
+	LIBCABI=gnuabin32
+	#else
+	LIBCABI=${LIBC}
+	#endif
+	#endif
+
+	#if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+	CPU=mipsisa64r6
+	#else
+	#if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+	CPU=mipsisa32r6
+	#else
+	#if defined(__mips64)
+	CPU=mips64
+	#else
+	CPU=mips
+	#endif
+	#endif
+	#endif
+
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	MIPS_ENDIAN=el
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	MIPS_ENDIAN=
+	#else
+	MIPS_ENDIAN=
+	#endif
+	#endif
+EOF
+	cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`
+	eval "$cc_set_vars"
+	test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
+	;;
+    mips64el:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    openrisc*:Linux:*:*)
+	GUESS=or1k-unknown-linux-$LIBC
+	;;
+    or32:Linux:*:* | or1k*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    padre:Linux:*:*)
+	GUESS=sparc-unknown-linux-$LIBC
+	;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	GUESS=hppa64-unknown-linux-$LIBC
+	;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;;
+	  PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;;
+	  *)    GUESS=hppa-unknown-linux-$LIBC ;;
+	esac
+	;;
+    ppc64:Linux:*:*)
+	GUESS=powerpc64-unknown-linux-$LIBC
+	;;
+    ppc:Linux:*:*)
+	GUESS=powerpc-unknown-linux-$LIBC
+	;;
+    ppc64le:Linux:*:*)
+	GUESS=powerpc64le-unknown-linux-$LIBC
+	;;
+    ppcle:Linux:*:*)
+	GUESS=powerpcle-unknown-linux-$LIBC
+	;;
+    riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	GUESS=$UNAME_MACHINE-ibm-linux-$LIBC
+	;;
+    sh64*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    sh*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    tile*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    vax:Linux:*:*)
+	GUESS=$UNAME_MACHINE-dec-linux-$LIBC
+	;;
+    x86_64:Linux:*:*)
+	set_cc_for_build
+	LIBCABI=$LIBC
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
+		(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		grep IS_X32 >/dev/null
+	    then
+		LIBCABI=${LIBC}x32
+	    fi
+	fi
+	GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI
+	;;
+    xtensa*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	GUESS=i386-sequent-sysv4
+	;;
+    i*86:UNIX_SV:4.2MP:2.*)
+	# Unixware is an offshoot of SVR4, but it has its own version
+	# number series starting with 2...
+	# I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+	# Use sysv4.2uw... so that sysv4* matches it.
+	GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
+	;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	GUESS=$UNAME_MACHINE-pc-os2-emx
+	;;
+    i*86:XTS-300:*:STOP)
+	GUESS=$UNAME_MACHINE-unknown-stop
+	;;
+    i*86:atheos:*:*)
+	GUESS=$UNAME_MACHINE-unknown-atheos
+	;;
+    i*86:syllable:*:*)
+	GUESS=$UNAME_MACHINE-pc-syllable
+	;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+	GUESS=i386-unknown-lynxos$UNAME_RELEASE
+	;;
+    i*86:*DOS:*:*)
+	GUESS=$UNAME_MACHINE-pc-msdosdjgpp
+	;;
+    i*86:*:4.*:*)
+	UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL
+	else
+		GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL
+	fi
+	;;
+    i*86:*:5:[678]*)
+	# UnixWare 7.x, OpenUNIX and OpenServer 6.
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		GUESS=$UNAME_MACHINE-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL
+	else
+		GUESS=$UNAME_MACHINE-pc-sysv32
+	fi
+	;;
+    pc:*:*:*)
+	# Left here for compatibility:
+	# uname -m prints for DJGPP always 'pc', but it prints nothing about
+	# the processor, so we play safe by assuming i586.
+	# Note: whatever this is, it MUST be the same as what config.sub
+	# prints for the "djgpp" host, or else GDB configure will decide that
+	# this is a cross-build.
+	GUESS=i586-pc-msdosdjgpp
+	;;
+    Intel:Mach:3*:*)
+	GUESS=i386-pc-mach3
+	;;
+    paragon:*:*:*)
+	GUESS=i860-intel-osf1
+	;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  GUESS=i860-stardent-sysv$UNAME_RELEASE    # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  GUESS=i860-unknown-sysv$UNAME_RELEASE     # Unknown i860-SVR4
+	fi
+	;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	GUESS=m68010-convergent-sysv
+	;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+	GUESS=m68k-convergent-sysv
+	;;
+    M680?0:D-NIX:5.3:*)
+	GUESS=m68k-diab-dnix
+	;;
+    M68*:*:R3V[5678]*:*)
+	test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+	OS_REL='.3'
+	test -r /etc/.relid \
+	    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	    && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	    && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+	    && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	GUESS=m68k-unknown-lynxos$UNAME_RELEASE
+	;;
+    mc68030:UNIX_System_V:4.*:*)
+	GUESS=m68k-atari-sysv4
+	;;
+    TSUNAMI:LynxOS:2.*:*)
+	GUESS=sparc-unknown-lynxos$UNAME_RELEASE
+	;;
+    rs6000:LynxOS:2.*:*)
+	GUESS=rs6000-unknown-lynxos$UNAME_RELEASE
+	;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+	GUESS=powerpc-unknown-lynxos$UNAME_RELEASE
+	;;
+    SM[BE]S:UNIX_SV:*:*)
+	GUESS=mips-dde-sysv$UNAME_RELEASE
+	;;
+    RM*:ReliantUNIX-*:*:*)
+	GUESS=mips-sni-sysv4
+	;;
+    RM*:SINIX-*:*:*)
+	GUESS=mips-sni-sysv4
+	;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		GUESS=$UNAME_MACHINE-sni-sysv4
+	else
+		GUESS=ns32k-sni-sysv
+	fi
+	;;
+    PENTIUM:*:4.0*:*)	# Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+			# says <Richard.M.Bartel@ccMail.Census.GOV>
+	GUESS=i586-unisys-sysv4
+	;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	GUESS=hppa1.1-stratus-sysv4
+	;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	GUESS=i860-stratus-sysv4
+	;;
+    i*86:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	GUESS=$UNAME_MACHINE-stratus-vos
+	;;
+    *:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	GUESS=hppa1.1-stratus-vos
+	;;
+    mc68*:A/UX:*:*)
+	GUESS=m68k-apple-aux$UNAME_RELEASE
+	;;
+    news*:NEWS-OS:6*:*)
+	GUESS=mips-sony-newsos6
+	;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if test -d /usr/nec; then
+		GUESS=mips-nec-sysv$UNAME_RELEASE
+	else
+		GUESS=mips-unknown-sysv$UNAME_RELEASE
+	fi
+	;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	GUESS=powerpc-be-beos
+	;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	GUESS=powerpc-apple-beos
+	;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	GUESS=i586-pc-beos
+	;;
+    BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
+	GUESS=i586-pc-haiku
+	;;
+    x86_64:Haiku:*:*)
+	GUESS=x86_64-unknown-haiku
+	;;
+    SX-4:SUPER-UX:*:*)
+	GUESS=sx4-nec-superux$UNAME_RELEASE
+	;;
+    SX-5:SUPER-UX:*:*)
+	GUESS=sx5-nec-superux$UNAME_RELEASE
+	;;
+    SX-6:SUPER-UX:*:*)
+	GUESS=sx6-nec-superux$UNAME_RELEASE
+	;;
+    SX-7:SUPER-UX:*:*)
+	GUESS=sx7-nec-superux$UNAME_RELEASE
+	;;
+    SX-8:SUPER-UX:*:*)
+	GUESS=sx8-nec-superux$UNAME_RELEASE
+	;;
+    SX-8R:SUPER-UX:*:*)
+	GUESS=sx8r-nec-superux$UNAME_RELEASE
+	;;
+    SX-ACE:SUPER-UX:*:*)
+	GUESS=sxace-nec-superux$UNAME_RELEASE
+	;;
+    Power*:Rhapsody:*:*)
+	GUESS=powerpc-apple-rhapsody$UNAME_RELEASE
+	;;
+    *:Rhapsody:*:*)
+	GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE
+	;;
+    arm64:Darwin:*:*)
+	GUESS=aarch64-apple-darwin$UNAME_RELEASE
+	;;
+    *:Darwin:*:*)
+	UNAME_PROCESSOR=`uname -p`
+	case $UNAME_PROCESSOR in
+	    unknown) UNAME_PROCESSOR=powerpc ;;
+	esac
+	if command -v xcode-select > /dev/null 2> /dev/null && \
+		! xcode-select --print-path > /dev/null 2> /dev/null ; then
+	    # Avoid executing cc if there is no toolchain installed as
+	    # cc will be a stub that puts up a graphical alert
+	    # prompting the user to install developer tools.
+	    CC_FOR_BUILD=no_compiler_found
+	else
+	    set_cc_for_build
+	fi
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+		   (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		   grep IS_64BIT_ARCH >/dev/null
+	    then
+		case $UNAME_PROCESSOR in
+		    i386) UNAME_PROCESSOR=x86_64 ;;
+		    powerpc) UNAME_PROCESSOR=powerpc64 ;;
+		esac
+	    fi
+	    # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+	    if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+		   (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		   grep IS_PPC >/dev/null
+	    then
+		UNAME_PROCESSOR=powerpc
+	    fi
+	elif test "$UNAME_PROCESSOR" = i386 ; then
+	    # uname -m returns i386 or x86_64
+	    UNAME_PROCESSOR=$UNAME_MACHINE
+	fi
+	GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE
+	;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	UNAME_PROCESSOR=`uname -p`
+	if test "$UNAME_PROCESSOR" = x86; then
+		UNAME_PROCESSOR=i386
+		UNAME_MACHINE=pc
+	fi
+	GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE
+	;;
+    *:QNX:*:4*)
+	GUESS=i386-pc-qnx
+	;;
+    NEO-*:NONSTOP_KERNEL:*:*)
+	GUESS=neo-tandem-nsk$UNAME_RELEASE
+	;;
+    NSE-*:NONSTOP_KERNEL:*:*)
+	GUESS=nse-tandem-nsk$UNAME_RELEASE
+	;;
+    NSR-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsr-tandem-nsk$UNAME_RELEASE
+	;;
+    NSV-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsv-tandem-nsk$UNAME_RELEASE
+	;;
+    NSX-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsx-tandem-nsk$UNAME_RELEASE
+	;;
+    *:NonStop-UX:*:*)
+	GUESS=mips-compaq-nonstopux
+	;;
+    BS2000:POSIX*:*:*)
+	GUESS=bs2000-siemens-sysv
+	;;
+    DS/*:UNIX_System_V:*:*)
+	GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE
+	;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "${cputype-}" = 386; then
+	    UNAME_MACHINE=i386
+	elif test "x${cputype-}" != x; then
+	    UNAME_MACHINE=$cputype
+	fi
+	GUESS=$UNAME_MACHINE-unknown-plan9
+	;;
+    *:TOPS-10:*:*)
+	GUESS=pdp10-unknown-tops10
+	;;
+    *:TENEX:*:*)
+	GUESS=pdp10-unknown-tenex
+	;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	GUESS=pdp10-dec-tops20
+	;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	GUESS=pdp10-xkl-tops20
+	;;
+    *:TOPS-20:*:*)
+	GUESS=pdp10-unknown-tops20
+	;;
+    *:ITS:*:*)
+	GUESS=pdp10-unknown-its
+	;;
+    SEI:*:*:SEIUX)
+	GUESS=mips-sei-seiux$UNAME_RELEASE
+	;;
+    *:DragonFly:*:*)
+	DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL
+	;;
+    *:*VMS:*:*)
+	UNAME_MACHINE=`(uname -p) 2>/dev/null`
+	case $UNAME_MACHINE in
+	    A*) GUESS=alpha-dec-vms ;;
+	    I*) GUESS=ia64-dec-vms ;;
+	    V*) GUESS=vax-dec-vms ;;
+	esac ;;
+    *:XENIX:*:SysV)
+	GUESS=i386-pc-xenix
+	;;
+    i*86:skyos:*:*)
+	SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`
+	GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL
+	;;
+    i*86:rdos:*:*)
+	GUESS=$UNAME_MACHINE-pc-rdos
+	;;
+    i*86:Fiwix:*:*)
+	GUESS=$UNAME_MACHINE-pc-fiwix
+	;;
+    *:AROS:*:*)
+	GUESS=$UNAME_MACHINE-unknown-aros
+	;;
+    x86_64:VMkernel:*:*)
+	GUESS=$UNAME_MACHINE-unknown-esx
+	;;
+    amd64:Isilon\ OneFS:*:*)
+	GUESS=x86_64-unknown-onefs
+	;;
+    *:Unleashed:*:*)
+	GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
+	;;
+esac
+
+# Do we have a guess based on uname results?
+if test "x$GUESS" != x; then
+    echo "$GUESS"
+    exit
+fi
+
+# No uname command or uname output not recognized.
+set_cc_for_build
+cat > "$dummy.c" <<EOF
+#ifdef _SEQUENT_
+#include <sys/types.h>
+#include <sys/utsname.h>
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include <signal.h>
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include <sys/utsname.h>
+#endif
+#endif
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+  "4"
+#else
+  ""
+#endif
+  ); exit (0);
+#endif
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+  struct utsname un;
+
+  uname(&un);
+  if (strncmp(un.version, "V2", 2) == 0) {
+    printf ("i386-sequent-ptx2\n"); exit (0);
+  }
+  if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+    printf ("i386-sequent-ptx1\n"); exit (0);
+  }
+  printf ("i386-sequent-ptx\n"); exit (0);
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+#include <sys/param.h>
+#if defined (BSD)
+#if BSD == 43
+  printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+  printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname un;
+  uname (&un);
+  printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname *un;
+  uname (&un);
+  printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` &&
+	{ echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
+
+echo "$0: unable to guess system type" >&2
+
+case $UNAME_MACHINE:$UNAME_SYSTEM in
+    mips:Linux | mips64:Linux)
+	# If we got here on MIPS GNU/Linux, output extra information.
+	cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+	;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+and
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+EOF
+
+our_year=`echo $timestamp | sed 's,-.*,,'`
+thisyear=`date +%Y`
+# shellcheck disable=SC2003
+script_age=`expr "$thisyear" - "$our_year"`
+if test "$script_age" -lt 3 ; then
+   cat >&2 <<EOF
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM  = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+fi
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
Index: simdzone/config.h.in
===================================================================
RCS file: simdzone/config.h.in
diff -N simdzone/config.h.in
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/config.h.in	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,85 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the declaration of `bswap16', and to 0 if you
+   don't. */
+#undef HAVE_DECL_BSWAP16
+
+/* Define to 1 if you have the declaration of `bswap32', and to 0 if you
+   don't. */
+#undef HAVE_DECL_BSWAP32
+
+/* Define to 1 if you have the declaration of `bswap64', and to 0 if you
+   don't. */
+#undef HAVE_DECL_BSWAP64
+
+/* Define to 1 if you have the <endian.h> header file. */
+#undef HAVE_ENDIAN_H
+
+/* Wether or not to compile support for AVX2 */
+#undef HAVE_HASWELL
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `realpath' function. */
+#undef HAVE_REALPATH
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_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 <sys/endian.h> header file. */
+#undef HAVE_SYS_ENDIAN_H
+
+/* 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 <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Wether or not to compile support for SSE4.2 */
+#undef HAVE_WESTMERE
+
+/* 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 all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#undef STDC_HEADERS
+
+
+/* Defines _XOPEN_SOURCE and _POSIX_C_SOURCE implicitly in features.h */
+#ifndef _DEFAULT_SOURCE
+# define _DEFAULT_SOURCE 1
+#endif
+
Index: simdzone/config.sub
===================================================================
RCS file: simdzone/config.sub
diff -N simdzone/config.sub
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/config.sub	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,1890 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright 1992-2022 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268 # see below for rationale
+
+timestamp='2022-01-03'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General 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/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2022 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo "$1"
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Split fields of configuration type
+# shellcheck disable=SC2162
+saved_IFS=$IFS
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
+IFS=$saved_IFS
+
+# Separate into logical components for further validation
+case $1 in
+	*-*-*-*-*)
+		echo Invalid configuration \`"$1"\': more than four components >&2
+		exit 1
+		;;
+	*-*-*-*)
+		basic_machine=$field1-$field2
+		basic_os=$field3-$field4
+		;;
+	*-*-*)
+		# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+		# parts
+		maybe_os=$field2-$field3
+		case $maybe_os in
+			nto-qnx* | linux-* | uclinux-uclibc* \
+			| uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
+			| netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
+			| storm-chaos* | os2-emx* | rtmk-nova*)
+				basic_machine=$field1
+				basic_os=$maybe_os
+				;;
+			android-linux)
+				basic_machine=$field1-unknown
+				basic_os=linux-android
+				;;
+			*)
+				basic_machine=$field1-$field2
+				basic_os=$field3
+				;;
+		esac
+		;;
+	*-*)
+		# A lone config we happen to match not fitting any pattern
+		case $field1-$field2 in
+			decstation-3100)
+				basic_machine=mips-dec
+				basic_os=
+				;;
+			*-*)
+				# Second component is usually, but not always the OS
+				case $field2 in
+					# Prevent following clause from handling this valid os
+					sun*os*)
+						basic_machine=$field1
+						basic_os=$field2
+						;;
+					zephyr*)
+						basic_machine=$field1-unknown
+						basic_os=$field2
+						;;
+					# Manufacturers
+					dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
+					| att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
+					| unicom* | ibm* | next | hp | isi* | apollo | altos* \
+					| convergent* | ncr* | news | 32* | 3600* | 3100* \
+					| hitachi* | c[123]* | convex* | sun | crds | omron* | dg \
+					| ultra | tti* | harris | dolphin | highlevel | gould \
+					| cbm | ns | masscomp | apple | axis | knuth | cray \
+					| microblaze* | sim | cisco \
+					| oki | wec | wrs | winbond)
+						basic_machine=$field1-$field2
+						basic_os=
+						;;
+					*)
+						basic_machine=$field1
+						basic_os=$field2
+						;;
+				esac
+			;;
+		esac
+		;;
+	*)
+		# Convert single-component short-hands not valid as part of
+		# multi-component configurations.
+		case $field1 in
+			386bsd)
+				basic_machine=i386-pc
+				basic_os=bsd
+				;;
+			a29khif)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			adobe68k)
+				basic_machine=m68010-adobe
+				basic_os=scout
+				;;
+			alliant)
+				basic_machine=fx80-alliant
+				basic_os=
+				;;
+			altos | altos3068)
+				basic_machine=m68k-altos
+				basic_os=
+				;;
+			am29k)
+				basic_machine=a29k-none
+				basic_os=bsd
+				;;
+			amdahl)
+				basic_machine=580-amdahl
+				basic_os=sysv
+				;;
+			amiga)
+				basic_machine=m68k-unknown
+				basic_os=
+				;;
+			amigaos | amigados)
+				basic_machine=m68k-unknown
+				basic_os=amigaos
+				;;
+			amigaunix | amix)
+				basic_machine=m68k-unknown
+				basic_os=sysv4
+				;;
+			apollo68)
+				basic_machine=m68k-apollo
+				basic_os=sysv
+				;;
+			apollo68bsd)
+				basic_machine=m68k-apollo
+				basic_os=bsd
+				;;
+			aros)
+				basic_machine=i386-pc
+				basic_os=aros
+				;;
+			aux)
+				basic_machine=m68k-apple
+				basic_os=aux
+				;;
+			balance)
+				basic_machine=ns32k-sequent
+				basic_os=dynix
+				;;
+			blackfin)
+				basic_machine=bfin-unknown
+				basic_os=linux
+				;;
+			cegcc)
+				basic_machine=arm-unknown
+				basic_os=cegcc
+				;;
+			convex-c1)
+				basic_machine=c1-convex
+				basic_os=bsd
+				;;
+			convex-c2)
+				basic_machine=c2-convex
+				basic_os=bsd
+				;;
+			convex-c32)
+				basic_machine=c32-convex
+				basic_os=bsd
+				;;
+			convex-c34)
+				basic_machine=c34-convex
+				basic_os=bsd
+				;;
+			convex-c38)
+				basic_machine=c38-convex
+				basic_os=bsd
+				;;
+			cray)
+				basic_machine=j90-cray
+				basic_os=unicos
+				;;
+			crds | unos)
+				basic_machine=m68k-crds
+				basic_os=
+				;;
+			da30)
+				basic_machine=m68k-da30
+				basic_os=
+				;;
+			decstation | pmax | pmin | dec3100 | decstatn)
+				basic_machine=mips-dec
+				basic_os=
+				;;
+			delta88)
+				basic_machine=m88k-motorola
+				basic_os=sysv3
+				;;
+			dicos)
+				basic_machine=i686-pc
+				basic_os=dicos
+				;;
+			djgpp)
+				basic_machine=i586-pc
+				basic_os=msdosdjgpp
+				;;
+			ebmon29k)
+				basic_machine=a29k-amd
+				basic_os=ebmon
+				;;
+			es1800 | OSE68k | ose68k | ose | OSE)
+				basic_machine=m68k-ericsson
+				basic_os=ose
+				;;
+			gmicro)
+				basic_machine=tron-gmicro
+				basic_os=sysv
+				;;
+			go32)
+				basic_machine=i386-pc
+				basic_os=go32
+				;;
+			h8300hms)
+				basic_machine=h8300-hitachi
+				basic_os=hms
+				;;
+			h8300xray)
+				basic_machine=h8300-hitachi
+				basic_os=xray
+				;;
+			h8500hms)
+				basic_machine=h8500-hitachi
+				basic_os=hms
+				;;
+			harris)
+				basic_machine=m88k-harris
+				basic_os=sysv3
+				;;
+			hp300 | hp300hpux)
+				basic_machine=m68k-hp
+				basic_os=hpux
+				;;
+			hp300bsd)
+				basic_machine=m68k-hp
+				basic_os=bsd
+				;;
+			hppaosf)
+				basic_machine=hppa1.1-hp
+				basic_os=osf
+				;;
+			hppro)
+				basic_machine=hppa1.1-hp
+				basic_os=proelf
+				;;
+			i386mach)
+				basic_machine=i386-mach
+				basic_os=mach
+				;;
+			isi68 | isi)
+				basic_machine=m68k-isi
+				basic_os=sysv
+				;;
+			m68knommu)
+				basic_machine=m68k-unknown
+				basic_os=linux
+				;;
+			magnum | m3230)
+				basic_machine=mips-mips
+				basic_os=sysv
+				;;
+			merlin)
+				basic_machine=ns32k-utek
+				basic_os=sysv
+				;;
+			mingw64)
+				basic_machine=x86_64-pc
+				basic_os=mingw64
+				;;
+			mingw32)
+				basic_machine=i686-pc
+				basic_os=mingw32
+				;;
+			mingw32ce)
+				basic_machine=arm-unknown
+				basic_os=mingw32ce
+				;;
+			monitor)
+				basic_machine=m68k-rom68k
+				basic_os=coff
+				;;
+			morphos)
+				basic_machine=powerpc-unknown
+				basic_os=morphos
+				;;
+			moxiebox)
+				basic_machine=moxie-unknown
+				basic_os=moxiebox
+				;;
+			msdos)
+				basic_machine=i386-pc
+				basic_os=msdos
+				;;
+			msys)
+				basic_machine=i686-pc
+				basic_os=msys
+				;;
+			mvs)
+				basic_machine=i370-ibm
+				basic_os=mvs
+				;;
+			nacl)
+				basic_machine=le32-unknown
+				basic_os=nacl
+				;;
+			ncr3000)
+				basic_machine=i486-ncr
+				basic_os=sysv4
+				;;
+			netbsd386)
+				basic_machine=i386-pc
+				basic_os=netbsd
+				;;
+			netwinder)
+				basic_machine=armv4l-rebel
+				basic_os=linux
+				;;
+			news | news700 | news800 | news900)
+				basic_machine=m68k-sony
+				basic_os=newsos
+				;;
+			news1000)
+				basic_machine=m68030-sony
+				basic_os=newsos
+				;;
+			necv70)
+				basic_machine=v70-nec
+				basic_os=sysv
+				;;
+			nh3000)
+				basic_machine=m68k-harris
+				basic_os=cxux
+				;;
+			nh[45]000)
+				basic_machine=m88k-harris
+				basic_os=cxux
+				;;
+			nindy960)
+				basic_machine=i960-intel
+				basic_os=nindy
+				;;
+			mon960)
+				basic_machine=i960-intel
+				basic_os=mon960
+				;;
+			nonstopux)
+				basic_machine=mips-compaq
+				basic_os=nonstopux
+				;;
+			os400)
+				basic_machine=powerpc-ibm
+				basic_os=os400
+				;;
+			OSE68000 | ose68000)
+				basic_machine=m68000-ericsson
+				basic_os=ose
+				;;
+			os68k)
+				basic_machine=m68k-none
+				basic_os=os68k
+				;;
+			paragon)
+				basic_machine=i860-intel
+				basic_os=osf
+				;;
+			parisc)
+				basic_machine=hppa-unknown
+				basic_os=linux
+				;;
+			psp)
+				basic_machine=mipsallegrexel-sony
+				basic_os=psp
+				;;
+			pw32)
+				basic_machine=i586-unknown
+				basic_os=pw32
+				;;
+			rdos | rdos64)
+				basic_machine=x86_64-pc
+				basic_os=rdos
+				;;
+			rdos32)
+				basic_machine=i386-pc
+				basic_os=rdos
+				;;
+			rom68k)
+				basic_machine=m68k-rom68k
+				basic_os=coff
+				;;
+			sa29200)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			sei)
+				basic_machine=mips-sei
+				basic_os=seiux
+				;;
+			sequent)
+				basic_machine=i386-sequent
+				basic_os=
+				;;
+			sps7)
+				basic_machine=m68k-bull
+				basic_os=sysv2
+				;;
+			st2000)
+				basic_machine=m68k-tandem
+				basic_os=
+				;;
+			stratus)
+				basic_machine=i860-stratus
+				basic_os=sysv4
+				;;
+			sun2)
+				basic_machine=m68000-sun
+				basic_os=
+				;;
+			sun2os3)
+				basic_machine=m68000-sun
+				basic_os=sunos3
+				;;
+			sun2os4)
+				basic_machine=m68000-sun
+				basic_os=sunos4
+				;;
+			sun3)
+				basic_machine=m68k-sun
+				basic_os=
+				;;
+			sun3os3)
+				basic_machine=m68k-sun
+				basic_os=sunos3
+				;;
+			sun3os4)
+				basic_machine=m68k-sun
+				basic_os=sunos4
+				;;
+			sun4)
+				basic_machine=sparc-sun
+				basic_os=
+				;;
+			sun4os3)
+				basic_machine=sparc-sun
+				basic_os=sunos3
+				;;
+			sun4os4)
+				basic_machine=sparc-sun
+				basic_os=sunos4
+				;;
+			sun4sol2)
+				basic_machine=sparc-sun
+				basic_os=solaris2
+				;;
+			sun386 | sun386i | roadrunner)
+				basic_machine=i386-sun
+				basic_os=
+				;;
+			sv1)
+				basic_machine=sv1-cray
+				basic_os=unicos
+				;;
+			symmetry)
+				basic_machine=i386-sequent
+				basic_os=dynix
+				;;
+			t3e)
+				basic_machine=alphaev5-cray
+				basic_os=unicos
+				;;
+			t90)
+				basic_machine=t90-cray
+				basic_os=unicos
+				;;
+			toad1)
+				basic_machine=pdp10-xkl
+				basic_os=tops20
+				;;
+			tpf)
+				basic_machine=s390x-ibm
+				basic_os=tpf
+				;;
+			udi29k)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			ultra3)
+				basic_machine=a29k-nyu
+				basic_os=sym1
+				;;
+			v810 | necv810)
+				basic_machine=v810-nec
+				basic_os=none
+				;;
+			vaxv)
+				basic_machine=vax-dec
+				basic_os=sysv
+				;;
+			vms)
+				basic_machine=vax-dec
+				basic_os=vms
+				;;
+			vsta)
+				basic_machine=i386-pc
+				basic_os=vsta
+				;;
+			vxworks960)
+				basic_machine=i960-wrs
+				basic_os=vxworks
+				;;
+			vxworks68)
+				basic_machine=m68k-wrs
+				basic_os=vxworks
+				;;
+			vxworks29k)
+				basic_machine=a29k-wrs
+				basic_os=vxworks
+				;;
+			xbox)
+				basic_machine=i686-pc
+				basic_os=mingw32
+				;;
+			ymp)
+				basic_machine=ymp-cray
+				basic_os=unicos
+				;;
+			*)
+				basic_machine=$1
+				basic_os=
+				;;
+		esac
+		;;
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+	# Here we handle the default manufacturer of certain CPU types.  It is in
+	# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		cpu=hppa1.1
+		vendor=winbond
+		;;
+	op50n)
+		cpu=hppa1.1
+		vendor=oki
+		;;
+	op60c)
+		cpu=hppa1.1
+		vendor=oki
+		;;
+	ibm*)
+		cpu=i370
+		vendor=ibm
+		;;
+	orion105)
+		cpu=clipper
+		vendor=highlevel
+		;;
+	mac | mpw | mac-mpw)
+		cpu=m68k
+		vendor=apple
+		;;
+	pmac | pmac-mpw)
+		cpu=powerpc
+		vendor=apple
+		;;
+
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		cpu=m68000
+		vendor=att
+		;;
+	3b*)
+		cpu=we32k
+		vendor=att
+		;;
+	bluegene*)
+		cpu=powerpc
+		vendor=ibm
+		basic_os=cnk
+		;;
+	decsystem10* | dec10*)
+		cpu=pdp10
+		vendor=dec
+		basic_os=tops10
+		;;
+	decsystem20* | dec20*)
+		cpu=pdp10
+		vendor=dec
+		basic_os=tops20
+		;;
+	delta | 3300 | motorola-3300 | motorola-delta \
+	      | 3300-motorola | delta-motorola)
+		cpu=m68k
+		vendor=motorola
+		;;
+	dpx2*)
+		cpu=m68k
+		vendor=bull
+		basic_os=sysv3
+		;;
+	encore | umax | mmax)
+		cpu=ns32k
+		vendor=encore
+		;;
+	elxsi)
+		cpu=elxsi
+		vendor=elxsi
+		basic_os=${basic_os:-bsd}
+		;;
+	fx2800)
+		cpu=i860
+		vendor=alliant
+		;;
+	genix)
+		cpu=ns32k
+		vendor=ns
+		;;
+	h3050r* | hiux*)
+		cpu=hppa1.1
+		vendor=hitachi
+		basic_os=hiuxwe2
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		cpu=m68000
+		vendor=hp
+		;;
+	hp9k3[2-9][0-9])
+		cpu=m68k
+		vendor=hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	i*86v32)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv32
+		;;
+	i*86v4*)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv4
+		;;
+	i*86v)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv
+		;;
+	i*86sol2)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=solaris2
+		;;
+	j90 | j90-cray)
+		cpu=j90
+		vendor=cray
+		basic_os=${basic_os:-unicos}
+		;;
+	iris | iris4d)
+		cpu=mips
+		vendor=sgi
+		case $basic_os in
+		    irix*)
+			;;
+		    *)
+			basic_os=irix4
+			;;
+		esac
+		;;
+	miniframe)
+		cpu=m68000
+		vendor=convergent
+		;;
+	*mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		cpu=m68k
+		vendor=atari
+		basic_os=mint
+		;;
+	news-3600 | risc-news)
+		cpu=mips
+		vendor=sony
+		basic_os=newsos
+		;;
+	next | m*-next)
+		cpu=m68k
+		vendor=next
+		case $basic_os in
+		    openstep*)
+		        ;;
+		    nextstep*)
+			;;
+		    ns2*)
+		      basic_os=nextstep2
+			;;
+		    *)
+		      basic_os=nextstep3
+			;;
+		esac
+		;;
+	np1)
+		cpu=np1
+		vendor=gould
+		;;
+	op50n-* | op60c-*)
+		cpu=hppa1.1
+		vendor=oki
+		basic_os=proelf
+		;;
+	pa-hitachi)
+		cpu=hppa1.1
+		vendor=hitachi
+		basic_os=hiuxwe2
+		;;
+	pbd)
+		cpu=sparc
+		vendor=tti
+		;;
+	pbb)
+		cpu=m68k
+		vendor=tti
+		;;
+	pc532)
+		cpu=ns32k
+		vendor=pc532
+		;;
+	pn)
+		cpu=pn
+		vendor=gould
+		;;
+	power)
+		cpu=power
+		vendor=ibm
+		;;
+	ps2)
+		cpu=i386
+		vendor=ibm
+		;;
+	rm[46]00)
+		cpu=mips
+		vendor=siemens
+		;;
+	rtpc | rtpc-*)
+		cpu=romp
+		vendor=ibm
+		;;
+	sde)
+		cpu=mipsisa32
+		vendor=sde
+		basic_os=${basic_os:-elf}
+		;;
+	simso-wrs)
+		cpu=sparclite
+		vendor=wrs
+		basic_os=vxworks
+		;;
+	tower | tower-32)
+		cpu=m68k
+		vendor=ncr
+		;;
+	vpp*|vx|vx-*)
+		cpu=f301
+		vendor=fujitsu
+		;;
+	w65)
+		cpu=w65
+		vendor=wdc
+		;;
+	w89k-*)
+		cpu=hppa1.1
+		vendor=winbond
+		basic_os=proelf
+		;;
+	none)
+		cpu=none
+		vendor=none
+		;;
+	leon|leon[3-9])
+		cpu=sparc
+		vendor=$basic_machine
+		;;
+	leon-*|leon[3-9]-*)
+		cpu=sparc
+		vendor=`echo "$basic_machine" | sed 's/-.*//'`
+		;;
+
+	*-*)
+		# shellcheck disable=SC2162
+		saved_IFS=$IFS
+		IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
+		IFS=$saved_IFS
+		;;
+	# We use `pc' rather than `unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+		cpu=$basic_machine
+		vendor=pc
+		;;
+	# These rules are duplicated from below for sake of the special case above;
+	# i.e. things that normalized to x86 arches should also default to "pc"
+	pc98)
+		cpu=i386
+		vendor=pc
+		;;
+	x64 | amd64)
+		cpu=x86_64
+		vendor=pc
+		;;
+	# Recognize the basic CPU types without company name.
+	*)
+		cpu=$basic_machine
+		vendor=unknown
+		;;
+esac
+
+unset -v basic_machine
+
+# Decode basic machines in the full and proper CPU-Company form.
+case $cpu-$vendor in
+	# Here we handle the default manufacturer of certain CPU types in canonical form. It is in
+	# some cases the only manufacturer, in others, it is the most popular.
+	craynv-unknown)
+		vendor=cray
+		basic_os=${basic_os:-unicosmp}
+		;;
+	c90-unknown | c90-cray)
+		vendor=cray
+		basic_os=${Basic_os:-unicos}
+		;;
+	fx80-unknown)
+		vendor=alliant
+		;;
+	romp-unknown)
+		vendor=ibm
+		;;
+	mmix-unknown)
+		vendor=knuth
+		;;
+	microblaze-unknown | microblazeel-unknown)
+		vendor=xilinx
+		;;
+	rs6000-unknown)
+		vendor=ibm
+		;;
+	vax-unknown)
+		vendor=dec
+		;;
+	pdp11-unknown)
+		vendor=dec
+		;;
+	we32k-unknown)
+		vendor=att
+		;;
+	cydra-unknown)
+		vendor=cydrome
+		;;
+	i370-ibm*)
+		vendor=ibm
+		;;
+	orion-unknown)
+		vendor=highlevel
+		;;
+	xps-unknown | xps100-unknown)
+		cpu=xps100
+		vendor=honeywell
+		;;
+
+	# Here we normalize CPU types with a missing or matching vendor
+	armh-unknown | armh-alt)
+		cpu=armv7l
+		vendor=alt
+		basic_os=${basic_os:-linux-gnueabihf}
+		;;
+	dpx20-unknown | dpx20-bull)
+		cpu=rs6000
+		vendor=bull
+		basic_os=${basic_os:-bosx}
+		;;
+
+	# Here we normalize CPU types irrespective of the vendor
+	amd64-*)
+		cpu=x86_64
+		;;
+	blackfin-*)
+		cpu=bfin
+		basic_os=linux
+		;;
+	c54x-*)
+		cpu=tic54x
+		;;
+	c55x-*)
+		cpu=tic55x
+		;;
+	c6x-*)
+		cpu=tic6x
+		;;
+	e500v[12]-*)
+		cpu=powerpc
+		basic_os=${basic_os}"spe"
+		;;
+	mips3*-*)
+		cpu=mips64
+		;;
+	ms1-*)
+		cpu=mt
+		;;
+	m68knommu-*)
+		cpu=m68k
+		basic_os=linux
+		;;
+	m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+		cpu=s12z
+		;;
+	openrisc-*)
+		cpu=or32
+		;;
+	parisc-*)
+		cpu=hppa
+		basic_os=linux
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+		cpu=i586
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
+		cpu=i686
+		;;
+	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+		cpu=i686
+		;;
+	pentium4-*)
+		cpu=i786
+		;;
+	pc98-*)
+		cpu=i386
+		;;
+	ppc-* | ppcbe-*)
+		cpu=powerpc
+		;;
+	ppcle-* | powerpclittle-*)
+		cpu=powerpcle
+		;;
+	ppc64-*)
+		cpu=powerpc64
+		;;
+	ppc64le-* | powerpc64little-*)
+		cpu=powerpc64le
+		;;
+	sb1-*)
+		cpu=mipsisa64sb1
+		;;
+	sb1el-*)
+		cpu=mipsisa64sb1el
+		;;
+	sh5e[lb]-*)
+		cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
+		;;
+	spur-*)
+		cpu=spur
+		;;
+	strongarm-* | thumb-*)
+		cpu=arm
+		;;
+	tx39-*)
+		cpu=mipstx39
+		;;
+	tx39el-*)
+		cpu=mipstx39el
+		;;
+	x64-*)
+		cpu=x86_64
+		;;
+	xscale-* | xscalee[bl]-*)
+		cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
+		;;
+	arm64-* | aarch64le-*)
+		cpu=aarch64
+		;;
+
+	# Recognize the canonical CPU Types that limit and/or modify the
+	# company names they are paired with.
+	cr16-*)
+		basic_os=${basic_os:-elf}
+		;;
+	crisv32-* | etraxfs*-*)
+		cpu=crisv32
+		vendor=axis
+		;;
+	cris-* | etrax*-*)
+		cpu=cris
+		vendor=axis
+		;;
+	crx-*)
+		basic_os=${basic_os:-elf}
+		;;
+	neo-tandem)
+		cpu=neo
+		vendor=tandem
+		;;
+	nse-tandem)
+		cpu=nse
+		vendor=tandem
+		;;
+	nsr-tandem)
+		cpu=nsr
+		vendor=tandem
+		;;
+	nsv-tandem)
+		cpu=nsv
+		vendor=tandem
+		;;
+	nsx-tandem)
+		cpu=nsx
+		vendor=tandem
+		;;
+	mipsallegrexel-sony)
+		cpu=mipsallegrexel
+		vendor=sony
+		;;
+	tile*-*)
+		basic_os=${basic_os:-linux-gnu}
+		;;
+
+	*)
+		# Recognize the canonical CPU types that are allowed with any
+		# company name.
+		case $cpu in
+			1750a | 580 \
+			| a29k \
+			| aarch64 | aarch64_be \
+			| abacus \
+			| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
+			| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \
+			| alphapca5[67] | alpha64pca5[67] \
+			| am33_2.0 \
+			| amdgcn \
+			| arc | arceb | arc32 | arc64 \
+			| arm | arm[lb]e | arme[lb] | armv* \
+			| avr | avr32 \
+			| asmjs \
+			| ba \
+			| be32 | be64 \
+			| bfin | bpf | bs2000 \
+			| c[123]* | c30 | [cjt]90 | c4x \
+			| c8051 | clipper | craynv | csky | cydra \
+			| d10v | d30v | dlx | dsp16xx \
+			| e2k | elxsi | epiphany \
+			| f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
+			| h8300 | h8500 \
+			| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+			| hexagon \
+			| i370 | i*86 | i860 | i960 | ia16 | ia64 \
+			| ip2k | iq2000 \
+			| k1om \
+			| le32 | le64 \
+			| lm32 \
+			| loongarch32 | loongarch64 | loongarchx32 \
+			| m32c | m32r | m32rle \
+			| m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
+			| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
+			| m88110 | m88k | maxq | mb | mcore | mep | metag \
+			| microblaze | microblazeel \
+			| mips | mipsbe | mipseb | mipsel | mipsle \
+			| mips16 \
+			| mips64 | mips64eb | mips64el \
+			| mips64octeon | mips64octeonel \
+			| mips64orion | mips64orionel \
+			| mips64r5900 | mips64r5900el \
+			| mips64vr | mips64vrel \
+			| mips64vr4100 | mips64vr4100el \
+			| mips64vr4300 | mips64vr4300el \
+			| mips64vr5000 | mips64vr5000el \
+			| mips64vr5900 | mips64vr5900el \
+			| mipsisa32 | mipsisa32el \
+			| mipsisa32r2 | mipsisa32r2el \
+			| mipsisa32r3 | mipsisa32r3el \
+			| mipsisa32r5 | mipsisa32r5el \
+			| mipsisa32r6 | mipsisa32r6el \
+			| mipsisa64 | mipsisa64el \
+			| mipsisa64r2 | mipsisa64r2el \
+			| mipsisa64r3 | mipsisa64r3el \
+			| mipsisa64r5 | mipsisa64r5el \
+			| mipsisa64r6 | mipsisa64r6el \
+			| mipsisa64sb1 | mipsisa64sb1el \
+			| mipsisa64sr71k | mipsisa64sr71kel \
+			| mipsr5900 | mipsr5900el \
+			| mipstx39 | mipstx39el \
+			| mmix \
+			| mn10200 | mn10300 \
+			| moxie \
+			| mt \
+			| msp430 \
+			| nds32 | nds32le | nds32be \
+			| nfp \
+			| nios | nios2 | nios2eb | nios2el \
+			| none | np1 | ns16k | ns32k | nvptx \
+			| open8 \
+			| or1k* \
+			| or32 \
+			| orion \
+			| picochip \
+			| pdp10 | pdp11 | pj | pjl | pn | power \
+			| powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
+			| pru \
+			| pyramid \
+			| riscv | riscv32 | riscv32be | riscv64 | riscv64be \
+			| rl78 | romp | rs6000 | rx \
+			| s390 | s390x \
+			| score \
+			| sh | shl \
+			| sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
+			| sh[1234]e[lb] |  sh[12345][lb]e | sh[23]ele | sh64 | sh64le \
+			| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \
+			| sparclite \
+			| sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
+			| spu \
+			| tahoe \
+			| thumbv7* \
+			| tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
+			| tron \
+			| ubicom32 \
+			| v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
+			| vax \
+			| visium \
+			| w65 \
+			| wasm32 | wasm64 \
+			| we32k \
+			| x86 | x86_64 | xc16x | xgate | xps100 \
+			| xstormy16 | xtensa* \
+			| ymp \
+			| z8k | z80)
+				;;
+
+			*)
+				echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
+				exit 1
+				;;
+		esac
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $vendor in
+	digital*)
+		vendor=dec
+		;;
+	commodore*)
+		vendor=cbm
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if test x$basic_os != x
+then
+
+# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just
+# set os.
+case $basic_os in
+	gnu/linux*)
+		kernel=linux
+		os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'`
+		;;
+	os2-emx)
+		kernel=os2
+		os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'`
+		;;
+	nto-qnx*)
+		kernel=nto
+		os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'`
+		;;
+	*-*)
+		# shellcheck disable=SC2162
+		saved_IFS=$IFS
+		IFS="-" read kernel os <<EOF
+$basic_os
+EOF
+		IFS=$saved_IFS
+		;;
+	# Default OS when just kernel was specified
+	nto*)
+		kernel=nto
+		os=`echo "$basic_os" | sed -e 's|nto|qnx|'`
+		;;
+	linux*)
+		kernel=linux
+		os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
+		;;
+	*)
+		kernel=
+		os=$basic_os
+		;;
+esac
+
+# Now, normalize the OS (knowing we just have one component, it's not a kernel,
+# etc.)
+case $os in
+	# First match some system type aliases that might get confused
+	# with valid system types.
+	# solaris* is a basic system type, with this one exception.
+	auroraux)
+		os=auroraux
+		;;
+	bluegene*)
+		os=cnk
+		;;
+	solaris1 | solaris1.*)
+		os=`echo "$os" | sed -e 's|solaris1|sunos4|'`
+		;;
+	solaris)
+		os=solaris2
+		;;
+	unixware*)
+		os=sysv4.2uw
+		;;
+	# es1800 is here to avoid being matched by es* (a different OS)
+	es1800*)
+		os=ose
+		;;
+	# Some version numbers need modification
+	chorusos*)
+		os=chorusos
+		;;
+	isc)
+		os=isc2.2
+		;;
+	sco6)
+		os=sco5v6
+		;;
+	sco5)
+		os=sco3.2v5
+		;;
+	sco4)
+		os=sco3.2v4
+		;;
+	sco3.2.[4-9]*)
+		os=`echo "$os" | sed -e 's/sco3.2./sco3.2v/'`
+		;;
+	sco*v* | scout)
+		# Don't match below
+		;;
+	sco*)
+		os=sco3.2v2
+		;;
+	psos*)
+		os=psos
+		;;
+	qnx*)
+		os=qnx
+		;;
+	hiux*)
+		os=hiuxwe2
+		;;
+	lynx*178)
+		os=lynxos178
+		;;
+	lynx*5)
+		os=lynxos5
+		;;
+	lynxos*)
+		# don't get caught up in next wildcard
+		;;
+	lynx*)
+		os=lynxos
+		;;
+	mac[0-9]*)
+		os=`echo "$os" | sed -e 's|mac|macos|'`
+		;;
+	opened*)
+		os=openedition
+		;;
+	os400*)
+		os=os400
+		;;
+	sunos5*)
+		os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+		;;
+	sunos6*)
+		os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+		;;
+	wince*)
+		os=wince
+		;;
+	utek*)
+		os=bsd
+		;;
+	dynix*)
+		os=bsd
+		;;
+	acis*)
+		os=aos
+		;;
+	atheos*)
+		os=atheos
+		;;
+	syllable*)
+		os=syllable
+		;;
+	386bsd)
+		os=bsd
+		;;
+	ctix* | uts*)
+		os=sysv
+		;;
+	nova*)
+		os=rtmk-nova
+		;;
+	ns2)
+		os=nextstep2
+		;;
+	# Preserve the version number of sinix5.
+	sinix5.*)
+		os=`echo "$os" | sed -e 's|sinix|sysv|'`
+		;;
+	sinix*)
+		os=sysv4
+		;;
+	tpf*)
+		os=tpf
+		;;
+	triton*)
+		os=sysv3
+		;;
+	oss*)
+		os=sysv3
+		;;
+	svr4*)
+		os=sysv4
+		;;
+	svr3)
+		os=sysv3
+		;;
+	sysvr4)
+		os=sysv4
+		;;
+	ose*)
+		os=ose
+		;;
+	*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+		os=mint
+		;;
+	dicos*)
+		os=dicos
+		;;
+	pikeos*)
+		# Until real need of OS specific support for
+		# particular features comes up, bare metal
+		# configurations are quite functional.
+		case $cpu in
+		    arm*)
+			os=eabi
+			;;
+		    *)
+			os=elf
+			;;
+		esac
+		;;
+	*)
+		# No normalization, but not necessarily accepted, that comes below.
+		;;
+esac
+
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+kernel=
+case $cpu-$vendor in
+	score-*)
+		os=elf
+		;;
+	spu-*)
+		os=elf
+		;;
+	*-acorn)
+		os=riscix1.2
+		;;
+	arm*-rebel)
+		kernel=linux
+		os=gnu
+		;;
+	arm*-semi)
+		os=aout
+		;;
+	c4x-* | tic4x-*)
+		os=coff
+		;;
+	c8051-*)
+		os=elf
+		;;
+	clipper-intergraph)
+		os=clix
+		;;
+	hexagon-*)
+		os=elf
+		;;
+	tic54x-*)
+		os=coff
+		;;
+	tic55x-*)
+		os=coff
+		;;
+	tic6x-*)
+		os=coff
+		;;
+	# This must come before the *-dec entry.
+	pdp10-*)
+		os=tops20
+		;;
+	pdp11-*)
+		os=none
+		;;
+	*-dec | vax-*)
+		os=ultrix4.2
+		;;
+	m68*-apollo)
+		os=domain
+		;;
+	i386-sun)
+		os=sunos4.0.2
+		;;
+	m68000-sun)
+		os=sunos3
+		;;
+	m68*-cisco)
+		os=aout
+		;;
+	mep-*)
+		os=elf
+		;;
+	mips*-cisco)
+		os=elf
+		;;
+	mips*-*)
+		os=elf
+		;;
+	or32-*)
+		os=coff
+		;;
+	*-tti)	# must be before sparc entry or we get the wrong os.
+		os=sysv3
+		;;
+	sparc-* | *-sun)
+		os=sunos4.1.1
+		;;
+	pru-*)
+		os=elf
+		;;
+	*-be)
+		os=beos
+		;;
+	*-ibm)
+		os=aix
+		;;
+	*-knuth)
+		os=mmixware
+		;;
+	*-wec)
+		os=proelf
+		;;
+	*-winbond)
+		os=proelf
+		;;
+	*-oki)
+		os=proelf
+		;;
+	*-hp)
+		os=hpux
+		;;
+	*-hitachi)
+		os=hiux
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=sysv
+		;;
+	*-cbm)
+		os=amigaos
+		;;
+	*-dg)
+		os=dgux
+		;;
+	*-dolphin)
+		os=sysv3
+		;;
+	m68k-ccur)
+		os=rtu
+		;;
+	m88k-omron*)
+		os=luna
+		;;
+	*-next)
+		os=nextstep
+		;;
+	*-sequent)
+		os=ptx
+		;;
+	*-crds)
+		os=unos
+		;;
+	*-ns)
+		os=genix
+		;;
+	i370-*)
+		os=mvs
+		;;
+	*-gould)
+		os=sysv
+		;;
+	*-highlevel)
+		os=bsd
+		;;
+	*-encore)
+		os=bsd
+		;;
+	*-sgi)
+		os=irix
+		;;
+	*-siemens)
+		os=sysv4
+		;;
+	*-masscomp)
+		os=rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=uxpv
+		;;
+	*-rom68k)
+		os=coff
+		;;
+	*-*bug)
+		os=coff
+		;;
+	*-apple)
+		os=macos
+		;;
+	*-atari*)
+		os=mint
+		;;
+	*-wrs)
+		os=vxworks
+		;;
+	*)
+		os=none
+		;;
+esac
+
+fi
+
+# Now, validate our (potentially fixed-up) OS.
+case $os in
+	# Sometimes we do "kernel-libc", so those need to count as OSes.
+	musl* | newlib* | relibc* | uclibc*)
+		;;
+	# Likewise for "kernel-abi"
+	eabi* | gnueabi*)
+		;;
+	# VxWorks passes extra cpu info in the 4th filed.
+	simlinux | simwindows | spe)
+		;;
+	# Now accept the basic system types.
+	# The portable systems comes first.
+	# Each alternative MUST end in a * to match a version number.
+	gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+	     | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \
+	     | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
+	     | sym* |  plan9* | psp* | sim* | xray* | os68k* | v88r* \
+	     | hiux* | abug | nacl* | netware* | windows* \
+	     | os9* | macos* | osx* | ios* \
+	     | mpw* | magic* | mmixware* | mon960* | lnews* \
+	     | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
+	     | aos* | aros* | cloudabi* | sortix* | twizzler* \
+	     | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
+	     | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
+	     | mirbsd* | netbsd* | dicos* | openedition* | ose* \
+	     | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \
+	     | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \
+	     | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
+	     | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+	     | udi* | lites* | ieee* | go32* | aux* | hcos* \
+	     | chorusrdb* | cegcc* | glidix* | serenity* \
+	     | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+	     | midipix* | mingw32* | mingw64* | mint* \
+	     | uxpv* | beos* | mpeix* | udk* | moxiebox* \
+	     | interix* | uwin* | mks* | rhapsody* | darwin* \
+	     | openstep* | oskit* | conix* | pw32* | nonstopux* \
+	     | storm-chaos* | tops10* | tenex* | tops20* | its* \
+	     | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \
+	     | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \
+	     | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
+	     | skyos* | haiku* | rdos* | toppers* | drops* | es* \
+	     | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
+	     | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
+	     | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
+	     | fiwix* )
+		;;
+	# This one is extra strict with allowed versions
+	sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
+		# Don't forget version if it is 3.2v4 or newer.
+		;;
+	none)
+		;;
+	*)
+		echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2
+		exit 1
+		;;
+esac
+
+# As a final step for OS-related things, validate the OS-kernel combination
+# (given a valid OS), if there is a kernel.
+case $kernel-$os in
+	linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \
+		   | linux-musl* | linux-relibc* | linux-uclibc* )
+		;;
+	uclinux-uclibc* )
+		;;
+	-dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* )
+		# These are just libc implementations, not actual OSes, and thus
+		# require a kernel.
+		echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2
+		exit 1
+		;;
+	kfreebsd*-gnu* | kopensolaris*-gnu*)
+		;;
+	vxworks-simlinux | vxworks-simwindows | vxworks-spe)
+		;;
+	nto-qnx*)
+		;;
+	os2-emx)
+		;;
+	*-eabi* | *-gnueabi*)
+		;;
+	-*)
+		# Blank kernel with real OS is always fine.
+		;;
+	*-*)
+		echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2
+		exit 1
+		;;
+esac
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+case $vendor in
+	unknown)
+		case $cpu-$os in
+			*-riscix*)
+				vendor=acorn
+				;;
+			*-sunos*)
+				vendor=sun
+				;;
+			*-cnk* | *-aix*)
+				vendor=ibm
+				;;
+			*-beos*)
+				vendor=be
+				;;
+			*-hpux*)
+				vendor=hp
+				;;
+			*-mpeix*)
+				vendor=hp
+				;;
+			*-hiux*)
+				vendor=hitachi
+				;;
+			*-unos*)
+				vendor=crds
+				;;
+			*-dgux*)
+				vendor=dg
+				;;
+			*-luna*)
+				vendor=omron
+				;;
+			*-genix*)
+				vendor=ns
+				;;
+			*-clix*)
+				vendor=intergraph
+				;;
+			*-mvs* | *-opened*)
+				vendor=ibm
+				;;
+			*-os400*)
+				vendor=ibm
+				;;
+			s390-* | s390x-*)
+				vendor=ibm
+				;;
+			*-ptx*)
+				vendor=sequent
+				;;
+			*-tpf*)
+				vendor=ibm
+				;;
+			*-vxsim* | *-vxworks* | *-windiss*)
+				vendor=wrs
+				;;
+			*-aux*)
+				vendor=apple
+				;;
+			*-hms*)
+				vendor=hitachi
+				;;
+			*-mpw* | *-macos*)
+				vendor=apple
+				;;
+			*-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
+				vendor=atari
+				;;
+			*-vos*)
+				vendor=stratus
+				;;
+		esac
+		;;
+esac
+
+echo "$cpu-$vendor-${kernel:+$kernel-}$os"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
Index: simdzone/configure
===================================================================
RCS file: simdzone/configure
diff -N simdzone/configure
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/configure	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,5370 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.71 for simdzone 0.2.0.
+#
+# Report bugs to <https://github.com/NLnetLabs/simdzone/issues>.
+#
+#
+# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
+# Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else $as_nop
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
+as_nl='
+'
+export as_nl
+IFS=" ""	$as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh).  This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2)            ; then :; else exec 2>/dev/null; fi
+
+# The user is always right.
+if ${PATH_SEPARATOR+false} :; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    test -r "$as_dir$0" && as_myself=$as_dir$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="as_nop=:
+if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else \$as_nop
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" )
+then :
+
+else \$as_nop
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+blah=\$(echo \$(echo blah))
+test x\"\$blah\" = xblah || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1"
+  if (eval "$as_required") 2>/dev/null
+then :
+  as_have_required=yes
+else $as_nop
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null
+then :
+
+else $as_nop
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+  as_found=:
+  case $as_dir in #(
+	 /*)
+	   for as_base in sh bash ksh sh5; do
+	     # Try only shells that exist, to save several forks.
+	     as_shell=$as_dir$as_base
+	     if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+		    as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+		   if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null
+then :
+  break 2
+fi
+fi
+	   done;;
+       esac
+  as_found=false
+done
+IFS=$as_save_IFS
+if $as_found
+then :
+
+else $as_nop
+  if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+	      as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi
+fi
+
+
+      if test "x$CONFIG_SHELL" != x
+then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno
+then :
+  printf "%s\n" "$0: This script requires a shell more modern than all"
+  printf "%s\n" "$0: the shells that I found on your system."
+  if test ${ZSH_VERSION+y} ; then
+    printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and
+$0: https://github.com/NLnetLabs/simdzone/issues about your
+$0: system, including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+# as_fn_nop
+# ---------
+# Do nothing but, unlike ":", preserve the value of $?.
+as_fn_nop ()
+{
+  return $?
+}
+as_nop=as_fn_nop
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else $as_nop
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else $as_nop
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+# as_fn_nop
+# ---------
+# Do nothing but, unlike ":", preserve the value of $?.
+as_fn_nop ()
+{
+  return $?
+}
+as_nop=as_fn_nop
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  printf "%s\n" "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n.  New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='simdzone'
+PACKAGE_TARNAME='simdzone'
+PACKAGE_VERSION='0.2.0'
+PACKAGE_STRING='simdzone 0.2.0'
+PACKAGE_BUGREPORT='https://github.com/NLnetLabs/simdzone/issues'
+PACKAGE_URL=''
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stddef.h>
+#ifdef HAVE_STDIO_H
+# include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_header_c_list=
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+HAVE_HASWELL
+HAVE_WESTMERE
+HAVE_ENDIAN_H
+target_os
+target_vendor
+target_cpu
+target
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+DEPFLAGS
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_westmere
+enable_haswell
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
+		datadir sysconfdir sharedstatedir localstatedir includedir \
+		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+		libdir localedir mandir runstatedir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_myself" : 'X\(//\)[^/]' \| \
+	 X"$as_myself" : 'X\(//\)$' \| \
+	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+	pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures simdzone 0.2.0 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/simdzone]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+  --target=TARGET   configure for building compilers for TARGET [HOST]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of simdzone 0.2.0:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --disable-westmere      Disable Westmere (SSE4.2) kernel
+  --disable-haswell       Disable Haswell (AVX2) kernel
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <https://github.com/NLnetLabs/simdzone/issues>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for configure.gnu first; this name is used for a wrapper for
+    # Metaconfig's "Configure" on case-insensitive file systems.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+simdzone configure 0.2.0
+generated by GNU Autoconf 2.71
+
+Copyright (C) 2021 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest.beam
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext
+then :
+  ac_retval=0
+else $as_nop
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  eval "$3=yes"
+else $as_nop
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR
+# ------------------------------------------------------------------
+# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
+# accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR.
+ac_fn_check_decl ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  as_decl_name=`echo $2|sed 's/ *(.*//'`
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
+printf %s "checking whether $as_decl_name is declared... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
+  eval ac_save_FLAGS=\$$6
+  as_fn_append $6 " $5"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+#ifndef $as_decl_name
+#ifdef __cplusplus
+  (void) $as_decl_use;
+#else
+  (void) $as_decl_name;
+#endif
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  eval "$3=yes"
+else $as_nop
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  eval $6=\$ac_save_FLAGS
+
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_check_decl
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 test -x conftest$ac_exeext
+       }
+then :
+  ac_retval=0
+else $as_nop
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+   which can conflict with char $2 (); below.  */
+
+#include <limits.h>
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main (void)
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  eval "$3=yes"
+else $as_nop
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+ac_configure_args_raw=
+for ac_arg
+do
+  case $ac_arg in
+  *\'*)
+    ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+  esac
+  as_fn_append ac_configure_args_raw " '$ac_arg'"
+done
+
+case $ac_configure_args_raw in
+  *$as_nl*)
+    ac_safe_unquote= ;;
+  *)
+    ac_unsafe_z='|&;<>()$`\\"*?[ ''	' # This string ends in space, tab.
+    ac_unsafe_a="$ac_unsafe_z#~"
+    ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g"
+    ac_configure_args_raw=`      printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;;
+esac
+
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by simdzone $as_me 0.2.0, which was
+generated by GNU Autoconf 2.71.  Invocation command line was
+
+  $ $0$ac_configure_args_raw
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    printf "%s\n" "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Sanitize IFS.
+  IFS=" ""	$as_nl"
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    printf "%s\n" "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+	"s/'\''/'\''\\\\'\'''\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    printf "%s\n" "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      printf "%s\n" "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      printf "%s\n" "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=\$$ac_var
+	case $ac_val in
+	*\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+	esac
+	printf "%s\n" "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      printf "%s\n" "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      printf "%s\n" "$as_me: caught signal $ac_signal"
+    printf "%s\n" "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+printf "%s\n" "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+if test -n "$CONFIG_SITE"; then
+  ac_site_files="$CONFIG_SITE"
+elif test "x$prefix" != xNONE; then
+  ac_site_files="$prefix/share/config.site $prefix/etc/config.site"
+else
+  ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+fi
+
+for ac_site_file in $ac_site_files
+do
+  case $ac_site_file in #(
+  */*) :
+     ;; #(
+  *) :
+    ac_site_file=./$ac_site_file ;;
+esac
+  if test -f "$ac_site_file" && test -r "$ac_site_file"; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+printf "%s\n" "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+printf "%s\n" "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Test code for whether the C compiler supports C89 (global declarations)
+ac_c_conftest_c89_globals='
+/* Does the compiler advertise C89 conformance?
+   Do not test the value of __STDC__, because some compilers set it to 0
+   while being otherwise adequately conformant. */
+#if !defined __STDC__
+# error "Compiler does not advertise C89 conformance"
+#endif
+
+#include <stddef.h>
+#include <stdarg.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7 src/conf.sh.  */
+struct buf { int x; };
+struct buf * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not \xHH hex character constants.
+   These do not provoke an error unfortunately, instead are silently treated
+   as an "x".  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously \x00 != x always comes out true, for an
+   array size at least.  It is necessary to write \x00 == 0 to get something
+   that is true only with -std.  */
+int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) '\''x'\''
+int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int),
+               int, int);'
+
+# Test code for whether the C compiler supports C89 (body of main).
+ac_c_conftest_c89_main='
+ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]);
+'
+
+# Test code for whether the C compiler supports C99 (global declarations)
+ac_c_conftest_c99_globals='
+// Does the compiler advertise C99 conformance?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
+# error "Compiler does not advertise C99 conformance"
+#endif
+
+#include <stdbool.h>
+extern int puts (const char *);
+extern int printf (const char *, ...);
+extern int dprintf (int, const char *, ...);
+extern void *malloc (size_t);
+
+// Check varargs macros.  These examples are taken from C99 6.10.3.5.
+// dprintf is used instead of fprintf to avoid needing to declare
+// FILE and stderr.
+#define debug(...) dprintf (2, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+  int x = 1234;
+  int y = 5678;
+  debug ("Flag");
+  debug ("X = %d\n", x);
+  showlist (The first, second, and third items.);
+  report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+  #error "your preprocessor is broken"
+#endif
+#if BIG_OK
+#else
+  #error "your preprocessor is broken"
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+  int datasize;
+  double data[];
+};
+
+struct named_init {
+  int number;
+  const wchar_t *name;
+  double average;
+};
+
+typedef const char *ccp;
+
+static inline int
+test_restrict (ccp restrict text)
+{
+  // See if C++-style comments work.
+  // Iterate through items via the restricted pointer.
+  // Also check for declarations in for loops.
+  for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i)
+    continue;
+  return 0;
+}
+
+// Check varargs and va_copy.
+static bool
+test_varargs (const char *format, ...)
+{
+  va_list args;
+  va_start (args, format);
+  va_list args_copy;
+  va_copy (args_copy, args);
+
+  const char *str = "";
+  int number = 0;
+  float fnumber = 0;
+
+  while (*format)
+    {
+      switch (*format++)
+	{
+	case '\''s'\'': // string
+	  str = va_arg (args_copy, const char *);
+	  break;
+	case '\''d'\'': // int
+	  number = va_arg (args_copy, int);
+	  break;
+	case '\''f'\'': // float
+	  fnumber = va_arg (args_copy, double);
+	  break;
+	default:
+	  break;
+	}
+    }
+  va_end (args_copy);
+  va_end (args);
+
+  return *str && number && fnumber;
+}
+'
+
+# Test code for whether the C compiler supports C99 (body of main).
+ac_c_conftest_c99_main='
+  // Check bool.
+  _Bool success = false;
+  success |= (argc != 0);
+
+  // Check restrict.
+  if (test_restrict ("String literal") == 0)
+    success = true;
+  char *restrict newvar = "Another string";
+
+  // Check varargs.
+  success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234);
+  test_varargs_macros ();
+
+  // Check flexible array members.
+  struct incomplete_array *ia =
+    malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+  ia->datasize = 10;
+  for (int i = 0; i < ia->datasize; ++i)
+    ia->data[i] = i * 1.234;
+
+  // Check named initializers.
+  struct named_init ni = {
+    .number = 34,
+    .name = L"Test wide string",
+    .average = 543.34343,
+  };
+
+  ni.number = 58;
+
+  int dynamic_array[ni.number];
+  dynamic_array[0] = argv[0][0];
+  dynamic_array[ni.number - 1] = 543;
+
+  // work around unused variable warnings
+  ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\''
+	 || dynamic_array[ni.number - 1] != 543);
+'
+
+# Test code for whether the C compiler supports C11 (global declarations)
+ac_c_conftest_c11_globals='
+// Does the compiler advertise C11 conformance?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L
+# error "Compiler does not advertise C11 conformance"
+#endif
+
+// Check _Alignas.
+char _Alignas (double) aligned_as_double;
+char _Alignas (0) no_special_alignment;
+extern char aligned_as_int;
+char _Alignas (0) _Alignas (int) aligned_as_int;
+
+// Check _Alignof.
+enum
+{
+  int_alignment = _Alignof (int),
+  int_array_alignment = _Alignof (int[100]),
+  char_alignment = _Alignof (char)
+};
+_Static_assert (0 < -_Alignof (int), "_Alignof is signed");
+
+// Check _Noreturn.
+int _Noreturn does_not_return (void) { for (;;) continue; }
+
+// Check _Static_assert.
+struct test_static_assert
+{
+  int x;
+  _Static_assert (sizeof (int) <= sizeof (long int),
+                  "_Static_assert does not work in struct");
+  long int y;
+};
+
+// Check UTF-8 literals.
+#define u8 syntax error!
+char const utf8_literal[] = u8"happens to be ASCII" "another string";
+
+// Check duplicate typedefs.
+typedef long *long_ptr;
+typedef long int *long_ptr;
+typedef long_ptr long_ptr;
+
+// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1.
+struct anonymous
+{
+  union {
+    struct { int i; int j; };
+    struct { int k; long int l; } w;
+  };
+  int m;
+} v1;
+'
+
+# Test code for whether the C compiler supports C11 (body of main).
+ac_c_conftest_c11_main='
+  _Static_assert ((offsetof (struct anonymous, i)
+		   == offsetof (struct anonymous, w.k)),
+		  "Anonymous union alignment botch");
+  v1.i = 2;
+  v1.w.k = 5;
+  ok |= v1.i != 5;
+'
+
+# Test code for whether the C compiler supports C11 (complete).
+ac_c_conftest_c11_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+${ac_c_conftest_c11_globals}
+
+int
+main (int argc, char **argv)
+{
+  int ok = 0;
+  ${ac_c_conftest_c89_main}
+  ${ac_c_conftest_c99_main}
+  ${ac_c_conftest_c11_main}
+  return ok;
+}
+"
+
+# Test code for whether the C compiler supports C99 (complete).
+ac_c_conftest_c99_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+
+int
+main (int argc, char **argv)
+{
+  int ok = 0;
+  ${ac_c_conftest_c89_main}
+  ${ac_c_conftest_c99_main}
+  return ok;
+}
+"
+
+# Test code for whether the C compiler supports C89 (complete).
+ac_c_conftest_c89_program="${ac_c_conftest_c89_globals}
+
+int
+main (int argc, char **argv)
+{
+  int ok = 0;
+  ${ac_c_conftest_c89_main}
+  return ok;
+}
+"
+
+as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H"
+as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H"
+as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H"
+as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H"
+as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H"
+as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H"
+as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H"
+as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H"
+as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H"
+
+# Auxiliary files required by this configure script.
+ac_aux_files="config.guess config.sub"
+
+# Locations in which to look for auxiliary files.
+ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.."
+
+# Search for a directory containing all of the required auxiliary files,
+# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates.
+# If we don't find one directory that contains all the files we need,
+# we report the set of missing files from the *first* directory in
+# $ac_aux_dir_candidates and give up.
+ac_missing_aux_files=""
+ac_first_candidate=:
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in $ac_aux_dir_candidates
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+  as_found=:
+
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}:  trying $as_dir" >&5
+  ac_aux_dir_found=yes
+  ac_install_sh=
+  for ac_aux in $ac_aux_files
+  do
+    # As a special case, if "install-sh" is required, that requirement
+    # can be satisfied by any of "install-sh", "install.sh", or "shtool",
+    # and $ac_install_sh is set appropriately for whichever one is found.
+    if test x"$ac_aux" = x"install-sh"
+    then
+      if test -f "${as_dir}install-sh"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}install-sh found" >&5
+        ac_install_sh="${as_dir}install-sh -c"
+      elif test -f "${as_dir}install.sh"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}install.sh found" >&5
+        ac_install_sh="${as_dir}install.sh -c"
+      elif test -f "${as_dir}shtool"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}shtool found" >&5
+        ac_install_sh="${as_dir}shtool install -c"
+      else
+        ac_aux_dir_found=no
+        if $ac_first_candidate; then
+          ac_missing_aux_files="${ac_missing_aux_files} install-sh"
+        else
+          break
+        fi
+      fi
+    else
+      if test -f "${as_dir}${ac_aux}"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}${ac_aux} found" >&5
+      else
+        ac_aux_dir_found=no
+        if $ac_first_candidate; then
+          ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}"
+        else
+          break
+        fi
+      fi
+    fi
+  done
+  if test "$ac_aux_dir_found" = yes; then
+    ac_aux_dir="$as_dir"
+    break
+  fi
+  ac_first_candidate=false
+
+  as_found=false
+done
+IFS=$as_save_IFS
+if $as_found
+then :
+
+else $as_nop
+  as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5
+fi
+
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+if test -f "${ac_aux_dir}config.guess"; then
+  ac_config_guess="$SHELL ${ac_aux_dir}config.guess"
+fi
+if test -f "${ac_aux_dir}config.sub"; then
+  ac_config_sub="$SHELL ${ac_aux_dir}config.sub"
+fi
+if test -f "$ac_aux_dir/configure"; then
+  ac_configure="$SHELL ${ac_aux_dir}configure"
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	# differences in whitespace do not lead to failure.
+	ac_old_val_w=`echo x $ac_old_val`
+	ac_new_val_w=`echo x $ac_new_val`
+	if test "$ac_old_val_w" != "$ac_new_val_w"; then
+	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	  ac_cache_corrupted=:
+	else
+	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+	  eval $ac_var=\$ac_old_val
+	fi
+	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+printf "%s\n" "$as_me:   former value:  \`$ac_old_val'" >&2;}
+	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+printf "%s\n" "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file'
+	    and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+ac_config_files="$ac_config_files Makefile"
+
+
+# ===========================================================================
+#  https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+#   Check whether the given FLAG works with the current language's compiler
+#   or gives an error.  (Warnings, however, are ignored)
+#
+#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+#   success/failure.
+#
+#   If EXTRA-FLAGS is defined, it is added to the current language's default
+#   flags (e.g. CFLAGS) when the check is done.  The check is thus made with
+#   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to
+#   force the compiler to issue an error when a bad flag is given.
+#
+#   INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+#   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 6
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args.
+set dummy ${ac_tool_prefix}clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}clang"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "clang", so it can be a program name with args.
+set dummy clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="clang"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+
+
+test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion -version; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+printf %s "checking whether the C compiler works... " >&6; }
+ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no;
+	then :; else
+	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	fi
+	# We set ac_cv_exeext here because the later test for it is not
+	# safe: cross compilers may not add the suffix if given an `-o'
+	# argument, so we may need to know it at that point already.
+	# Even if this section looks crufty: it has the advantage of
+	# actually working.
+	break;;
+    * )
+	break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else $as_nop
+  ac_file=''
+fi
+if test -z "$ac_file"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+printf %s "checking for C compiler default output file name... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+printf "%s\n" "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+printf %s "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  break;;
+    * ) break;;
+  esac
+done
+else $as_nop
+  { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+printf "%s\n" "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main (void)
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+printf %s "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+printf "%s\n" "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+printf %s "checking for suffix of object files... " >&6; }
+if test ${ac_cv_objext+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else $as_nop
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+printf "%s\n" "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5
+printf %s "checking whether the compiler supports GNU C... " >&6; }
+if test ${ac_cv_c_compiler_gnu+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_compiler_gnu=yes
+else $as_nop
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+y}
+ac_save_CFLAGS=$CFLAGS
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+printf %s "checking whether $CC accepts -g... " >&6; }
+if test ${ac_cv_prog_cc_g+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_g=yes
+else $as_nop
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+  ac_c_werror_flag=$ac_save_c_werror_flag
+	 CFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
+if test $ac_test_CFLAGS; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+ac_prog_cc_stdc=no
+if test x$ac_prog_cc_stdc = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
+printf %s "checking for $CC option to enable C11 features... " >&6; }
+if test ${ac_cv_prog_cc_c11+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_prog_cc_c11=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_c_conftest_c11_program
+_ACEOF
+for ac_arg in '' -std=gnu11
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_c11=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+  test "x$ac_cv_prog_cc_c11" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c11" = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+  if test "x$ac_cv_prog_cc_c11" = x
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
+printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
+     CC="$CC $ac_cv_prog_cc_c11"
+fi
+  ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
+  ac_prog_cc_stdc=c11
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
+printf %s "checking for $CC option to enable C99 features... " >&6; }
+if test ${ac_cv_prog_cc_c99+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_c_conftest_c99_program
+_ACEOF
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99=
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_c99=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+  test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c99" = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+  if test "x$ac_cv_prog_cc_c99" = x
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
+     CC="$CC $ac_cv_prog_cc_c99"
+fi
+  ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+  ac_prog_cc_stdc=c99
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
+printf %s "checking for $CC option to enable C89 features... " >&6; }
+if test ${ac_cv_prog_cc_c89+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_c_conftest_c89_program
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c89" = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+  if test "x$ac_cv_prog_cc_c89" = x
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
+     CC="$CC $ac_cv_prog_cc_c89"
+fi
+  ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+  ac_prog_cc_stdc=c89
+fi
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_header= ac_cache=
+for ac_item in $ac_header_c_list
+do
+  if test $ac_cache; then
+    ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default"
+    if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then
+      printf "%s\n" "#define $ac_item 1" >> confdefs.h
+    fi
+    ac_header= ac_cache=
+  elif test $ac_header; then
+    ac_cache=$ac_item
+  else
+    ac_header=$ac_item
+  fi
+done
+
+
+
+
+
+
+
+
+if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes
+then :
+
+printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "endian.h" "ac_cv_header_endian_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_endian_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_ENDIAN_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/endian.h" "ac_cv_header_sys_endian_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_sys_endian_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_ENDIAN_H 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5
+printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; }
+if test ${ac_cv_c_undeclared_builtin_options+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_save_CFLAGS=$CFLAGS
+   ac_cv_c_undeclared_builtin_options='cannot detect'
+   for ac_arg in '' -fno-builtin; do
+     CFLAGS="$ac_save_CFLAGS $ac_arg"
+     # This test program should *not* compile successfully.
+     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+(void) strchr;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+  # This test program should compile successfully.
+        # No library function is consistently available on
+        # freestanding implementations, so test against a dummy
+        # declaration.  Include always-available headers on the
+        # off chance that they somehow elicit warnings.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <float.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+extern void ac_decl (int, char *);
+
+int
+main (void)
+{
+(void) ac_decl (0, (char *) 0);
+  (void) ac_decl;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  if test x"$ac_arg" = x
+then :
+  ac_cv_c_undeclared_builtin_options='none needed'
+else $as_nop
+  ac_cv_c_undeclared_builtin_options=$ac_arg
+fi
+          break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+    done
+    CFLAGS=$ac_save_CFLAGS
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5
+printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; }
+  case $ac_cv_c_undeclared_builtin_options in #(
+  'cannot detect') :
+    { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot make $CC report undeclared builtins
+See \`config.log' for more details" "$LINENO" 5; } ;; #(
+  'none needed') :
+    ac_c_undeclared_builtin_options='' ;; #(
+  *) :
+    ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;;
+esac
+
+ac_fn_check_decl "$LINENO" "bswap16" "ac_cv_have_decl_bswap16" "
+$ac_includes_default
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#endif
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#endif
+
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_bswap16" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_BSWAP16 $ac_have_decl" >>confdefs.h
+ac_fn_check_decl "$LINENO" "bswap32" "ac_cv_have_decl_bswap32" "
+$ac_includes_default
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#endif
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#endif
+
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_bswap32" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_BSWAP32 $ac_have_decl" >>confdefs.h
+ac_fn_check_decl "$LINENO" "bswap64" "ac_cv_have_decl_bswap64" "
+$ac_includes_default
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#endif
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#endif
+
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_bswap64" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_BSWAP64 $ac_have_decl" >>confdefs.h
+
+
+# Check whether --enable-westmere was given.
+if test ${enable_westmere+y}
+then :
+  enableval=$enable_westmere;
+fi
+
+case "$enable_westmere" in
+  no)    enable_westmere=no ;;
+  yes|*) enable_westmere=yes ;;
+esac
+
+# Check whether --enable-haswell was given.
+if test ${enable_haswell+y}
+then :
+  enableval=$enable_haswell;
+fi
+
+case "$enable_haswell" in
+  no)    enable_haswell=no ;;
+  yes|*) enable_haswell=yes ;;
+esac
+
+# GCC and Clang
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -MMD" >&5
+printf %s "checking whether C compiler accepts -MMD... " >&6; }
+if test ${ax_cv_check_cflags___MMD+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+
+  ax_check_save_flags=$CFLAGS
+  CFLAGS="$CFLAGS  -MMD"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ax_cv_check_cflags___MMD=yes
+else $as_nop
+  ax_cv_check_cflags___MMD=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  CFLAGS=$ax_check_save_flags
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___MMD" >&5
+printf "%s\n" "$ax_cv_check_cflags___MMD" >&6; }
+if test "x$ax_cv_check_cflags___MMD" = xyes
+then :
+  DEPFLAGS="-MMD -MP"
+else $as_nop
+  :
+fi
+
+# Oracle Developer Studio (no -MP)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -xMMD" >&5
+printf %s "checking whether C compiler accepts -xMMD... " >&6; }
+if test ${ax_cv_check_cflags___xMMD+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+
+  ax_check_save_flags=$CFLAGS
+  CFLAGS="$CFLAGS  -xMMD"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ax_cv_check_cflags___xMMD=yes
+else $as_nop
+  ax_cv_check_cflags___xMMD=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  CFLAGS=$ax_check_save_flags
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___xMMD" >&5
+printf "%s\n" "$ax_cv_check_cflags___xMMD" >&6; }
+if test "x$ax_cv_check_cflags___xMMD" = xyes
+then :
+  DEPFLAGS="-xMMD"
+else $as_nop
+  :
+fi
+
+
+
+
+# Figure out the canonical target architecture.
+
+
+
+  # Make sure we can run config.sub.
+$SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 ||
+  as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+printf %s "checking build system type... " >&6; }
+if test ${ac_cv_build+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+  ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"`
+test "x$ac_build_alias" = x &&
+  as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` ||
+  as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+printf "%s\n" "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+printf %s "checking host system type... " >&6; }
+if test ${ac_cv_host+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test "x$host_alias" = x; then
+  ac_cv_host=$ac_cv_build
+else
+  ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` ||
+    as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+printf "%s\n" "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking target system type" >&5
+printf %s "checking target system type... " >&6; }
+if test ${ac_cv_target+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test "x$target_alias" = x; then
+  ac_cv_target=$ac_cv_host
+else
+  ac_cv_target=`$SHELL "${ac_aux_dir}config.sub" $target_alias` ||
+    as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $target_alias failed" "$LINENO" 5
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5
+printf "%s\n" "$ac_cv_target" >&6; }
+case $ac_cv_target in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;;
+esac
+target=$ac_cv_target
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_target
+shift
+target_cpu=$1
+target_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+target_os=$*
+IFS=$ac_save_IFS
+case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac
+
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+test -n "$target_alias" &&
+  test "$program_prefix$program_suffix$program_transform_name" = \
+    NONENONEs,x,x, &&
+  program_prefix=${target_alias}-
+
+# Multiple instruction sets may be supported by a specific architecture.
+# e.g. x86_64 may (or may not) support any of SSE42, AVX2 and AVX-512. The
+# best instruction set is automatically selected at runtime, but the compiler
+# may or may not support generating code for an instruction set.
+case "$target" in
+  *amd64*)  x86_64=yes ;;
+  *x86_64*) x86_64=yes ;;
+  *)        x86_64=no  ;;
+esac
+
+HAVE_WESTMERE=NO
+HAVE_HASWELL=NO
+
+if test $x86_64 = "yes"; then
+  ac_fn_c_check_header_compile "$LINENO" "immintrin.h" "ac_cv_header_immintrin_h" "$ac_includes_default"
+if test "x$ac_cv_header_immintrin_h" = xyes
+then :
+
+fi
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -march=westmere" >&5
+printf %s "checking whether C compiler accepts -march=westmere... " >&6; }
+if test ${ax_cv_check_cflags__Werror__march_westmere+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+
+  ax_check_save_flags=$CFLAGS
+  CFLAGS="$CFLAGS -Werror -march=westmere"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ax_cv_check_cflags__Werror__march_westmere=yes
+else $as_nop
+  ax_cv_check_cflags__Werror__march_westmere=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  CFLAGS=$ax_check_save_flags
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__march_westmere" >&5
+printf "%s\n" "$ax_cv_check_cflags__Werror__march_westmere" >&6; }
+if test "x$ax_cv_check_cflags__Werror__march_westmere" = xyes
+then :
+  :
+else $as_nop
+  :
+fi
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -march=haswell" >&5
+printf %s "checking whether C compiler accepts -march=haswell... " >&6; }
+if test ${ax_cv_check_cflags__Werror__march_haswell+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+
+  ax_check_save_flags=$CFLAGS
+  CFLAGS="$CFLAGS -Werror -march=haswell"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ax_cv_check_cflags__Werror__march_haswell=yes
+else $as_nop
+  ax_cv_check_cflags__Werror__march_haswell=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  CFLAGS=$ax_check_save_flags
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__march_haswell" >&5
+printf "%s\n" "$ax_cv_check_cflags__Werror__march_haswell" >&6; }
+if test "x$ax_cv_check_cflags__Werror__march_haswell" = xyes
+then :
+  :
+else $as_nop
+  :
+fi
+
+
+  # Check if the arch instruction set support includes the simd instructions.
+  if test $enable_westmere != "no" -a \
+          $ax_cv_check_cflags__Werror__march_westmere = "yes" -a \
+          $ac_cv_header_immintrin_h = "yes" ; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether -march=westmere works" >&5
+printf %s "checking whether -march=westmere works... " >&6; }
+    BAKCFLAGS="$CFLAGS"
+    CFLAGS="-march=westmere $CFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+$ac_includes_default
+
+#include <stdint.h>
+#include <immintrin.h>
+
+int main(int argc, char *argv[])
+{
+  (void)argv;
+  uint64_t popcnt = _mm_popcnt_u64((uint64_t)argc);
+  return popcnt == 11;
+}
+
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+
+printf "%s\n" "#define HAVE_WESTMERE 1" >>confdefs.h
+
+    HAVE_WESTMERE=WESTMERE
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+else $as_nop
+
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+    CFLAGS="$BAKCFLAGS"
+  fi
+
+  if test $enable_haswell != "no" -a \
+          $ax_cv_check_cflags__Werror__march_haswell = "yes" -a \
+          $ac_cv_header_immintrin_h = "yes" ; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether -march=haswell works" >&5
+printf %s "checking whether -march=haswell works... " >&6; }
+    BAKCFLAGS="$CFLAGS"
+    CFLAGS="-march=haswell $CFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+$ac_includes_default
+
+#include <stdint.h>
+#include <immintrin.h>
+
+int main(int argc, char *argv[])
+{
+  (void)argv;
+  int argc32x8[8] = { argc, 0, 0, 0, 0, 0, 0, 0 };
+  __m256i argc256 = _mm256_loadu_si256((__m256i *)argc32x8);
+  return _mm256_testz_si256(argc256, _mm256_set1_epi8(11));
+}
+
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+
+printf "%s\n" "#define HAVE_HASWELL 1" >>confdefs.h
+
+    HAVE_HASWELL=HASWELL
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+else $as_nop
+
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+    CFLAGS="$BAKCFLAGS"
+  fi
+fi
+
+
+  for ac_func in realpath
+do :
+  ac_fn_c_check_func "$LINENO" "realpath" "ac_cv_func_realpath"
+if test "x$ac_cv_func_realpath" = xyes
+then :
+  printf "%s\n" "#define HAVE_REALPATH 1" >>confdefs.h
+
+else $as_nop
+  as_fn_error $? "realpath is not available" "$LINENO" 5
+fi
+
+done
+
+
+
+
+
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+printf "%s\n" "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+	cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+	  mv -f confcache "$cache_file"$$ &&
+	  mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+	  mv -f confcache "$cache_file" ;;
+	esac
+      fi
+    fi
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else $as_nop
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
+as_nl='
+'
+export as_nl
+IFS=" ""	$as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh).  This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2)            ; then :; else exec 2>/dev/null; fi
+
+# The user is always right.
+if ${PATH_SEPARATOR+false} :; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    test -r "$as_dir$0" && as_myself=$as_dir$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  printf "%s\n" "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else $as_nop
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else $as_nop
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n.  New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by simdzone $as_me 0.2.0, which was
+generated by GNU Autoconf 2.71.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <https://github.com/NLnetLabs/simdzone/issues>."
+
+_ACEOF
+ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
+ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"`
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config='$ac_cs_config_escaped'
+ac_cs_version="\\
+simdzone config.status 0.2.0
+configured by $0, generated by GNU Autoconf 2.71,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2021 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    printf "%s\n" "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    printf "%s\n" "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    printf "%s\n" "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  printf "%s\n" "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files
+  test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "␇"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=[	 ]*/{
+h
+s///
+s/^/:/
+s/[	 ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[	 ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[	 ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[	 ]*#[	 ]*define[	 ][	 ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "␇"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
+	 # because $ac_f cannot contain `:'.
+	 test -f "$ac_f" ||
+	   case $ac_f in
+	   [\\/$]*) false;;
+	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+	   esac ||
+	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+	  printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+	`' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+printf "%s\n" "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`printf "%s\n" "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[	 ]*datarootdir[	 ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      printf "%s\n" "/* $configure_input  */" >&1 \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+printf "%s\n" "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+	|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    printf "%s\n" "/* $configure_input  */" >&1 \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
+
+
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
Index: simdzone/configure.ac
===================================================================
RCS file: simdzone/configure.ac
diff -N simdzone/configure.ac
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/configure.ac	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,146 @@
+#
+# configure.ac -- Autoconf script for simdzone
+#
+# Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+# This file is intended for inclusion by configure.ac in NSD. Support for any
+# platform not supported by NSD here is undesirable. Builds for standalone use
+# or development/testing are required to use CMake.
+
+AC_INIT([simdzone],[0.2.0],[https://github.com/NLnetLabs/simdzone/issues])
+
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_FILES([Makefile])
+
+m4_include(m4/ax_check_compile_flag.m4)
+m4_version_prereq([2.70], [AC_PROG_CC], [AC_PROG_CC_STDC])
+
+AC_CHECK_HEADERS([endian.h sys/endian.h],,, [AC_INCLUDES_DEFAULT])
+AC_CHECK_DECLS([bswap16,bswap32,bswap64], [], [], [
+AC_INCLUDES_DEFAULT
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#endif
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#endif
+])
+
+AC_ARG_ENABLE(westmere, AS_HELP_STRING([--disable-westmere],[Disable Westmere (SSE4.2) kernel]))
+case "$enable_westmere" in
+  no)    enable_westmere=no ;;
+  yes|*) enable_westmere=yes ;;
+esac
+
+AC_ARG_ENABLE(haswell, AS_HELP_STRING([--disable-haswell],[Disable Haswell (AVX2) kernel]))
+case "$enable_haswell" in
+  no)    enable_haswell=no ;;
+  yes|*) enable_haswell=yes ;;
+esac
+
+# GCC and Clang
+AX_CHECK_COMPILE_FLAG([-MMD],DEPFLAGS="-MMD -MP")
+# Oracle Developer Studio (no -MP)
+AX_CHECK_COMPILE_FLAG([-xMMD],DEPFLAGS="-xMMD")
+
+AC_SUBST([DEPFLAGS])
+
+# Figure out the canonical target architecture.
+AC_CANONICAL_TARGET
+
+# Multiple instruction sets may be supported by a specific architecture.
+# e.g. x86_64 may (or may not) support any of SSE42, AVX2 and AVX-512. The
+# best instruction set is automatically selected at runtime, but the compiler
+# may or may not support generating code for an instruction set.
+case "$target" in
+  *amd64*)  x86_64=yes ;;
+  *x86_64*) x86_64=yes ;;
+  *)        x86_64=no  ;;
+esac
+
+HAVE_WESTMERE=NO
+HAVE_HASWELL=NO
+
+if test $x86_64 = "yes"; then
+  AC_CHECK_HEADER(immintrin.h,,,)
+  AX_CHECK_COMPILE_FLAG([-march=westmere],,,[-Werror])
+  AX_CHECK_COMPILE_FLAG([-march=haswell],,,[-Werror])
+
+  # Check if the arch instruction set support includes the simd instructions.
+  if test $enable_westmere != "no" -a \
+          $ax_cv_check_cflags__Werror__march_westmere = "yes" -a \
+          $ac_cv_header_immintrin_h = "yes" ; then
+    AC_MSG_CHECKING(whether -march=westmere works)
+    BAKCFLAGS="$CFLAGS"
+    CFLAGS="-march=westmere $CFLAGS"
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+AC_INCLUDES_DEFAULT
+[
+#include <stdint.h>
+#include <immintrin.h>
+
+int main(int argc, char *argv[])
+{
+  (void)argv;
+  uint64_t popcnt = _mm_popcnt_u64((uint64_t)argc);
+  return popcnt == 11;
+}
+]])
+],[
+    AC_DEFINE(HAVE_WESTMERE, 1, [Wether or not to compile support for SSE4.2])
+    HAVE_WESTMERE=WESTMERE
+    AC_MSG_RESULT(yes)
+],[
+    AC_MSG_RESULT(no)
+])
+    CFLAGS="$BAKCFLAGS"
+  fi
+
+  if test $enable_haswell != "no" -a \
+          $ax_cv_check_cflags__Werror__march_haswell = "yes" -a \
+          $ac_cv_header_immintrin_h = "yes" ; then
+    AC_MSG_CHECKING(whether -march=haswell works)
+    BAKCFLAGS="$CFLAGS"
+    CFLAGS="-march=haswell $CFLAGS"
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+AC_INCLUDES_DEFAULT
+[
+#include <stdint.h>
+#include <immintrin.h>
+
+int main(int argc, char *argv[])
+{
+  (void)argv;
+  int argc32x8[8] = { argc, 0, 0, 0, 0, 0, 0, 0 };
+  __m256i argc256 = _mm256_loadu_si256((__m256i *)argc32x8);
+  return _mm256_testz_si256(argc256, _mm256_set1_epi8(11));
+}
+]])
+],[
+    AC_DEFINE(HAVE_HASWELL, 1, [Wether or not to compile support for AVX2])
+    HAVE_HASWELL=HASWELL
+    AC_MSG_RESULT(yes)
+],[
+    AC_MSG_RESULT(no)
+])
+    CFLAGS="$BAKCFLAGS"
+  fi
+fi
+
+AC_CHECK_FUNCS([realpath],,[AC_MSG_ERROR([realpath is not available])])
+
+AC_SUBST([HAVE_ENDIAN_H])
+AC_SUBST([HAVE_WESTMERE])
+AC_SUBST([HAVE_HASWELL])
+
+AH_BOTTOM([
+/* Defines _XOPEN_SOURCE and _POSIX_C_SOURCE implicitly in features.h */
+#ifndef _DEFAULT_SOURCE
+# define _DEFAULT_SOURCE 1
+#endif
+])
+
+AC_OUTPUT
Index: simdzone/include/zone.h
===================================================================
RCS file: simdzone/include/zone.h
diff -N simdzone/include/zone.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/include/zone.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,658 @@
+/*
+ * zone.h -- (DNS) presentation format parser
+ *
+ * Copyright (c) 2022-2024, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef ZONE_H
+#define ZONE_H
+
+/**
+ * @file
+ * @brief simdzone main header
+ */
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stddef.h>
+
+#include "zone/attributes.h"
+#include "zone/export.h"
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/**
+ * @defgroup class_codes Class codes
+ *
+ * Supported CLASSes.
+ *
+ * See @iana{DNS CLASSes,dns-parameters,dns-parameters-2} for a list of
+ * classes registered by IANA.
+ *
+ * @{
+ */
+/** Internet @rfc{1035} */
+#define ZONE_CLASS_IN (1u)
+/** CSNET @rfc{1035} @obsolete */
+#define ZONE_CLASS_CS (2u)
+/** CHAOS @rfc{1035} */
+#define ZONE_CLASS_CH (3u)
+/** Hesiod @rfc{1035} */
+#define ZONE_CLASS_HS (4u)
+/** Any (QCLASS) @rfc{1035} */
+#define ZONE_CLASS_ANY (255u)
+/** @} */
+
+/**
+ * @defgroup type_codes Type codes
+ *
+ * Supported resource record (RR) TYPEs.
+ *
+ * See @iana{RR TYPEs,dns-parameters,dns-parameters-4} for a list of
+ * types registered by IANA.
+ *
+ * @{
+ */
+/** Host address @rfc{1035} */
+#define ZONE_TYPE_A (1u)
+/** Authoritative name server @rfc{1035} */
+#define ZONE_TYPE_NS (2u)
+/** Mail destination @rfc{1035} @obsolete */
+#define ZONE_TYPE_MD (3u)
+/** Mail forwarder @rfc{1035} @obsolete */
+#define ZONE_TYPE_MF (4u)
+/** Canonical name for an alias @rfc{1035} */
+#define ZONE_TYPE_CNAME (5u)
+/** Marks the start of authority @rfc{1035} */
+#define ZONE_TYPE_SOA (6u)
+/** Mailbox domain name @rfc{1035} @experimental */
+#define ZONE_TYPE_MB (7u)
+/** Mail group member @rfc{1035} @experimental */
+#define ZONE_TYPE_MG (8u)
+/** Mail rename domain name @rfc{1035} @experimental */
+#define ZONE_TYPE_MR (9u)
+/** Anything @rfc{883} @obsolete */
+#define ZONE_TYPE_NULL (10u)
+/** Well known service description @rfc{1035} */
+#define ZONE_TYPE_WKS (11u)
+/** Domain name pointer @rfc{1035} */
+#define ZONE_TYPE_PTR (12u)
+/** Host information @rfc{1035} */
+#define ZONE_TYPE_HINFO (13u)
+/** Mailbox or mail list information @rfc{1035} */
+#define ZONE_TYPE_MINFO (14u)
+/** Mail exchange @rfc{1035} */
+#define ZONE_TYPE_MX (15u)
+/** Text strings @rfc{1035} */
+#define ZONE_TYPE_TXT (16u)
+/** Responsible person @rfc{1035} */
+#define ZONE_TYPE_RP (17u)
+/** AFS Data Base location @rfc{1183} @rfc{5864} */
+#define ZONE_TYPE_AFSDB (18u)
+/** X.25 PSDN address @rfc{1183} */
+#define ZONE_TYPE_X25 (19u)
+/** ISDN address @rfc{1183} */
+#define ZONE_TYPE_ISDN (20u)
+/** Route Through @rfc{1183} */
+#define ZONE_TYPE_RT (21u)
+/** NSAP address, NSAP style A record @rfc{1706} */
+#define ZONE_TYPE_NSAP (22u)
+/** Domain name pointer, NSAP style @rfc{1348} @rfc{1637} */
+#define ZONE_TYPE_NSAP_PTR (23u)
+/** Signature @rfc{2535} */
+#define ZONE_TYPE_SIG (24u)
+/** Public key @rfc{2535} @rfc{2930} */
+#define ZONE_TYPE_KEY (25u)
+/** X.400 mail mapping information @rfc{2163} */
+#define ZONE_TYPE_PX (26u)
+/** Geographical Position @rfc{1712} */
+#define ZONE_TYPE_GPOS (27u)
+/** IPv6 Address @rfc{3596} */
+#define ZONE_TYPE_AAAA (28u)
+/** Location Information @rfc{1876} */
+#define ZONE_TYPE_LOC (29u)
+/** Next domain @rfc{3755} @rfc{2535} @obsolete */
+#define ZONE_TYPE_NXT (30u)
+/** Server Selection @rfc{2782} */
+#define ZONE_TYPE_SRV (33u)
+/** Naming Authority Pointer @rfc{2915} @rfc{2168} @rfc{3403} */
+#define ZONE_TYPE_NAPTR (35u)
+/** Key Exchanger @rfc{2230} */
+#define ZONE_TYPE_KX (36u)
+/** CERT @rfc{4398}*/
+#define ZONE_TYPE_CERT (37u)
+/** IPv6 Address @rfc{3226} @rfc{2874} @rfc{6563} @obsolete */
+#define ZONE_TYPE_A6 (38u)
+/** DNAME @rfc{6672} */
+#define ZONE_TYPE_DNAME (39u)
+/** Address Prefix List @rfc{3123} */
+#define ZONE_TYPE_APL (42u)
+/** Delegation Signer @rfc{4034} @rfc{3658} */
+#define ZONE_TYPE_DS (43u)
+/** SSH Key Fingerprint @rfc{4255} */
+#define ZONE_TYPE_SSHFP (44u)
+/** IPsec public key @rfc{4025} */
+#define ZONE_TYPE_IPSECKEY (45u)
+/** Resource Record Signature @rfc{4034} @rfc{3755} */
+#define ZONE_TYPE_RRSIG (46u)
+/** Next Secure @rfc{4034} @rfc{3755} */
+#define ZONE_TYPE_NSEC (47u)
+/** DNS Public Key @rfc{4034} @rfc{3755} */
+#define ZONE_TYPE_DNSKEY (48u)
+/** DHCID @rfc{4701} */
+#define ZONE_TYPE_DHCID (49u)
+/** NSEC3 @rfc{5155} */
+#define ZONE_TYPE_NSEC3 (50u)
+/** NSEC3PARAM @rfc{5155} */
+#define ZONE_TYPE_NSEC3PARAM (51u)
+/** TLSA @rfc{6698} */
+#define ZONE_TYPE_TLSA (52u)
+/** S/MIME cert association @rfc{8162} */
+#define ZONE_TYPE_SMIMEA (53u)
+/** Host Identity Protocol @rfc{8005} */
+#define ZONE_TYPE_HIP (55u)
+/** NINFO */
+#define ZONE_TYPE_NINFO (56u)
+/** RKEY */
+#define ZONE_TYPE_RKEY (57u)
+/** Child DS @rfc{7344} */
+#define ZONE_TYPE_CDS (59u)
+/** DNSKEY(s) the Child wants reflected in DS @rfc{7344} */
+#define ZONE_TYPE_CDNSKEY (60u)
+/** OpenPGP Key @rfc{7929} */
+#define ZONE_TYPE_OPENPGPKEY (61u)
+/** Child-To-Parent Synchronization @rfc{7477} */
+#define ZONE_TYPE_CSYNC (62u)
+/** Zone message digest @rfc{8976} */
+#define ZONE_TYPE_ZONEMD (63u)
+/** Service binding @rfc{9460} */
+#define ZONE_TYPE_SVCB (64u)
+/** Service binding @rfc{9460} */
+#define ZONE_TYPE_HTTPS (65u)
+/** Sender Policy Framework @rfc{7208} */
+#define ZONE_TYPE_SPF (99u)
+/** Node Identifier @rfc{6742} */
+#define ZONE_TYPE_NID (104u)
+/** 32-bit Locator for ILNPv4-capable nodes @rfc{6742} */
+#define ZONE_TYPE_L32 (105u)
+/** 64-bit Locator for ILNPv6-capable nodes @rfc{6742} */
+#define ZONE_TYPE_L64 (106u)
+/** Name of an ILNP subnetwork @rfc{6742} */
+#define ZONE_TYPE_LP (107u)
+/** EUI-48 address @rfc{7043} */
+#define ZONE_TYPE_EUI48 (108u)
+/** EUI-64 address @rfc{7043} */
+#define ZONE_TYPE_EUI64 (109u)
+/** Uniform Resource Identifier @rfc{7553} */
+#define ZONE_TYPE_URI (256u)
+/** Certification Authority Restriction @rfc{6844} */
+#define ZONE_TYPE_CAA (257u)
+/** DNS Authoritative Source (DNS-AS) */
+#define ZONE_TYPE_AVC (258u)
+/** Resolver Information as Key/Value Pairs @rfc{9606} */
+#define ZONE_TYPE_RESINFO (261u)
+/** Public wallet address */
+#define ZONE_TYPE_WALLET (262u)
+/** BP Convergence Layer Adapter */
+#define ZONE_TYPE_CLA (263u)
+/** DNSSEC Trust Authorities */
+#define ZONE_TYPE_TA (32768u)
+/** DNSSEC Lookaside Validation @rfc{4431} @obsolete */
+#define ZONE_TYPE_DLV (32769u)
+/** @} */
+
+/**
+ * @defgroup svc_params Service Parameter Keys
+ *
+ * Supported service parameters.
+ *
+ * See @iana{Service Parameter Keys (SvcParamKeys),dns-svcb,dns-svcparamkeys}
+ * for a list of service parameter keys registered by IANA.
+ *
+ * @{
+ */
+/** Parameters clients must not ignore @rfc{9460} */
+#define ZONE_SVC_PARAM_KEY_MANDATORY (0u)
+/** Application Layer Protocol Negotiation (ALPN) protocol identifiers @rfc{9460} */
+#define ZONE_SVC_PARAM_KEY_ALPN (1u)
+/** No support for default protocol (alpn must be specified) @rfc{9460} */
+#define ZONE_SVC_PARAM_KEY_NO_DEFAULT_ALPN (2u)
+/** TCP or UDP port for alternative endpoint @rfc{9460} */
+#define ZONE_SVC_PARAM_KEY_PORT (3u)
+/** IPv4 address hints @rfc{9460} */
+#define ZONE_SVC_PARAM_KEY_IPV4HINT (4u)
+/** Encrypted ClientHello (ECH) configuration @draft{ietf, tls-svcb-ech} */
+#define ZONE_SVC_PARAM_KEY_ECH (5u)
+/** IPv6 address hints @rfc{9460} */
+#define ZONE_SVC_PARAM_KEY_IPV6HINT (6u)
+/** URI template in relative form @rfc{9461} */
+#define ZONE_SVC_PARAM_KEY_DOHPATH (7u)
+/** Target is an Oblivious HTTP service @rfc{9540} */
+#define ZONE_SVC_PARAM_KEY_OHTTP (8u)
+/** Supported groups in TLS @draft{ietf, tls-key-share-prediction} */
+#define ZONE_SVC_PARAM_KEY_TLS_SUPPORTED_GROUPS (9u)
+/** Reserved ("invalid key") @rfc{9460} */
+#define ZONE_SVC_PARAM_KEY_INVALID_KEY (65535u)
+/** @} */
+
+/**
+ * Number of bytes per block.
+ *
+ * Higher throughput is achieved by block-based operation. The size of a
+ * block is determined by the word size of the CPU. To avoid pipeline flushes
+ * as much as possible buffers are required to be padded by the number of
+ * bytes in a single block.
+ *
+ * @warning The input buffer to @zone_parse_string is required to be
+ *          null-terminated and padded, which is somewhat counter intuitive. A
+ *          future release may lift this requirement (@issue{174}).
+ */
+#define ZONE_BLOCK_SIZE (64)
+
+/**
+ * Number of blocks per window.
+ *
+ * Master files can become quite large and are read in multiples of blocks.
+ * The input buffer is expanded as needed.
+ */
+#define ZONE_WINDOW_SIZE (256 * ZONE_BLOCK_SIZE) // 16KB
+
+/** Maximum size of domain name. */
+#define ZONE_NAME_SIZE (255)
+
+typedef struct zone_name_buffer zone_name_buffer_t;
+struct zone_name_buffer {
+  /** Length of domain name stored in buffer. */
+  size_t length;
+  /** Maximum number of octets in a domain name plus padding. */
+  uint8_t octets[ ZONE_NAME_SIZE + ZONE_BLOCK_SIZE ];
+};
+
+/** Maximum size of RDATA section. */
+#define ZONE_RDATA_SIZE (65535)
+
+typedef struct zone_rdata_buffer zone_rdata_buffer_t;
+struct zone_rdata_buffer {
+  /** Maximum number of octets in RDATA section plus padding. */
+  uint8_t octets[ ZONE_RDATA_SIZE + ZONE_BLOCK_SIZE ];
+};
+
+/**
+ * @brief Tape capacity per-file.
+ *
+ * Tape capacity must be sufficiently large to hold every token from a single
+ * worst-case read (e.g. 64 consecutive line feeds). Not likely to occur in
+ * practice, therefore, to optimize throughput, allocate at least twice the
+ * size so consecutive index operations can be performed.
+ */
+#define ZONE_TAPE_SIZE ((100 * ZONE_BLOCK_SIZE) + ZONE_BLOCK_SIZE)
+
+typedef struct zone_file zone_file_t;
+struct zone_file {
+  /** @private */
+  zone_file_t *includer;
+  /** @private */
+  zone_name_buffer_t origin, owner;
+  /** @private */
+  uint16_t last_type;
+  /** Last stated TTL. */
+  uint32_t last_ttl;
+  /** Last parsed TTL in $TTL entry. */
+  uint32_t dollar_ttl;
+  /** TTL passed to accept callback. */
+  uint32_t *ttl;
+  /** Default TTL passed to accept. */
+  /** Last stated TTL is used as default unless $TTL entry was found. */
+  uint32_t *default_ttl;
+  /** @private */
+  uint16_t last_class;
+  /** Number of lines spanned by RR. */
+  /** Non-terminating line feeds, i.e. escaped line feeds, line feeds in
+      quoted sections or within parentheses, are counted, but deferred for
+      consistency in error reports */
+  size_t span;
+  /** Starting line of RR. */
+  size_t line;
+  /** Filename in control directive. */
+  char *name;
+  /** Absolute path. */
+  char *path;
+  /** @private */
+  FILE *handle;
+  /** @private */
+  bool grouped;
+  /** @private */
+  bool start_of_line;
+  /** @private */
+  uint8_t end_of_file;
+  /** @private */
+  struct {
+    size_t index, length, size;
+    char *data;
+  } buffer;
+  /** @private */
+  /** scanner state is kept per-file */
+  struct {
+    uint64_t in_comment;
+    uint64_t in_quoted;
+    uint64_t is_escaped;
+    uint64_t follows_contiguous;
+  } state;
+  /** @private */
+  /** vector of tokens generated by the scanner guaranteed to be large
+      enough to hold every token for a single read + terminators */
+  struct { const char **head, **tail, *tape[ZONE_TAPE_SIZE + 2]; } fields;
+  struct { const char **head, **tail, *tape[ZONE_TAPE_SIZE + 1]; } delimiters;
+  struct { uint16_t *head, *tail, tape[ZONE_TAPE_SIZE + 1]; } newlines;
+};
+
+typedef struct zone_parser zone_parser_t;
+struct zone_parser;
+
+/**
+ * @brief Signature of callback function that is invoked for log messages.
+ *
+ * By default messages are printed to stdout (info) and stderr (warnings,
+ * errors). A custom log handler (callback) may be provided for better
+ * integration of reporting.
+ *
+ * @note file maybe NULL if initial file does not exist.
+ */
+typedef void(*zone_log_t)(
+  zone_parser_t *,
+  uint32_t, // priority
+  const char *, // file
+  size_t, // line
+  const char *, // message
+  void *); // user data
+
+/**
+ * @brief Domain name and corresponding length in wire format.
+ */
+typedef struct zone_name zone_name_t;
+struct zone_name {
+  /** Length of domain name. */
+  uint8_t length;
+  /** Absolute, uncompressed, domain name in wire format. */
+  const uint8_t *octets;
+};
+
+/**
+ * @brief Signature of callback function invoked for each RR.
+ *
+ * Header is in host order, RDATA section is in network order.
+ */
+typedef int32_t(*zone_accept_t)(
+  zone_parser_t *,
+  const zone_name_t *, // owner (length + octets)
+  uint16_t, // type
+  uint16_t, // class
+  uint32_t, // ttl
+  uint16_t, // rdlength
+  const uint8_t *, // rdata
+  void *); // user data
+
+/**
+ * @brief Signature of callback function invoked on $INCLUDE.
+ *
+ * Signal file name in $INCLUDE directive to application. Useful for
+ * dependency tracking, etc.
+ */
+typedef int32_t(*zone_include_t)(
+  zone_parser_t *,
+  const char *, // name in $INCLUDE entry
+  const char *, // fully qualified path
+  void *); // user data
+
+/**
+ * @brief Available configuration options.
+ */
+typedef struct {
+  /** Non-strict mode of operation. */
+  /** Authoritative servers may choose to be more lenient when operating as
+      a secondary as data may have been transferred over AXFR/IXFR that
+      would have triggered an error otherwise. */
+  bool secondary;
+  /** Disable $INCLUDE directive. */
+  /** Useful in setups where untrusted input may be offered. */
+  bool no_includes;
+  /** Maximum $INCLUDE depth. 0 for default. */
+  uint32_t include_limit;
+  /** Enable 1h2m3s notations for TTLS. */
+  bool pretty_ttls;
+  /** Origin in wire format. */
+  zone_name_t origin;
+  /** Default TTL to use. */
+  uint32_t default_ttl;
+  /** Default CLASS to use. */
+  uint16_t default_class;
+  struct {
+    /** Priorities NOT to write out. */
+    uint32_t mask;
+    /** Callback invoked to write out log messages. */
+    zone_log_t callback;
+  } log;
+  struct {
+    /** Callback invoked for each RR. */
+    zone_accept_t callback;
+  } accept;
+  struct {
+    /** Callback invoked for each $INCLUDE entry. */
+    zone_include_t callback;
+  } include;
+} zone_options_t;
+
+/**
+ * @brief Scratch buffer space reserved for parser.
+ *
+ * @note Future versions may leverage multiple buffers to improve throughput
+ *       as parsing and committing resource records are disjunct operations.
+ */
+typedef struct zone_buffers zone_buffers_t;
+struct zone_buffers {
+  /** Number of name and rdata buffers available. */
+  size_t size;
+  /** Vector of name buffers to use as scratch buffer. */
+  zone_name_buffer_t *owner;
+  /** Vector of rdata buffers to use as scratch buffer. */
+  zone_rdata_buffer_t *rdata;
+};
+
+/**
+ * @brief Parser state.
+ * @warning Do not modify directly.
+ */
+struct zone_parser {
+  /** @private */
+  zone_options_t options;
+  /** @private */
+  void *user_data;
+  struct {
+    size_t size;
+    struct {
+      size_t active;
+      zone_name_buffer_t *blocks;
+    } owner;
+    struct {
+      size_t active;
+      zone_rdata_buffer_t *blocks;
+    } rdata;
+  } buffers;
+  /** @private */
+  zone_name_buffer_t *owner;
+  /** @private */
+  zone_rdata_buffer_t *rdata;
+  /** @private */
+  zone_file_t *file, first;
+};
+
+/**
+ * @defgroup return_codes Return codes
+ *
+ * @{
+ */
+/** Success. */
+#define ZONE_SUCCESS (0)
+/** A syntax error occurred. */
+#define ZONE_SYNTAX_ERROR (-256)  // (-1 << 8)
+/** A semantic error occurred. */
+#define ZONE_SEMANTIC_ERROR (-512)  // (-2 << 8)
+/** Operation failed due to lack of memory. */
+#define ZONE_OUT_OF_MEMORY (-768)  // (-3 << 8)
+/** Bad parameter value. */
+#define ZONE_BAD_PARAMETER (-1024)  // (-4 << 8)
+/** Error reading zone file. */
+#define ZONE_READ_ERROR (-1280)  // (-5 << 8)
+/** Control directive or support for record type is not implemented. */
+#define ZONE_NOT_IMPLEMENTED (-1536)  // (-6 << 8)
+/** Specified file does not exist. */
+#define ZONE_NOT_A_FILE (-1792)  // (-7 << 8)
+/** Access to specified file is not allowed. */
+#define ZONE_NOT_PERMITTED (-2048)  // (-8 << 8)
+/** @} */
+
+/**
+ * @brief Parse zone file
+ *
+ * Parse file containing resource records.
+ *
+ * @param[in]  parser     Zone parser
+ * @param[in]  options    Settings used for parsing.
+ * @param[in]  buffers    Scratch buffers used for parsing.
+ * @param[in]  path       Path of master file to parse.
+ * @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(
+  zone_parser_t *parser,
+  const zone_options_t *options,
+  zone_buffers_t *buffers,
+  const char *path,
+  void *user_data)
+zone_nonnull((1,2,3,4));
+
+/**
+ * @brief Parse zone from string
+ *
+ * Parse string containing resource records in presentation format.
+ *
+ * @warning The input string must be null terminated and padded with at least
+ *          @ref ZONE_BLOCK_SIZE bytes.
+ *
+ * @param[in]  parser     Zone parser
+ * @param[in]  options    Settings used for parsing.
+ * @param[in]  buffers    Scratch buffers used by parsing.
+ * @param[in]  string     Input string.
+ * @param[in]  length     Length of string (excluding null byte and padding).
+ * @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_string(
+  zone_parser_t *parser,
+  const zone_options_t *options,
+  zone_buffers_t *buffers,
+  const char *string,
+  size_t length,
+  void *user_data)
+zone_nonnull((1,2,3,4));
+
+/**
+ * @defgroup log_priorities Log categories.
+ *
+ * @note No direct relation between log categories and error codes exists.
+ *       Log categories communicate the importance of the log message, error
+ *       codes communicate what went wrong to the caller.
+ * @{
+ */
+/** Error condition. */
+/** @hideinitializer */
+#define ZONE_ERROR (1u<<1)
+/** Warning condition. */
+/** @hideinitializer */
+#define ZONE_WARNING (1u<<2)
+/** Informational message. */
+/** @hideinitializer */
+#define ZONE_INFO (1u<<3)
+/** @} */
+
+/**
+ * @brief Write message to active log handler.
+ *
+ * The zone parser operates on a per-record base and therefore cannot detect
+ * errors that span records. e.g. SOA records being specified more than once.
+ * The user may print a message using the active log handler, keeping the
+ * error message format consistent.
+ *
+ * @param[in]  parser    Zone parser
+ * @param[in]  priority  Log priority
+ * @param[in]  format    Format string compatible with printf
+ * @param[in]  ...       Variadic arguments corresponding to #format
+ */
+ZONE_EXPORT void zone_log(
+  zone_parser_t *parser,
+  uint32_t priority,
+  const char *format,
+  ...)
+zone_nonnull((1,3))
+zone_format_printf(3,4);
+
+/**
+ * @brief Write error message to active log handler.
+ * @hideinitializer
+ *
+ * Shorthand to write out error message via @ref zone_log if error messages are
+ * not to be discarded.
+ *
+ * @param[in]  parser  Zone parser
+ * @param[in]  format  Format string
+ * @param[in]  ...     Variadic arguments corresponding to #format
+ */
+#define zone_error(parser, ...) \
+  (((parser)->options.log.mask & ZONE_ERROR) ? \
+     (void)0 : zone_log((parser), ZONE_ERROR, __VA_ARGS__))
+
+/**
+ * @brief Write warning message to active log handler.
+ * @hideinitializer
+ *
+ * Shorthand to write out warning message via @ref zone_log if warning messages
+ * are not to be discarded.
+ *
+ * @param[in]  parser  Zone parser
+ * @param[in]  format  Format string compatible with printf.
+ * @param[in]  ...     Variadic arguments corresponding to @format.
+ */
+#define zone_warning(parser, ...) \
+  (((parser)->options.mask & ZONE_WARNING) ? \
+     (void)0 : zone_log((parser), ZONE_WARNING, __VA_ARGS__))
+
+/**
+ * @brief Write informational message to active log handler.
+ * @hideinitializer
+ *
+ * Shorthand to write out informational message via @ref zone_log if
+ * informational messages are not be discarded.
+ *
+ * @param[in]  parser  Zone parser.
+ * @param[in]  format  Format string compatible with printf.
+ * @param[in]  ...     Variadic arguments corresponding to @format.
+ */
+#define zone_info(parser, ...) \
+  (((parser)->options.mask & ZONE_INFO) ? \
+     (void)0 : zone_log((parser), ZONE_INFO, __VA_ARGS__))
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // ZONE_H
Index: simdzone/include/zone/attributes.h
===================================================================
RCS file: simdzone/include/zone/attributes.h
diff -N simdzone/include/zone/attributes.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/include/zone/attributes.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,52 @@
+/*
+ * attributes.h -- compiler attribute abstractions
+ *
+ * Copyright (c) 2022, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef ZONE_ATTRIBUTES_H
+#define ZONE_ATTRIBUTES_H
+
+#if defined __GNUC__
+# define zone_has_gnuc(major, minor) \
+    ((__GNUC__ > major) || (__GNUC__ == major && __GNUC_MINOR__ >= minor))
+#else
+# define zone_has_gnuc(major, minor) (0)
+#endif
+
+#if defined __has_attribute
+# define zone_has_attribute(params) __has_attribute(params)
+#else
+# define zone_has_attribute(params) (0)
+#endif
+
+#if zone_has_attribute(nonnull)
+# define zone_nonnull(params) __attribute__((__nonnull__ params))
+# define zone_nonnull_all __attribute__((__nonnull__))
+#else
+# define zone_nonnull(params)
+# define zone_nonnull_all
+#endif
+
+#if zone_has_attribute(format) || zone_has_gnuc(2, 4)
+# define zone_format(params) __attribute__((__format__ params))
+# if __MINGW32__
+#   if __MINGW_PRINTF_FORMAT
+#     define zone_format_printf(string_index, first_to_check) \
+        zone_format((__MINGW_PRINTF_FORMAT, string_index, first_to_check))
+#   else
+#     define zone_format_printf(string_index, first_to_check) \
+        zone_format((gnu_printf, string_index, first_to_check))
+#   endif
+# else
+#   define zone_format_printf(string_index, first_to_check) \
+      zone_format((printf, string_index, first_to_check))
+# endif
+#else
+# define zone_format(params)
+# define zone_format_printf(string_index, first_to_check)
+#endif
+
+#endif // ZONE_ATTRIBUTES_H
Index: simdzone/m4/ax_check_compile_flag.m4
===================================================================
RCS file: simdzone/m4/ax_check_compile_flag.m4
diff -N simdzone/m4/ax_check_compile_flag.m4
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/m4/ax_check_compile_flag.m4	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,53 @@
+# ===========================================================================
+#  https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+#   Check whether the given FLAG works with the current language's compiler
+#   or gives an error.  (Warnings, however, are ignored)
+#
+#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+#   success/failure.
+#
+#   If EXTRA-FLAGS is defined, it is added to the current language's default
+#   flags (e.g. CFLAGS) when the check is done.  The check is thus made with
+#   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to
+#   force the compiler to issue an error when a bad flag is given.
+#
+#   INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+#   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 6
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+  ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+  _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+  AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+    [AS_VAR_SET(CACHEVAR,[yes])],
+    [AS_VAR_SET(CACHEVAR,[no])])
+  _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_VAR_IF(CACHEVAR,yes,
+  [m4_default([$2], :)],
+  [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
Index: simdzone/src/attributes.h
===================================================================
RCS file: simdzone/src/attributes.h
diff -N simdzone/src/attributes.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/attributes.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,76 @@
+/*
+ * attributes.h -- internal compiler attribute abstractions
+ *
+ * Copyright (c) 2023-2024, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include "zone/attributes.h"
+
+#ifndef ATTRIBUTES_H
+#define ATTRIBUTES_H
+
+#define nonnull(params) zone_nonnull(params)
+#define nonnull_all zone_nonnull_all
+
+#if _MSC_VER
+# define really_inline __forceinline
+# define never_inline __declspec(noinline)
+# define warn_unused_result
+# define no_sanitize_undefined
+
+# define likely(params) (params)
+# define unlikely(params) (params)
+
+#else // _MSC_VER
+#if defined __has_builtin
+# define has_builtin(params) __has_builtin(params)
+#else
+# define has_builtin(params) (0)
+#endif
+
+# if (zone_has_attribute(always_inline) || zone_has_gnuc(3, 1)) && ! defined __NO_INLINE__
+    // Compilation using GCC 4.2.1 without optimizations fails.
+    //   sorry, unimplemented: inlining failed in call to ...
+    // GCC 4.1.2 and GCC 4.30 compile forward declared functions annotated
+    // with __attribute__((always_inline)) without problems. Test if
+    // __NO_INLINE__ is defined and define macro accordingly.
+#   define really_inline inline __attribute__((always_inline))
+# else
+#   define really_inline inline
+# endif
+
+# if zone_has_attribute(noinline) || zone_has_gnuc(2, 96)
+#   define never_inline __attribute__((noinline))
+# else
+#   define never_inline
+# endif
+
+# if zone_has_attribute(warn_unused_result)
+#   define warn_unused_result __attribute__((warn_unused_result))
+# else
+#   define warn_unused_result
+# endif
+
+# if zone_has_attribute(no_sanitize)
+    // GCC 8.1 added the no_sanitize function attribute.
+#   define no_sanitize_undefined __attribute__((no_sanitize("undefined")))
+# elif zone_has_attribute(no_sanitize_undefined)
+    // GCC 4.9.0 added the UndefinedBehaviorSanitizer (ubsan) and the
+    // no_sanitize_undefined function attribute.
+#   define no_sanitize_undefined
+# else
+#   define no_sanitize_undefined
+# endif
+
+# if has_builtin(__builtin_expect)
+#   define likely(params) __builtin_expect(!!(params), 1)
+#   define unlikely(params) __builtin_expect(!!(params), 0)
+# else
+#   define likely(params) (params)
+#   define unlikely(params) (params)
+# endif
+#endif
+
+#endif // ATTRIBUTES_H
Index: simdzone/src/bench.c
===================================================================
RCS file: simdzone/src/bench.c
diff -N simdzone/src/bench.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/bench.c	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,236 @@
+/*
+ * bench.c -- simple scanner/parser benchmarking tool
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if !defined(HAVE_GETOPT)
+# include "getopt.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "zone.h"
+#include "config.h"
+#include "isadetection.h"
+#include "attributes.h"
+#include "diagnostic.h"
+
+#if _MSC_VER
+#define strcasecmp(s1, s2) _stricmp(s1, s2)
+#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n)
+#else
+#include <strings.h>
+#endif
+
+typedef zone_parser_t parser_t;
+
+#if HAVE_HASWELL
+extern int32_t zone_bench_haswell_lex(zone_parser_t *, size_t *);
+extern int32_t zone_haswell_parse(zone_parser_t *);
+#endif
+
+#if HAVE_WESTMERE
+extern int32_t zone_bench_westmere_lex(zone_parser_t *, size_t *);
+extern int32_t zone_westmere_parse(zone_parser_t *);
+#endif
+
+extern int32_t zone_bench_fallback_lex(zone_parser_t *, size_t *);
+extern int32_t zone_fallback_parse(zone_parser_t *);
+
+typedef struct kernel kernel_t;
+struct kernel {
+  const char *name;
+  uint32_t instruction_set;
+  int32_t (*bench_lex)(zone_parser_t *, size_t *);
+  int32_t (*parse)(zone_parser_t *);
+};
+
+static const kernel_t kernels[] = {
+#if HAVE_HASWELL
+  { "haswell", AVX2, &zone_bench_haswell_lex, &zone_haswell_parse },
+#endif
+#if HAVE_WESTMERE
+  { "westmere", SSE42|PCLMULQDQ, &zone_bench_westmere_lex, &zone_westmere_parse },
+#endif
+  { "fallback", DEFAULT, &zone_bench_fallback_lex, &zone_fallback_parse }
+};
+
+extern int32_t zone_open(
+  zone_parser_t *,
+  const zone_options_t *,
+  zone_buffers_t *,
+  const char *,
+  void *user_data);
+
+extern void zone_close(
+  zone_parser_t *);
+
+static int32_t bench_lex(zone_parser_t *parser, const kernel_t *kernel)
+{
+  size_t tokens = 0;
+  int32_t result;
+
+  if ((result = kernel->bench_lex(parser, &tokens)) < 0)
+    return result;
+
+  printf("Lexed %zu tokens\n", tokens);
+  return 0;
+}
+
+static int32_t bench_accept(
+  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)
+{
+  (void)parser;
+  (void)owner;
+  (void)type;
+  (void)class;
+  (void)ttl;
+  (void)rdlength;
+  (void)rdata;
+  (*(size_t *)user_data)++;
+  return ZONE_SUCCESS;
+}
+
+static int32_t bench_parse(zone_parser_t *parser, const kernel_t *kernel)
+{
+  size_t records = 0;
+  int32_t result;
+
+  parser->user_data = &records;
+  result = kernel->parse(parser);
+
+  printf("Parsed %zu records\n", records);
+  return result;
+}
+
+diagnostic_push()
+msvc_diagnostic_ignored(4996)
+
+static const kernel_t *select_kernel(const char *name)
+{
+  const size_t n = sizeof(kernels)/sizeof(kernels[0]);
+  const uint32_t supported = detect_supported_architectures();
+  const kernel_t *kernel = NULL;
+
+  if ((!name || !*name) && !(name = getenv("ZONE_KERNEL"))) {
+    for (size_t i=0; !kernel && i < n; i++) {
+      if ((kernels[i].instruction_set & supported) == kernels[i].instruction_set)
+        kernel = &kernels[i];
+    }
+    assert(kernel != NULL);
+  } else {
+    for (size_t i=0; !kernel && i < n; i++) {
+      if (strcasecmp(name, kernels[i].name) == 0)
+        kernel = &kernels[i];
+    }
+
+    if (!kernel || (kernel->instruction_set && !(kernel->instruction_set & supported))) {
+      fprintf(stderr, "Target %s is unavailable\n", name);
+      return NULL;
+    }
+  }
+
+  printf("Selected target %s\n", kernel->name);
+  return kernel;
+}
+
+diagnostic_pop()
+
+static void help(const char *program)
+{
+  const char *format =
+    "Usage: %s [OPTION] <lex or parse> <zone file>\n"
+    "\n"
+    "Options:\n"
+    "  -h         Display available options.\n"
+    "  -t target  Select target (default:%s)\n"
+    "\n"
+    "Kernels:\n";
+
+  printf(format, program, kernels[0].name);
+
+  for (size_t i=0, n=sizeof(kernels)/sizeof(kernels[0]); i < n; i++)
+    printf("  %s\n", kernels[i].name);
+}
+
+static void usage(const char *program)
+{
+  fprintf(stderr, "Usage: %s [OPTION] <lex or parse> <zone file>\n", program);
+  exit(EXIT_FAILURE);
+}
+
+static uint8_t root[] = { 0 };
+
+int main(int argc, char *argv[])
+{
+  const char *name = NULL, *program = argv[0];
+
+  for (const char *slash = argv[0]; *slash; slash++)
+    if (*slash == '/' || *slash == '\\')
+      program = slash + 1;
+
+  for (int option; (option = getopt(argc, argv, "ht:")) != -1;) {
+    switch (option) {
+      case 'h':
+        help(program);
+        exit(EXIT_SUCCESS);
+      case 't':
+        name = optarg;
+        break;
+      default:
+        usage(program);
+    }
+  }
+
+  if (optind > argc || argc - optind < 2)
+    usage(program);
+
+  int32_t (*bench)(zone_parser_t *, const kernel_t *) = 0;
+  if (strcasecmp(argv[optind], "lex") == 0)
+    bench = &bench_lex;
+  else if (strcasecmp(argv[optind], "parse") == 0)
+    bench = &bench_parse;
+  else
+    usage(program);
+
+  const kernel_t *kernel;
+  if (!(kernel = select_kernel(name)))
+    exit(EXIT_FAILURE);
+
+  zone_parser_t parser;
+  memset(&parser, 0, sizeof(parser));
+  zone_options_t options;
+  memset(&options, 0, sizeof(options));
+  options.pretty_ttls = true;
+  options.origin.octets = root;
+  options.origin.length = 1;
+  options.accept.callback = &bench_accept;
+  options.default_ttl = 3600;
+  options.default_class = 1;
+
+  zone_name_buffer_t owner;
+  zone_rdata_buffer_t rdata;
+  zone_buffers_t buffers = { 1, &owner, &rdata };
+
+  if (zone_open(&parser, &options, &buffers, argv[argc-1], NULL) < 0)
+    exit(EXIT_FAILURE);
+  if (bench(&parser, kernel) < 0)
+    exit(EXIT_FAILURE);
+
+  zone_close(&parser);
+  return EXIT_SUCCESS;
+}
Index: simdzone/src/config.h.in
===================================================================
RCS file: simdzone/src/config.h.in
diff -N simdzone/src/config.h.in
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/config.h.in	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,41 @@
+/*
+ * config.h.in -- configuration header template
+ *
+ * Copyright (c) 2024, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef CONFIG_H
+#define CONFIG_H
+
+/* Define to 1 if you have the declaration of `bswap16', and to 0 if you
+   don't. */
+#cmakedefine01 HAVE_DECL_BSWAP16
+
+/* Define to 1 if you have the declaration of `bswap32', and to 0 if you
+   don't. */
+#cmakedefine01 HAVE_DECL_BSWAP32
+
+/* Define to 1 if you have the declaration of `bswap64', and to 0 if you
+   don't. */
+#cmakedefine01 HAVE_DECL_BSWAP64
+
+/* Define to 1 if you have the <endian.h> header file. */
+#cmakedefine HAVE_ENDIAN_H 1
+
+/* Define to 1 if you have the `getopt' function. */
+#cmakedefine HAVE_GETOPT 1
+
+/* Wether or not to compile support for AVX2 */
+#cmakedefine HAVE_HASWELL 1
+
+/* Wether or not to compile support for SSE4.2 */
+#cmakedefine HAVE_WESTMERE 1
+
+/* Defines _XOPEN_SOURCE and _POSIX_C_SOURCE implicitly in features.h */
+#ifndef _DEFAULT_SOURCE
+# define _DEFAULT_SOURCE 1
+#endif
+
+#endif // CONFIG_H
Index: simdzone/src/diagnostic.h
===================================================================
RCS file: simdzone/src/diagnostic.h
diff -N simdzone/src/diagnostic.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/diagnostic.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,57 @@
+/*
+ * diagnostic.h -- compiler diagnostic abstractions
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef DIAGNOSTIC_H
+#define DIAGNOSTIC_H
+
+#if _MSC_VER
+# define diagnostic_push() \
+           __pragma(warning(push))
+# define msvc_diagnostic_ignored(warning_specifier) \
+           __pragma(warning(disable:warning_specifier))
+# define diagnostic_pop() \
+           __pragma(warning(pop))
+// Support for selectively enabling and disabling warnings via
+// #pragma GCC diagnostic was added in GCC 4.6
+// (https://gcc.gnu.org/gcc-4.6/changes.html).
+#elif (defined __clang__) \
+   || (defined __GNUC__ && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 406))
+# define stringify(x) #x
+# define paste(flag, warning) stringify(flag ## warning)
+# define pragma(x) _Pragma(#x)
+# define diagnostic_ignored(warning) pragma(warning)
+
+# define diagnostic_push() _Pragma("GCC diagnostic push")
+# define diagnostic_pop() _Pragma("GCC diagnostic pop")
+# if __clang__
+#   define clang_diagnostic_ignored(warning) \
+      diagnostic_ignored(GCC diagnostic ignored paste(-W,warning))
+# else
+#   define gcc_diagnostic_ignored(warning) \
+      diagnostic_ignored(GCC diagnostic ignored paste(-W,warning))
+# endif
+#endif
+
+#if !defined diagnostic_push
+# define diagnostic_push()
+# define diagnostic_pop()
+#endif
+
+#if !defined gcc_diagnostic_ignored
+# define gcc_diagnostic_ignored(warning)
+#endif
+
+#if !defined clang_diagnostic_ignored
+# define clang_diagnostic_ignored(warning)
+#endif
+
+#if !defined msvc_diagnostic_ignored
+# define msvc_diagnostic_ignored(warning)
+#endif
+
+#endif // DIAGNOSTIC_H
Index: simdzone/src/isadetection.h
===================================================================
RCS file: simdzone/src/isadetection.h
diff -N simdzone/src/isadetection.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/isadetection.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,269 @@
+/*
+ * isadetection.h -- detect supported instruction set(s)
+ *
+ * Slightly modified version of isadetection.h in simdjson.
+ *
+ * Copyright (c) 2024      NLnet Labs               (Jeroen Koekkoek)
+ * Copyright (c) 2020-     simdjson                 (Daniel Lemire,
+ *                                                   Geoff Langdale,
+ *                                                   John Keiser)
+ * Copyright (c) 2016-     Facebook, Inc            (Adam Paszke)
+ * Copyright (c) 2014-     Facebook, Inc            (Soumith Chintala)
+ * Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
+ * Copyright (c) 2012-2014 Deepmind Technologies    (Koray Kavukcuoglu)
+ * Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
+ * Copyright (c) 2011-2013 NYU                      (Clement Farabet)
+ * Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert,
+ *                                                   Leon Bottou,
+ *                                                   Iain Melvin,
+ *                                                   Jason Weston)
+ * Copyright (c) 2006      Idiap Research Institute (Samy Bengio)
+ * Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert,
+ *                                                   Samy Bengio,
+ *                                                   Johnny Mariethoz)
+ *
+ * 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. Neither the names of simdjson, Facebook, Deepmind Technologies, NYU,
+ *    NEC Laboratories America and IDIAP Research Institute 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 OWNER 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.
+ */
+
+#ifndef ISADETECTION_H
+#define ISADETECTION_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#if defined(_MSC_VER)
+#include <intrin.h>
+#include <immintrin.h>
+#elif defined(HAVE_CPUID)
+#include <cpuid.h>
+#endif
+
+enum instruction_set {
+  DEFAULT = 0x0,
+  NEON = 0x1,
+  AVX2 = 0x4,
+  SSE42 = 0x8,
+  PCLMULQDQ = 0x10,
+  BMI1 = 0x20,
+  BMI2 = 0x40,
+  ALTIVEC = 0x80,
+  AVX512F = 0x100,
+  AVX512DQ = 0x200,
+  AVX512IFMA = 0x400,
+  AVX512PF = 0x800,
+  AVX512ER = 0x1000,
+  AVX512CD = 0x2000,
+  AVX512BW = 0x4000,
+  AVX512VL = 0x8000,
+  AVX512VBMI2 = 0x10000
+};
+
+#if defined(__PPC64__)
+
+static inline uint32_t detect_supported_architectures() {
+  return ALTIVEC;
+}
+
+#elif defined(__arm__) || defined(__aarch64__) // incl. armel, armhf, arm64
+
+#if defined(__ARM_NEON)
+
+static inline uint32_t detect_supported_architectures() {
+  return NEON;
+}
+
+#else // ARM without NEON
+
+static inline uint32_t detect_supported_architectures() {
+  return DEFAULT;
+}
+
+#endif
+
+#elif defined(__x86_64__) || defined(_M_AMD64) // x64
+
+// Can be found on Intel ISA Reference for CPUID
+static const uint32_t cpuid_avx2_bit = 1 << 5;          ///< @private Bit  5 of EBX for EAX=0x7
+static const uint32_t cpuid_bmi1_bit = 1 << 3;          ///< @private bit  3 of EBX for EAX=0x7
+static const uint32_t cpuid_bmi2_bit = 1 << 8;          ///< @private bit  8 of EBX for EAX=0x7
+static const uint32_t cpuid_avx512f_bit = 1 << 16;      ///< @private bit 16 of EBX for EAX=0x7
+static const uint32_t cpuid_avx512dq_bit = 1 << 17;     ///< @private bit 17 of EBX for EAX=0x7
+static const uint32_t cpuid_avx512ifma_bit = 1 << 21;   ///< @private bit 21 of EBX for EAX=0x7
+static const uint32_t cpuid_avx512pf_bit = 1 << 26;     ///< @private bit 26 of EBX for EAX=0x7
+static const uint32_t cpuid_avx512er_bit = 1 << 27;     ///< @private bit 27 of EBX for EAX=0x7
+static const uint32_t cpuid_avx512cd_bit = 1 << 28;     ///< @private bit 28 of EBX for EAX=0x7
+static const uint32_t cpuid_avx512bw_bit = 1 << 30;     ///< @private bit 30 of EBX for EAX=0x7
+static const uint32_t cpuid_avx512vl_bit = 1U << 31;    ///< @private bit 31 of EBX for EAX=0x7
+static const uint32_t cpuid_avx512vbmi2_bit = 1 << 6;   ///< @private bit  6 of ECX for EAX=0x7
+static const uint32_t cpuid_sse42_bit = 1 << 20;        ///< @private bit 20 of ECX for EAX=0x1
+static const uint32_t cpuid_pclmulqdq_bit = 1 << 1;     ///< @private bit  1 of ECX for EAX=0x1
+static const uint32_t cpuid_have_xgetbv_bit = 1 << 27;  ///< @private bit 27 of ECX for EAX=0x1
+static const uint32_t cpuid_have_avx_bit = 1 << 28;     ///< @private bit 28 of ECX for EAX=0x1
+
+static inline void cpuid(
+  uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
+{
+#if defined(_MSC_VER)
+  int cpu_info[4];
+  __cpuid(cpu_info, *eax);
+  *eax = cpu_info[0];
+  *ebx = cpu_info[1];
+  *ecx = cpu_info[2];
+  *edx = cpu_info[3];
+#elif defined(HAVE_CPUID)
+  uint32_t level = *eax;
+  __get_cpuid(level, eax, ebx, ecx, edx);
+#else
+  uint32_t a = *eax, b, c = *ecx, d;
+  asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d));
+  *eax = a;
+  *ebx = b;
+  *ecx = c;
+  *edx = d;
+#endif
+}
+
+static inline uint64_t xgetbv(uint32_t ecx)
+{
+#if defined(_MSC_VER)
+  return _xgetbv(ecx);
+#else
+  uint32_t a, c = ecx, d;
+  asm volatile("xgetbv\n\t" : "=d"(d), "=a"(a) : "c"(c));
+  uint64_t xcr0 = ((uint64_t)d << 32) | (uint64_t)a;
+  return xcr0;
+#endif
+}
+
+static inline uint32_t detect_supported_architectures(void)
+{
+  uint32_t eax, ebx, ecx, edx;
+  uint32_t host_isa = 0x0, host_avx_isa = 0x0;
+
+  // ECX for EAX=0x7
+  eax = 0x7;
+  ecx = 0x0;
+  cpuid(&eax, &ebx, &ecx, &edx);
+  if (ebx & cpuid_bmi1_bit) {
+    host_isa |= BMI1;
+  }
+
+  if (ebx & cpuid_bmi2_bit) {
+    host_isa |= BMI2;
+  }
+
+  if (ebx & cpuid_avx2_bit) {
+    host_avx_isa |= AVX2;
+  }
+
+  if (ebx & cpuid_avx512f_bit) {
+    host_avx_isa |= AVX512F;
+  }
+
+  if (ebx & cpuid_avx512dq_bit) {
+    host_avx_isa |= AVX512DQ;
+  }
+
+  if (ebx & cpuid_avx512ifma_bit) {
+    host_avx_isa |= AVX512IFMA;
+  }
+
+  if (ebx & cpuid_avx512pf_bit) {
+    host_avx_isa |= AVX512PF;
+  }
+
+  if (ebx & cpuid_avx512er_bit) {
+    host_avx_isa |= AVX512ER;
+  }
+
+  if (ebx & cpuid_avx512cd_bit) {
+    host_avx_isa |= AVX512CD;
+  }
+
+  if (ebx & cpuid_avx512bw_bit) {
+    host_avx_isa |= AVX512BW;
+  }
+
+  if (ebx & cpuid_avx512vl_bit) {
+    host_avx_isa |= AVX512VL;
+  }
+
+  if (ecx & cpuid_avx512vbmi2_bit) {
+    host_avx_isa |= AVX512VBMI2;
+  }
+
+  bool have_avx = false, have_xgetbv = false;
+
+  // EBX for EAX=0x1
+  eax = 0x1;
+  cpuid(&eax, &ebx, &ecx, &edx);
+  if (ecx & cpuid_sse42_bit) {
+    host_isa |= SSE42;
+  }
+
+  if (ecx & cpuid_pclmulqdq_bit) {
+    host_isa |= PCLMULQDQ;
+  }
+
+  // Correct detection of AVX2 support requires more than checking the CPUID
+  // bit. Peter Cordes provides an excellent answer on Stack Overflow
+  // (https://stackoverflow.com/a/34071400) quoting the article Introduction
+  // to Intel Advanced Vector Extensions (search Wayback Machine).
+  //
+  // 1. Verify that the operating system supports XGETBV using
+  //    CPUID.1:ECX.OSXSAVE bit 27 = 1.
+  // 2. Verify the processor supports the AVX instruction extensions using:
+  //    CPUID.1:ECX bit 28 = 1.
+  // 3. Issue XGETBV, and verify that the feature-enabled mask at bits 1 and 2
+  //    are 11b (XMM state and YMM state enabled by the operating system).
+
+
+  // Determine if the CPU supports AVX
+  have_avx = (ecx & cpuid_have_avx_bit) != 0;
+  // Determine if the Operating System supports XGETBV
+  have_xgetbv = (ecx & cpuid_have_xgetbv_bit) != 0;
+
+  if (have_avx && have_xgetbv) {
+    uint64_t xcr0 = xgetbv(0x0);
+    if ((xcr0 & 0x6) == 0x6)
+      host_isa |= host_avx_isa;
+  }
+
+  return host_isa;
+}
+#else // fallback
+
+static inline uint32_t detect_supported_architectures() {
+  return DEFAULT;
+}
+
+#endif // end SIMD extension detection code
+
+#endif // ISADETECTION_H
Index: simdzone/src/zone.c
===================================================================
RCS file: simdzone/src/zone.c
diff -N simdzone/src/zone.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/zone.c	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,525 @@
+/*
+ * zone.c -- zone parser
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include "config.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stddef.h>
+#if _WIN32
+#  include <direct.h>
+#else
+#  include <unistd.h>
+#endif
+
+#include "zone.h"
+
+typedef zone_parser_t parser_t; // convenience
+typedef zone_file_t file_t;
+
+#include "attributes.h"
+#include "diagnostic.h"
+
+#if _MSC_VER
+# define strcasecmp(s1, s2) _stricmp(s1, s2)
+# define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n)
+#else
+#include <strings.h>
+#endif
+
+static const char not_a_file[] = "<string>";
+
+#include "isadetection.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#if HAVE_HASWELL
+extern int32_t zone_haswell_parse(parser_t *);
+#endif
+
+#if HAVE_WESTMERE
+extern int32_t zone_westmere_parse(parser_t *);
+#endif
+
+extern int32_t zone_fallback_parse(parser_t *);
+
+typedef struct kernel kernel_t;
+struct kernel {
+  const char *name;
+  uint32_t instruction_set;
+  int32_t (*parse)(parser_t *);
+};
+
+static const kernel_t kernels[] = {
+#if HAVE_HASWELL
+  { "haswell", AVX2, &zone_haswell_parse },
+#endif
+#if HAVE_WESTMERE
+  { "westmere", SSE42|PCLMULQDQ, &zone_westmere_parse },
+#endif
+  { "fallback", DEFAULT, &zone_fallback_parse }
+};
+
+diagnostic_push()
+msvc_diagnostic_ignored(4996)
+
+static inline const kernel_t *
+select_kernel(void)
+{
+  const char *preferred;
+  const uint32_t supported = detect_supported_architectures();
+  const size_t length = sizeof(kernels)/sizeof(kernels[0]);
+  size_t count = 0;
+
+  if ((preferred = getenv("ZONE_KERNEL"))) {
+    for (; count < length; count++)
+      if (strcasecmp(preferred, kernels[count].name) == 0)
+        break;
+    if (count == length)
+      count = 0;
+  }
+
+  for (; count < length; count++)
+    if ((kernels[count].instruction_set & supported) == (kernels[count].instruction_set))
+      return &kernels[count];
+
+  return &kernels[length - 1];
+}
+
+diagnostic_pop()
+
+static int32_t parse(parser_t *parser, void *user_data)
+{
+  const kernel_t *kernel;
+
+  kernel = select_kernel();
+  assert(kernel);
+  parser->user_data = user_data;
+  return kernel->parse(parser);
+}
+
+diagnostic_push()
+msvc_diagnostic_ignored(4996)
+
+#if _WIN32
+// The Win32 API offers PathIsRelative, but it requires linking with shlwapi.
+// Rewriting a relative path is not too complex, unlike correct conversion of
+// Windows paths in general (https://googleprojectzero.blogspot.com/2016/02/).
+// Rooted paths, relative or not, unc and extended paths are never resolved
+// relative to the includer.
+nonnull_all
+static int32_t resolve_path(const char *include, char **path)
+{
+  if ((*path = _fullpath(NULL, include, 0)))
+    return 0;
+  return (errno == ENOMEM) ? ZONE_OUT_OF_MEMORY : ZONE_NOT_A_FILE;
+}
+#else
+nonnull_all
+static int32_t resolve_path(const char *include, char **path)
+{
+  char *resolved;
+  char buffer[PATH_MAX + 1];
+
+  if (!(resolved = realpath(include, buffer)))
+    return (errno == ENOMEM) ? ZONE_OUT_OF_MEMORY : ZONE_NOT_A_FILE;
+  assert(resolved == buffer);
+  size_t length = strlen(buffer);
+  if (!(resolved = malloc(length + 1)))
+    return ZONE_OUT_OF_MEMORY;
+  memcpy(resolved, buffer, length + 1);
+  *path = resolved;
+  return 0;
+}
+#endif
+
+nonnull((1))
+static void close_file(
+  parser_t *parser, file_t *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;
+
+  assert(!is_string || file == &parser->first);
+  assert(!is_string || file->handle == NULL);
+  (void)parser;
+
+  const bool is_stdin = file->name &&
+                        file->name != not_a_file &&
+                        strcmp(file->name, "-") == 0;
+  assert(!is_stdin || (!file->handle || file->handle == stdin));
+
+  if (file->buffer.data && !is_string)
+    free(file->buffer.data);
+  file->buffer.data = NULL;
+  if (file->name && file->name != not_a_file)
+    free((char *)file->name);
+  file->name = NULL;
+  if (file->path && file->path != not_a_file)
+    free((char *)file->path);
+  file->path = NULL;
+  // stdin is not opened, it must not be closed
+  if (file->handle && file->handle != stdin)
+    (void)fclose(file->handle);
+  file->handle = NULL;
+}
+
+nonnull_all
+static void initialize_file(
+  parser_t *parser, file_t *file)
+{
+  const size_t size = offsetof(file_t, fields.head);
+  memset(file, 0, size);
+
+  if (file == &parser->first) {
+    file->includer = NULL;
+    memcpy(file->origin.octets,
+           parser->options.origin.octets,
+           parser->options.origin.length);
+    file->origin.length = parser->options.origin.length;
+    file->last_class = parser->options.default_class;
+    file->dollar_ttl = parser->options.default_ttl;
+    file->last_ttl = parser->options.default_ttl;
+    file->ttl = file->default_ttl = &file->last_ttl;
+  } else {
+    assert(parser->file);
+    file->includer = parser->file;
+    memcpy(&file->origin, &parser->file->origin, sizeof(file->origin));
+    // Retain class and TTL values.
+    file->last_class = parser->file->last_class;
+    file->dollar_ttl = parser->file->dollar_ttl;
+    file->last_ttl = parser->file->last_ttl;
+    // RRs appearing after the $TTL directive that do not explicitly include
+    // a TTL value, have their TTL set to the TTL in the $TTL directive. RRs
+    // appearing before a $TTL directive use the last explicitly stated value.
+    if (parser->file->default_ttl == &parser->file->last_ttl)
+      file->ttl = file->default_ttl = &file->last_ttl;
+    else
+      file->ttl = file->default_ttl = &file->dollar_ttl;
+  }
+
+  file->line = 1;
+  file->name = (char *)not_a_file;
+  file->path = (char *)not_a_file;
+  file->handle = NULL;
+  file->buffer.data = NULL;
+  file->start_of_line = true;
+  file->end_of_file = 1;
+  file->fields.tape[0] = NULL;
+  file->fields.head = file->fields.tail = file->fields.tape;
+  file->delimiters.tape[0] = NULL;
+  file->delimiters.head = file->delimiters.tail = file->delimiters.tape;
+  file->newlines.tape[0] = 0;
+  file->newlines.head = file->newlines.tail = file->newlines.tape;
+}
+
+nonnull_all
+static int32_t open_file(
+  parser_t *parser, file_t *file, const char *include, size_t length)
+{
+  int32_t code;
+  const size_t size = ZONE_WINDOW_SIZE + 1 + ZONE_BLOCK_SIZE;
+
+  initialize_file(parser, file);
+
+  file->path = NULL;
+  if (!(file->name = malloc(length + 1)))
+    return ZONE_OUT_OF_MEMORY;
+  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;
+  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];
+
+  if(file == &parser->first && strcmp(file->name, "-") == 0) {
+    if (!(file->path = malloc(2)))
+      return (void)close_file(parser, file), ZONE_OUT_OF_MEMORY;
+    file->path[0] = '-';
+    file->path[1] = '\0';
+  } else {
+    // The file is resolved relative to the working directory. The absolute
+    // path is used to protect against recusive includes. Not for opening the
+    // file as file descriptors for pipes and sockets the entries will be
+    // symoblic links whose content is the file type with the inode.
+    // See NLnetLabs/nsd#380.
+    if ((code = resolve_path(file->name, &file->path)))
+      return (void)close_file(parser, file), code;
+  }
+
+  if(strcmp(file->path, "-") == 0) {
+    file->handle = stdin;
+    return 0;
+  } else {
+    if ((file->handle = fopen(file->name, "rb")))
+      return 0;
+  }
+
+  switch (errno) {
+    case ENOMEM:
+      code = ZONE_OUT_OF_MEMORY;
+      break;
+    case EACCES:
+      code = ZONE_NOT_PERMITTED;
+      break;
+    default:
+      code = ZONE_NOT_A_FILE;
+      break;
+  }
+
+  close_file(parser, file);
+  return code;
+}
+
+diagnostic_pop()
+
+diagnostic_push()
+clang_diagnostic_ignored(missing-prototypes)
+
+nonnull((1))
+void zone_close_file(
+  parser_t *parser, zone_file_t *file)
+{
+  if (!file)
+    return;
+  close_file(parser, file);
+  free(file);
+}
+
+nonnull_all
+int32_t zone_open_file(
+  parser_t *parser, const char *path, size_t length, zone_file_t **file)
+{
+  int32_t code;
+
+  if (!(*file = malloc(sizeof(**file))))
+    return ZONE_OUT_OF_MEMORY;
+  if ((code = open_file(parser, *file, path, length)) == 0)
+    return 0;
+
+  free(*file);
+
+  const char *reason = NULL;
+  switch (code) {
+    case ZONE_OUT_OF_MEMORY: reason = "out of memory"; break;
+    case ZONE_NOT_PERMITTED: reason = "access denied"; break;
+    case ZONE_NOT_A_FILE:    reason = "no such file";  break;
+  }
+
+  assert(reason);
+  zone_error(parser, "Cannot open %.*s, %s", (int)length, path, reason);
+  return code;
+}
+
+nonnull_all
+void zone_close(parser_t *parser)
+{
+  assert(parser);
+  for (zone_file_t *file = parser->file, *includer; file; file = includer) {
+    includer = file->includer;
+    close_file(parser, file);
+    if (file != &parser->first)
+      free(file);
+  }
+}
+
+nonnull((1,2,3))
+static int32_t initialize_parser(
+  zone_parser_t *parser,
+  const zone_options_t *options,
+  zone_buffers_t *buffers,
+  void *user_data)
+{
+  if (!options->accept.callback)
+    return ZONE_BAD_PARAMETER;
+  if (!options->default_ttl)
+    return ZONE_BAD_PARAMETER;
+  if (!options->secondary && options->default_ttl > INT32_MAX)
+    return ZONE_BAD_PARAMETER;
+  if (!options->origin.octets || !options->origin.length)
+    return ZONE_BAD_PARAMETER;
+
+  const uint8_t *root = &options->origin.octets[options->origin.length - 1];
+  if (root[0] != 0)
+    return ZONE_BAD_PARAMETER;
+  const uint8_t *label = &options->origin.octets[0];
+  while (label < root) {
+    if (root - label < label[0])
+      return ZONE_BAD_PARAMETER;
+    label += label[0] + 1;
+  }
+
+  if (label != root)
+    return ZONE_BAD_PARAMETER;
+
+  const size_t size = offsetof(parser_t, file);
+  memset(parser, 0, size);
+  parser->options = *options;
+  parser->user_data = user_data;
+  parser->file = &parser->first;
+  parser->buffers.size = buffers->size;
+  parser->buffers.owner.active = 0;
+  parser->buffers.owner.blocks = buffers->owner;
+  parser->buffers.rdata.active = 0;
+  parser->buffers.rdata.blocks = buffers->rdata;
+  parser->owner = &parser->buffers.owner.blocks[0];
+  parser->owner->length = 0;
+  parser->rdata = &parser->buffers.rdata.blocks[0];
+
+  if (!parser->options.no_includes && !parser->options.include_limit)
+    parser->options.include_limit = 10; // arbitrary, default in NSD
+
+  return 0;
+}
+
+int32_t zone_open(
+  zone_parser_t *parser,
+  const zone_options_t *options,
+  zone_buffers_t *buffers,
+  const char *path,
+  void *user_data)
+{
+  int32_t code;
+
+  if ((code = initialize_parser(parser, options, buffers, user_data)) < 0)
+    return code;
+  if ((code = open_file(parser, &parser->first, path, strlen(path))) == 0)
+    return 0;
+
+  const char *reason = NULL;
+  switch (code) {
+    case ZONE_OUT_OF_MEMORY: reason = "out of memory"; break;
+    case ZONE_NOT_PERMITTED: reason = "access denied"; break;
+    case ZONE_NOT_A_FILE:    reason = "no such file";  break;
+  }
+
+  assert(reason);
+  zone_error(parser, "Cannot open %s, %s", path, reason);
+  return code;
+}
+
+diagnostic_pop()
+
+int32_t zone_parse(
+  zone_parser_t *parser,
+  const zone_options_t *options,
+  zone_buffers_t *buffers,
+  const char *path,
+  void *user_data)
+{
+  int32_t code;
+
+  if ((code = zone_open(parser, options, buffers, path, user_data)) < 0)
+    return code;
+  code = parse(parser, user_data);
+  zone_close(parser);
+  return code;
+}
+
+int32_t zone_parse_string(
+  parser_t *parser,
+  const zone_options_t *options,
+  zone_buffers_t *buffers,
+  const char *string,
+  size_t length,
+  void *user_data)
+{
+  int32_t code;
+
+  if ((code = initialize_parser(parser, options, buffers, user_data)) < 0)
+    return code;
+  if (!length || string[length] != '\0')
+    return ZONE_BAD_PARAMETER;
+  initialize_file(parser, parser->file);
+  parser->file->buffer.data = (char *)string;
+  parser->file->buffer.size = length;
+  parser->file->buffer.length = length;
+  parser->file->fields.tape[0] = &string[length];
+  parser->file->fields.tape[1] = &string[length];
+  assert(parser->file->end_of_file == 1);
+
+  code = parse(parser, user_data);
+  zone_close(parser);
+  return code;
+}
+
+zone_nonnull((1,5))
+static void print_message(
+  zone_parser_t *parser,
+  uint32_t priority,
+  const char *file,
+  size_t line,
+  const char *message,
+  void *user_data)
+{
+  (void)parser;
+  (void)user_data;
+
+  assert(parser->file);
+  FILE *output = priority == ZONE_INFO ? stdout : stderr;
+
+  if (file)
+    fprintf(output, "%s:%zu: %s\n", file, line, message);
+  else
+    fprintf(output, "%s\n", message);
+}
+
+void zone_vlog(
+  zone_parser_t *parser,
+  uint32_t priority,
+  const char *format,
+  va_list arguments);
+
+void zone_vlog(
+  zone_parser_t *parser,
+  uint32_t priority,
+  const char *format,
+  va_list arguments)
+{
+  char message[2048];
+  int length;
+  zone_log_t callback = print_message;
+
+  if (!(priority & ~parser->options.log.mask))
+    return;
+
+  length = vsnprintf(message, sizeof(message), format, arguments);
+  assert(length >= 0);
+  if ((size_t)length >= sizeof(message))
+    memcpy(message+(sizeof(message) - 4), "...", 3);
+  if (parser->options.log.callback)
+    callback = parser->options.log.callback;
+  assert(parser->file);
+  const char *file = parser->file->name;
+  const size_t line = parser->file->line;
+  callback(parser, priority, file, line, message, parser->user_data);
+}
+
+void zone_log(
+  zone_parser_t *parser,
+  uint32_t priority,
+  const char *format,
+  ...)
+{
+  va_list arguments;
+  va_start(arguments, format);
+  zone_vlog(parser, priority, format, arguments);
+  va_end(arguments);
+}
Index: simdzone/src/fallback/bench.c
===================================================================
RCS file: simdzone/src/fallback/bench.c
diff -N simdzone/src/fallback/bench.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/fallback/bench.c	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,32 @@
+/*
+ * bench.c -- benchmark function(s) for fallback (non-simd) implementation
+ *
+ * Copyright (c) 2022, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include "zone.h"
+#include "attributes.h"
+#include "diagnostic.h"
+#include "generic/parser.h"
+#include "fallback/scanner.h"
+
+diagnostic_push()
+clang_diagnostic_ignored(missing-prototypes)
+
+int32_t zone_bench_fallback_lex(parser_t *parser, size_t *tokens)
+{
+  token_t token;
+
+  (*tokens) = 0;
+  take(parser, &token);
+  while (token.code > 0) {
+    (*tokens)++;
+    take(parser, &token);
+  }
+
+  return token.code ? -1 : 0;
+}
+
+diagnostic_pop()
Index: simdzone/src/fallback/bits.h
===================================================================
RCS file: simdzone/src/fallback/bits.h
diff -N simdzone/src/fallback/bits.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/fallback/bits.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,88 @@
+/*
+ * bits.h -- bit manipulation instructions
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef BITS_H
+#define BITS_H
+
+#if _MSC_VER
+#include <intrin.h>
+
+static really_inline uint64_t trailing_zeroes(uint64_t mask)
+{
+  unsigned long index;
+  if (_BitScanForward64(&index, mask))
+    return index;
+  else
+    return 64;
+}
+
+static really_inline uint64_t leading_zeroes(uint64_t mask)
+{
+  unsigned long index;
+  if (_BitScanReverse64(&index, mask))
+    return 63 - index;
+  else
+    return 64;
+}
+
+#else
+
+static really_inline uint64_t trailing_zeroes(uint64_t mask)
+{
+#if has_builtin(__builtin_ctzll)
+  return (uint64_t)__builtin_ctzll(mask);
+#else
+  // Code by Kim Walish from https://www.chessprogramming.org/BitScan.
+  // Distributed under CC BY-SA 3.0.
+  static const uint64_t magic = 0x03f79d71b4cb0a89ull;
+  const int magictable[64] = {
+      0, 47,  1, 56, 48, 27,  2, 60,
+     57, 49, 41, 37, 28, 16,  3, 61,
+     54, 58, 35, 52, 50, 42, 21, 44,
+     38, 32, 29, 23, 17, 11,  4, 62,
+     46, 55, 26, 59, 40, 36, 15, 53,
+     34, 51, 20, 43, 31, 22, 10, 45,
+     25, 39, 14, 33, 19, 30,  9, 24,
+     13, 18,  8, 12,  7,  6,  5, 63
+  };
+
+  return magictable[((mask ^ (mask - 1)) * magic) >> 58];
+#endif
+}
+
+static really_inline uint64_t leading_zeroes(uint64_t mask)
+{
+#if has_builtin(__builtin_clzll)
+  return (uint64_t)__builtin_clzll(mask);
+#else
+  // Code by Kim Walish from https://www.chessprogramming.org/BitScan.
+  // Distributed under CC BY-SA 3.0.
+  static const uint64_t magic = 0x03f79d71b4cb0a89ull;
+  const int magictable[64] = {
+     63, 16, 62,  7, 15, 36, 61,  3,
+      6, 14, 22, 26, 35, 47, 60,  2,
+      9,  5, 28, 11, 13, 21, 42, 19,
+     25, 31, 34, 40, 46, 52, 59,  1,
+     17,  8, 37,  4, 23, 27, 48, 10,
+     29, 12, 43, 20, 32, 41, 53, 18,
+     38, 24, 49, 30, 44, 33, 54, 39,
+     50, 45, 55, 51, 56, 57, 58,  0
+  };
+
+  mask |= mask >> 1;
+  mask |= mask >> 2;
+  mask |= mask >> 4;
+  mask |= mask >> 8;
+  mask |= mask >> 16;
+  mask |= mask >> 32;
+
+  return magictable[(mask * magic) >> 58];
+#endif
+}
+#endif // _MSC_VER
+#endif // BITS_H
Index: simdzone/src/fallback/name.h
===================================================================
RCS file: simdzone/src/fallback/name.h
diff -N simdzone/src/fallback/name.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/fallback/name.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,58 @@
+/*
+ * name.h -- domain name parser
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef NAME_H
+#define NAME_H
+
+nonnull_all
+static really_inline int32_t scan_name(
+  const char *data,
+  size_t length,
+  uint8_t octets[255 + ZONE_BLOCK_SIZE],
+  size_t *lengthp)
+{
+  uint8_t *l = octets, *w = octets + 1;
+  const uint8_t *we = octets + 255;
+  const char *t = data, *te = t + length;
+
+  l[0] = 0;
+
+  if (*t == '.')
+    return (*lengthp = length) == 1 ? 0 : -1;
+
+  while ((t < te) & (w < we)) {
+    *w = (uint8_t)*t;
+    if (*t == '\\') {
+      uint32_t n;
+      if (!(n = unescape(t, w)))
+        return -1;
+      w += 1; t += n;
+    } else if (*t == '.') {
+      if ((w - 1) - l > 63 || (w - 1) - l == 0)
+        return -1;
+      l[0] = (uint8_t)((w - 1) - l);
+      l = w;
+      l[0] = 0;
+      w += 1; t += 1;
+    } else {
+      w += 1; t += 1;
+    }
+  }
+
+  if ((w - 1) - l > 63)
+    return -1;
+  *l = (uint8_t)((w - 1) - l);
+
+  if (t != te || w > we)
+    return -1;
+
+  *lengthp = (size_t)(w - octets);
+  return *l != 0;
+}
+
+#endif // NAME_H
Index: simdzone/src/fallback/parser.c
===================================================================
RCS file: simdzone/src/fallback/parser.c
diff -N simdzone/src/fallback/parser.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/fallback/parser.c	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,51 @@
+/*
+ * parser.c -- compilation target for fallback (DNS) zone parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include "zone.h"
+#include "attributes.h"
+#include "diagnostic.h"
+#include "generic/endian.h"
+#include "fallback/bits.h"
+#include "generic/parser.h"
+#include "fallback/scanner.h"
+#include "generic/number.h"
+#include "generic/ttl.h"
+#include "generic/time.h"
+#include "fallback/text.h"
+#include "fallback/name.h"
+#include "generic/ip4.h"
+#include "generic/ip6.h"
+#include "generic/base16.h"
+#include "generic/base32.h"
+#include "generic/base64.h"
+#include "generic/nsec.h"
+#include "generic/nxt.h"
+#include "generic/caa.h"
+#include "generic/ilnp64.h"
+#include "generic/eui.h"
+#include "generic/nsap.h"
+#include "generic/wks.h"
+#include "generic/loc.h"
+#include "generic/gpos.h"
+#include "generic/apl.h"
+#include "generic/svcb.h"
+#include "generic/cert.h"
+#include "generic/algorithm.h"
+#include "generic/types.h"
+#include "generic/type.h"
+#include "generic/format.h"
+
+diagnostic_push()
+clang_diagnostic_ignored(missing-prototypes)
+
+int32_t zone_fallback_parse(parser_t *parser)
+{
+  return parse(parser);
+}
+
+diagnostic_pop()
Index: simdzone/src/fallback/scanner.h
===================================================================
RCS file: simdzone/src/fallback/scanner.h
diff -N simdzone/src/fallback/scanner.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/fallback/scanner.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,174 @@
+/*
+ * scanner.h -- fallback (non-simd) lexical analyzer for (DNS) zone data
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef SCANNER_H
+#define SCANNER_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+nonnull_all
+static really_inline const char *scan_comment(
+  parser_t *parser, const char *start, const char *end)
+{
+  assert(!parser->file->state.is_escaped);
+
+  while (start < end) {
+    if (unlikely(*start == '\n')) {
+      parser->file->state.in_comment = 0;
+      return start;
+    }
+    start++;
+  }
+
+  parser->file->state.in_comment = 1;
+  return start;
+}
+
+nonnull_all
+static really_inline const char *scan_quoted(
+  parser_t *parser, const char *start, const char *end)
+{
+  if (unlikely(parser->file->state.is_escaped && start < end))
+    goto escaped;
+
+  while (start < end) {
+    if (*start == '\\') {
+      start++;
+escaped:
+      if ((parser->file->state.is_escaped = (start == end)))
+        break;
+      assert(start < end);
+      *parser->file->newlines.tail += (*start == '\n');
+      start++;
+    } else if (*start == '\"') {
+      parser->file->state.in_quoted = 0;
+      *parser->file->delimiters.tail++ = start;
+      return ++start;
+    } else {
+      *parser->file->newlines.tail += (*start == '\n');
+      start++;
+    }
+  }
+
+  parser->file->state.in_quoted = 1;
+  return start;
+}
+
+nonnull_all
+static really_inline const char *scan_contiguous(
+  parser_t *parser, const char *start, const char *end)
+{
+  if (parser->file->state.is_escaped && start < end)
+    goto escaped;
+
+  while (start < end) {
+    // null-byte is considered contiguous by the indexer (for now)
+    if (likely((classify[ (uint8_t)*start ] & ~CONTIGUOUS) == 0)) {
+      if (unlikely(*start == '\\')) {
+        start++;
+escaped:
+        if ((parser->file->state.is_escaped = (start == end)))
+          break;
+        assert(start < end);
+        parser->file->newlines.tail[0] += (*start == '\n');
+      }
+      start++;
+    } else {
+      parser->file->state.follows_contiguous = 0;
+      *parser->file->delimiters.tail++ = start;
+      return start;
+    }
+  }
+
+  parser->file->state.follows_contiguous = 1;
+  return start;
+}
+
+nonnull_all
+static really_inline void scan(
+  parser_t *parser, const char *start, const char *end)
+{
+  if (parser->file->state.follows_contiguous)
+    start = scan_contiguous(parser, start, end);
+  else if (parser->file->state.in_comment)
+    start = scan_comment(parser, start, end);
+  else if (parser->file->state.in_quoted)
+    start = scan_quoted(parser, start, end);
+
+  while (start < end) {
+    const int32_t code = classify[(uint8_t)*start];
+    if (code == BLANK) {
+      start++;
+    } else if ((code & ~CONTIGUOUS) == 0) {
+      // null-byte is considered contiguous by the indexer (for now)
+      *parser->file->fields.tail++ = start;
+      start = scan_contiguous(parser, start, end);
+    } else if (code == LINE_FEED) {
+      if (*parser->file->newlines.tail) {
+        *parser->file->fields.tail++ = line_feed;
+        parser->file->newlines.tail++;
+      } else {
+        *parser->file->fields.tail++ = start;
+      }
+      start++;
+    } else if (code == QUOTED) {
+      *parser->file->fields.tail++ = start;
+      start = scan_quoted(parser, start + 1, end);
+    } else if (code == LEFT_PAREN || code == RIGHT_PAREN) {
+      *parser->file->fields.tail++ = start;
+      start++;
+    } else {
+      assert(code == COMMENT);
+      start = scan_comment(parser, start, end);
+    }
+  }
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t reindex(parser_t *parser)
+{
+  assert(parser->file->buffer.index <= parser->file->buffer.length);
+  size_t left = parser->file->buffer.length - parser->file->buffer.index;
+  const char *data = parser->file->buffer.data + parser->file->buffer.index;
+  const char **tape = parser->file->fields.tail;
+  const char **tape_limit = parser->file->fields.tape + ZONE_TAPE_SIZE;
+
+  if (left >= ZONE_BLOCK_SIZE) {
+    const char *data_limit = parser->file->buffer.data +
+                            (parser->file->buffer.length - ZONE_BLOCK_SIZE);
+    while (data <= data_limit && ((uintptr_t)tape_limit - (uintptr_t)tape) >= ZONE_BLOCK_SIZE) {
+      scan(parser, data, data + ZONE_BLOCK_SIZE);
+      parser->file->buffer.index += ZONE_BLOCK_SIZE;
+      data += ZONE_BLOCK_SIZE;
+      tape = parser->file->fields.tail;
+    }
+
+    assert(parser->file->buffer.index <= parser->file->buffer.length);
+    left = parser->file->buffer.length - parser->file->buffer.index;
+  }
+
+  // only scan partial blocks after reading all data
+  if (parser->file->end_of_file) {
+    assert(left < ZONE_BLOCK_SIZE);
+    if (!left) {
+      parser->file->end_of_file = NO_MORE_DATA;
+    } else if (((uintptr_t)tape_limit - (uintptr_t)tape) >= left) {
+      scan(parser, data, data + left);
+      parser->file->end_of_file = NO_MORE_DATA;
+      parser->file->buffer.index += left;
+      parser->file->state.follows_contiguous = 0;
+    }
+  }
+
+  return (parser->file->state.follows_contiguous | parser->file->state.in_quoted) != 0;
+}
+
+#endif // SCANNER_H
Index: simdzone/src/fallback/text.h
===================================================================
RCS file: simdzone/src/fallback/text.h
diff -N simdzone/src/fallback/text.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/fallback/text.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,78 @@
+/*
+ * text.h -- fallback string parser
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef TEXT_H
+#define TEXT_H
+
+nonnull_all
+static really_inline uint32_t unescape(const char *text, uint8_t *wire)
+{
+  uint8_t d[3];
+
+  if ((d[0] = (uint8_t)text[1] - '0') > 9) {
+    *wire = (uint8_t)text[1];
+    return 2u;
+  } else {
+    d[1] = (uint8_t)text[2] - '0';
+    d[2] = (uint8_t)text[3] - '0';
+    uint32_t o = d[0] * 100 + d[1] * 10 + d[2];
+    *wire = (uint8_t)o;
+    return (o > 255 || d[1] > 9 || d[2] > 9) ? 0 : 4u;
+  }
+}
+
+nonnull_all
+static really_inline int32_t scan_string(
+  const char *data,
+  size_t length,
+  uint8_t *octets,
+  const uint8_t *limit)
+{
+  const char *text = data, *text_limit = data + length;
+  uint8_t *wire = octets;
+
+  if (likely((uintptr_t)limit - (uintptr_t)wire >= length)) {
+    while (text < text_limit) {
+      *wire = (uint8_t)*text;
+      if (likely(*text != '\\')) {
+        text += 1;
+        wire += 1;
+      } else {
+        const uint32_t octet = unescape(text, wire);
+        if (!octet)
+          return -1;
+        text += octet;
+        wire += 1;
+      }
+    }
+
+    if (text != text_limit)
+      return -1;
+    return (int32_t)(wire - octets);
+  } else {
+    while (text < text_limit && wire < limit) {
+      *wire = (uint8_t)*text;
+      if (likely(*text != '\\')) {
+        text += 1;
+        wire += 1;
+      } else {
+        const uint32_t octet = unescape(text, wire);
+        if (!octet)
+          return -1;
+        text += octet;
+        wire += 1;
+      }
+    }
+
+    if (text != text_limit || wire > limit)
+      return -1;
+    return (int32_t)(wire - octets);
+  }
+}
+
+#endif // TEXT_H
Index: simdzone/src/generic/algorithm.h
===================================================================
RCS file: simdzone/src/generic/algorithm.h
diff -N simdzone/src/generic/algorithm.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/algorithm.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,173 @@
+/**
+ * algorithm.h -- Algorithm RDATA parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef ALGORITHM_H
+#define ALGORITHM_H
+
+// https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
+
+typedef struct algorithm algorithm_t;
+struct algorithm {
+  struct {
+    char name[24];
+    size_t length;
+  } key;
+  uint8_t value;
+};
+
+#define BAD_ALGORITHM(value) \
+  { { "",  0 }, 0 }
+#define ALGORITHM(name, value) \
+  { { name, sizeof(name) - 1 }, value }
+
+static const algorithm_t algorithms[32] = {
+  BAD_ALGORITHM(0),
+  ALGORITHM("RSAMD5", 1),
+  ALGORITHM("DH", 2),
+  ALGORITHM("DSA", 3),
+  ALGORITHM("ECC", 4),
+  ALGORITHM("RSASHA1", 5),
+  ALGORITHM("DSA-NSEC-SHA1", 6),
+  ALGORITHM("RSASHA1-NSEC3-SHA1", 7),
+  ALGORITHM("RSASHA256", 8),
+  BAD_ALGORITHM(9),
+  ALGORITHM("RSASHA512", 10),
+  BAD_ALGORITHM(11),
+  ALGORITHM("ECC-GOST", 12),
+  ALGORITHM("ECDSAP256SHA256", 13),
+  ALGORITHM("ECDSAP384SHA384", 14),
+  BAD_ALGORITHM(15),
+  ALGORITHM("INDIRECT", 252),
+  ALGORITHM("PRIVATEDNS", 253),
+  ALGORITHM("PRIVATEOID", 254),
+};
+
+static const struct {
+  const algorithm_t *algorithm;
+  uint8_t mask[24];
+} algorithm_hash_map[16] = {
+  { &algorithms[2],  // DH (0)
+    { 0xdf, 0xdf, 0 } },
+  { &algorithms[10], // RSASHA512 (1)
+    { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff,
+      0xff, 0 } },
+  { &algorithms[7],  // RSASHA1-NSEC3-SHA1 (2)
+    { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff, 
+      0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff, 0xdf, 0xdf,
+      0xdf, 0xff, 0 } },
+  { &algorithms[8],  // RSASHA256 (3)
+    { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff,
+      0xff, 0 } },
+  { &algorithms[13], // ECDSAP256SHA256 (4)
+    { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff,
+      0xff, 0xdf, 0xdf, 0xdf, 0xff, 0xff, 0xff, 0 } },
+  { &algorithms[0],  // unknown
+    { 0 } },
+  { &algorithms[6],  // DSA-NSEC-SHA1 (6)
+    { 0xdf, 0xdf, 0xdf, 0xff, 0xdf, 0xdf, 0xdf, 0xdf,
+      0xff, 0xdf, 0xdf, 0xdf, 0xff, 0 } },
+  { &algorithms[1],  // RSAMD5 (7)
+    { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0 } },
+  { &algorithms[5],  // RSASHA1 (8)
+    { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0 } },
+  { &algorithms[17], // PRIVATEDNS (9)
+    { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+      0xdf, 0xdf, 0 } },
+  { &algorithms[18], // PRIVATEOID (10)
+    { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+      0xdf, 0xdf, 0 } },
+  { &algorithms[16], // INDIRECT (11)
+    { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+      0 } },
+  { &algorithms[14], // ECDSAP384SHA384 (12)
+    { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff,
+      0xff, 0xdf, 0xdf, 0xdf, 0xff, 0xff, 0xff, 0 } },
+  { &algorithms[3],  // DSA (13)
+    { 0xdf, 0xdf, 0xdf, 0 } },
+  { &algorithms[4],  // ECC (14)
+    { 0xdf, 0xdf, 0xdf, 0 } },
+  { &algorithms[12], // ECC-GHOST (15)
+    { 0xdf, 0xdf, 0xdf, 0xff, 0xdf, 0xdf, 0xdf, 0xdf,
+      0 } }
+};
+
+#undef UNKNOWN_ALGORITHM
+#undef ALGORITHM
+
+// magic value generated using algorithm-hash.c
+static uint8_t algorithm_hash(uint64_t value)
+{
+  value = le64toh(value);
+  uint32_t value32 = (uint32_t)((value >> 32) ^ value);
+  return (uint8_t)((value32 * 29874llu) >> 32) & 0xf;
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t scan_algorithm(
+  const char *data, size_t length, uint8_t *number)
+{
+  static const int8_t zero_masks[48] = {
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+     0,  0,  0,  0,  0,  0,  0,  0,
+     0,  0,  0,  0,  0,  0,  0,  0
+  };
+
+  if ((uint8_t)*data - '0' > 9) {
+    uint64_t input;
+    memcpy(&input, data, 8);
+    const uint64_t letter_mask = 0x4040404040404040llu;
+    // convert to upper case
+    input &= ~((input & letter_mask) >> 1);
+    // zero out non-relevant bytes
+    uint64_t zero_mask;
+    memcpy(&zero_mask, &zero_masks[32 - (length & 0x1f)], 8);
+    input &= zero_mask;
+    const uint8_t index = algorithm_hash(input);
+    assert(index < 16);
+    const algorithm_t *algorithm = algorithm_hash_map[index].algorithm;
+    uint64_t matches, mask, name;
+    // compare bytes 0-7
+    memcpy(&name, algorithm->key.name, 8);
+    matches = input == name;
+    // compare bytes 8-15
+    memcpy(&input, data + 8, 8);
+    memcpy(&mask, algorithm_hash_map[index].mask + 8, 8);
+    memcpy(&name, algorithm->key.name + 8, 8);
+    matches &= (input & mask) == name;
+    // compare bytes 16-23
+    memcpy(&input, data + 16, 8);
+    memcpy(&mask, algorithm_hash_map[index].mask + 16, 8);
+    memcpy(&name, algorithm->key.name + 16, 8);
+    matches &= (input & mask) == name;
+    *number = algorithm->value;
+    return matches & (length == algorithm->key.length) & (*number > 0);
+  }
+
+  return scan_int8(data, length, number);
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t parse_algorithm(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  if (!scan_algorithm(token->data, token->length, rdata->octets))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  rdata->octets++;
+  return 0;
+}
+
+#endif // ALGORITHM_H
Index: simdzone/src/generic/apl.h
===================================================================
RCS file: simdzone/src/generic/apl.h
diff -N simdzone/src/generic/apl.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/apl.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,74 @@
+/*
+ * apl.h -- Address Prefix Lists (RFC3123) parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef APL_H
+#define APL_H
+
+static really_inline int32_t scan_apl(
+  const char *text, size_t length, uint8_t *octets, size_t size)
+{
+  uint8_t negate = text[0] == '!';
+  uint8_t digits[3];
+  int32_t count;
+  uint32_t prefix;
+  const uint8_t af_inet[2] = { 0x00, 0x01 }, af_inet6[2] = { 0x00, 0x02 };
+
+  // address family is immediately followed by a colon ":"
+  if (text[negate + 1] != ':')
+    return -1;
+
+  switch (text[negate]) {
+    case '1':
+      if (size < 8)
+        return -1;
+      memcpy(octets, af_inet, sizeof(af_inet));
+      if (!(count = scan_ip4(&text[negate+2], &octets[4])))
+        return -1;
+      count += negate + 2;
+      digits[0] = (uint8_t)text[count+1] - '0';
+      digits[1] = (uint8_t)text[count+2] - '0';
+      if (text[count] != '/' || digits[0] > 9)
+        return -1;
+      if (digits[1] > 9)
+        (void)(count += 2), prefix = digits[0];
+      else
+        (void)(count += 3), prefix = digits[0] * 10 + digits[1];
+      if (prefix > 32 || (size_t)count != length)
+        return -1;
+      octets[2] = (uint8_t)prefix;
+      octets[3] = (uint8_t)((negate << 7) | 4);
+      return 8;
+    case '2':
+      if (size < 20)
+        return -1;
+      memcpy(octets, af_inet6, sizeof(af_inet6));
+      if (!(count = scan_ip6(&text[negate+2], &octets[4])))
+        return -1;
+      count += negate + 2;
+      digits[0] = (uint8_t)text[count+1] - '0';
+      digits[1] = (uint8_t)text[count+2] - '0';
+      digits[2] = (uint8_t)text[count+3] - '0';
+      if (text[count] != '/' || digits[0] > 9)
+        return -1;
+      if (digits[1] > 9)
+        (void)(count += 2), prefix = digits[0];
+      else if (digits[2] > 9)
+        (void)(count += 3), prefix = digits[0] * 10 + digits[1];
+      else
+        (void)(count += 4), prefix = digits[0] * 100 + digits[1] * 10 + digits[0];
+      if (prefix > 128 || (size_t)count != length)
+        return -1;
+      octets[2] = (uint8_t)prefix;
+      octets[3] = (uint8_t)((negate << 7) | 16);
+      return 20;
+    default:
+      return -1;
+  }
+}
+
+#endif // APL_H
Index: simdzone/src/generic/base16.h
===================================================================
RCS file: simdzone/src/generic/base16.h
diff -N simdzone/src/generic/base16.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/base16.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,304 @@
+/*
+ * base16.h -- Fast Base16 stream decoder
+ *
+ * Copyright (c) 2005-2016, Nick Galbreath.
+ * Copyright (c) 2022, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ */
+#ifndef BASE16_H
+#define BASE16_H
+
+// adaptation of base16 decoder by Nick Galbreath to operate similar to the
+// base64 stream decoder by Alfred Klomp.
+// https://github.com/client9/stringencoders
+// https://github.com/aklomp/base64
+
+struct base16_state {
+  int eof;
+  int bytes;
+  unsigned char carry;
+};
+
+#define BASE16_EOF 1
+
+static const uint32_t base16_table_dec_32bit_d0[256] = {
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 256, 256,
+  256, 256, 256, 256, 256, 160, 176, 192, 208, 224, 240, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 160, 176, 192, 208, 224, 240, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256
+};
+
+static const uint32_t base16_table_dec_32bit_d1[256] = {
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 256, 256,
+  256, 256, 256, 256, 256, 10, 11, 12, 13, 14, 15, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 10, 11, 12, 13, 14, 15, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+  256, 256, 256, 256
+};
+
+static really_inline int
+base16_dec_loop_generic_32_inner(
+  const uint8_t **s, uint8_t **o, size_t *rounds)
+{
+  const uint32_t val1 = base16_table_dec_32bit_d0[(*s)[0]]
+                      | base16_table_dec_32bit_d1[(*s)[1]];
+  const uint32_t val2 = base16_table_dec_32bit_d0[(*s)[2]]
+                      | base16_table_dec_32bit_d1[(*s)[3]];
+
+  if (val1 > 0xff || val2 > 0xff)
+    return 0;
+
+  (*o)[0] = (uint8_t)val1;
+  (*o)[1] = (uint8_t)val2;
+
+  *s += 4;
+  *o += 2;
+  *rounds -= 1;
+
+  return 1;
+}
+
+static really_inline void
+base16_dec_loop_generic_32(
+  const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)
+{
+  if (*slen < 4)
+    return;
+
+  // some comment on the how and what...
+  size_t rounds = (*slen - 4) / 4;
+
+  *slen -= rounds * 4; // 4 bytes consumed per round
+  *olen += rounds * 2; // 2 bytes produced per round
+
+  do {
+    if (rounds >= 8) {
+      if (base16_dec_loop_generic_32_inner(s, o, &rounds) &&
+          base16_dec_loop_generic_32_inner(s, o, &rounds) &&
+          base16_dec_loop_generic_32_inner(s, o, &rounds) &&
+          base16_dec_loop_generic_32_inner(s, o, &rounds) &&
+          base16_dec_loop_generic_32_inner(s, o, &rounds) &&
+          base16_dec_loop_generic_32_inner(s, o, &rounds) &&
+          base16_dec_loop_generic_32_inner(s, o, &rounds) &&
+          base16_dec_loop_generic_32_inner(s, o, &rounds))
+        continue;
+      break;
+    }
+    if (rounds >= 4) {
+      if (base16_dec_loop_generic_32_inner(s, o, &rounds) &&
+          base16_dec_loop_generic_32_inner(s, o, &rounds) &&
+          base16_dec_loop_generic_32_inner(s, o, &rounds) &&
+          base16_dec_loop_generic_32_inner(s, o, &rounds))
+        continue;
+      break;
+    }
+    if (rounds >= 2) {
+      if (base16_dec_loop_generic_32_inner(s, o, &rounds) &&
+          base16_dec_loop_generic_32_inner(s, o, &rounds))
+        continue;
+      break;
+    }
+    base16_dec_loop_generic_32_inner(s, o, &rounds);
+    break;
+  } while (rounds > 0);
+
+  // Adjust for any rounds that were skipped:
+  *slen += rounds * 4;
+  *olen -= rounds * 2;
+}
+
+nonnull((1,2,4,5))
+static really_inline int base16_stream_decode(
+  struct base16_state *state,
+  const char *src,
+  size_t srclen,
+  uint8_t *out,
+  size_t *outlen)
+{
+  int ret = 0;
+  const uint8_t *s = (const uint8_t *) src;
+  uint8_t *o = (uint8_t *) out;
+  uint32_t q;
+
+  // Use local temporaries to avoid cache thrashing:
+  size_t olen = 0;
+  size_t slen = srclen; 
+  struct base16_state st;
+  st.eof = state->eof;
+  st.bytes = state->bytes;
+  st.carry = state->carry;
+
+  if (st.eof) {
+    *outlen = 0;
+    return ret;
+  }
+
+  // Duff's device again:
+  switch (st.bytes)
+  {
+#if defined(__SUNPRO_C)
+#pragma error_messages(off, E_STATEMENT_NOT_REACHED)
+#endif
+    for (;;)
+#if defined(__SUNPRO_C)
+#pragma error_messages(default, E_STATEMENT_NOT_REACHED)
+#endif
+    {
+    case 0:
+      base16_dec_loop_generic_32(&s, &slen, &o, &olen);
+      if (slen-- == 0) {
+        ret = 1;
+        break;
+      }
+      if ((q = base16_table_dec_32bit_d0[*s++]) >= 255) {
+        st.eof = BASE16_EOF;
+        break;
+      }
+      st.carry = (uint8_t)q;
+      st.bytes = 1;
+
+      // fallthrough
+
+    case 1:
+      if (slen-- == 0) {
+        ret = 1;
+        break;
+      }
+      if ((q = base16_table_dec_32bit_d1[*s++]) >= 255) {
+        st.eof = BASE16_EOF;
+        break;
+      }
+      *o++ = st.carry | (uint8_t)q;
+      st.carry = 0;
+      st.bytes = 0;
+      olen++;
+    }
+  }
+
+  state->eof = st.eof;
+  state->bytes = st.bytes;
+  state->carry = st.carry;
+  *outlen = olen;
+  return ret;
+}
+
+nonnull((1,3,4))
+static really_inline int base16_decode(
+  const char *src, size_t srclen, uint8_t *out, size_t *outlen)
+{
+  struct base16_state state = { .eof = 0, .bytes = 0, .carry = 0 };
+  return base16_stream_decode(&state, src, srclen, out, outlen) & !state.bytes;
+}
+
+// FIXME: RFC3597 section 5 states each word of data must contain an even
+//        number of hexadecimal digits. The same is not true for DS records.
+//        RFC4043 section 5.3 merely states whitespace is allowed within the
+//        hexadecimal text. Words containing an uneven number of hexadecimal
+//        digits are impractical, but supported (BIND supports uneven
+//        sequences)
+nonnull_all
+static really_inline int32_t parse_base16_sequence(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  token_t *token)
+{
+  if (is_contiguous(token)) {
+    struct base16_state state = { .eof = 0, .bytes = 0, .carry = 0 };
+
+    do {
+      size_t length = (token->length + 1) / 2;
+      if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets < length)
+        SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+      if (!base16_stream_decode(&state, token->data, token->length, rdata->octets, &length))
+        SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+      rdata->octets += length;
+      take(parser, token);
+    } while (is_contiguous(token));
+
+    if (state.bytes)
+      *rdata->octets++ = state.carry;
+  }
+
+  return have_delimiter(parser, type, token);
+}
+
+nonnull_all
+static really_inline int32_t parse_base16(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  size_t length = token->length / 2;
+  if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets < length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  if (!base16_decode(token->data, token->length, rdata->octets, &length))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  rdata->octets += length;
+  return 0;
+}
+
+nonnull_all
+static really_inline int32_t parse_salt(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  if (token->length == 1 && token->data[0] == '-')
+    return (void)(*rdata->octets++ = 0), 0;
+
+  size_t length = token->length / 2;
+  uint8_t *octets = rdata->octets++;
+  // FIXME: not quite right yet! we must not exceed 255 octets!
+  if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets < (length + 1))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  if (!base16_decode(token->data, token->length, rdata->octets, &length))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  *octets = (uint8_t)length;
+  rdata->octets += length;
+  return 0;
+}
+
+#endif // BASE16_H
Index: simdzone/src/generic/base32.h
===================================================================
RCS file: simdzone/src/generic/base32.h
diff -N simdzone/src/generic/base32.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/base32.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,117 @@
+/*
+ * base32.h -- Base32 decoder
+ *
+ * Copyright (c) 2022, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef BASE32_H
+#define BASE32_H
+
+static const uint8_t b32rmap[256] = {
+  0xfd, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /*   0 -   7 */
+  0xff, 0xfe, 0xfe, 0xfe,  0xfe, 0xfe, 0xff, 0xff,  /*   8 -  15 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /*  16 -  23 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /*  24 -  31 */
+  0xfe, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /*  32 -  39 */
+  0xff, 0xff, 0xff, 0x3e,  0xff, 0xff, 0xff, 0x3f,  /*  40 -  47 */
+  0x00, 0x01, 0x02, 0x03,  0x04, 0x05, 0x06, 0x07,  /*  48 -  55 */
+  0x08, 0x09, 0xff, 0xff,  0xff, 0xfd, 0xff, 0xff,  /*  56 -  63 */
+  0xff, 0x0a, 0x0b, 0x0c,  0x0d, 0x0e, 0x0f, 0x10,  /*  64 -  71 */
+  0x11, 0x12, 0x13, 0x14,  0x15, 0x16, 0x17, 0x18,  /*  72 -  79 */
+  0x19, 0x1a, 0x1b, 0x1c,  0x1d, 0x1e, 0x1f, 0xff,  /*  80 -  87 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /*  88 -  95 */
+  0xff, 0x0a, 0x0b, 0x0c,  0x0d, 0x0e, 0x0f, 0x10,  /*  96 - 103 */
+  0x11, 0x12, 0x13, 0x14,  0x15, 0x16, 0x17, 0x18,  /* 104 - 111 */
+  0x19, 0x1a, 0x1b, 0x1c,  0x1d, 0x1e, 0x1f, 0xff,  /* 112 - 119 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 120 - 127 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 128 - 135 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 136 - 143 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 144 - 151 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 152 - 159 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 160 - 167 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 168 - 175 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 176 - 183 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 184 - 191 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 192 - 199 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 200 - 207 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 208 - 215 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 216 - 223 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 224 - 231 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 232 - 239 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 240 - 247 */
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,  /* 248 - 255 */
+};
+
+static const uint8_t b32rmap_special = 0xf0;
+
+nonnull_all
+static really_inline int32_t parse_base32(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  uint32_t state = 0;
+
+  size_t length = (token->length * 5) / 8;
+  if (length > 255 || (uintptr_t)rdata->limit - (uintptr_t)rdata->octets < (length + 1))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  *rdata->octets++ = (uint8_t)length;
+
+  const char *p = token->data;
+  for (;; p++) {
+    const uint8_t ofs = b32rmap[(uint8_t)*p];
+
+    if (ofs >= b32rmap_special)
+      break;
+
+    switch (state) {
+      case 0:
+        *rdata->octets    = (uint8_t)(ofs << 3);
+        state = 1;
+        break;
+      case 1:
+        *rdata->octets++ |= (uint8_t)(ofs >> 2);
+        *rdata->octets    = (uint8_t)(ofs << 6);
+        state = 2;
+        break;
+      case 2:
+        *rdata->octets   |= (uint8_t)(ofs << 1);
+        state = 3;
+        break;
+      case 3:
+        *rdata->octets++ |= (uint8_t)(ofs >> 4);
+        *rdata->octets    = (uint8_t)(ofs << 4);
+        state = 4;
+        break;
+      case 4:
+        *rdata->octets++ |= (uint8_t)(ofs >> 1);
+        *rdata->octets    = (uint8_t)(ofs << 7);
+        state = 5;
+        break;
+      case 5:
+        *rdata->octets   |= (uint8_t)(ofs << 2);
+        state = 6;
+        break;
+      case 6:
+        *rdata->octets++ |= (uint8_t)(ofs >> 3);
+        *rdata->octets    = (uint8_t)(ofs << 5);
+        state = 7;
+        break;
+      case 7:
+        *rdata->octets++ |= ofs;
+        state = 0;
+        break;
+    }
+  }
+
+  if (p != token->data + token->length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  return 0;
+}
+
+#endif // BASE32_H
Index: simdzone/src/generic/base64.h
===================================================================
RCS file: simdzone/src/generic/base64.h
diff -N simdzone/src/generic/base64.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/base64.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,738 @@
+/*
+ * base64.h -- Fast Base64 stream decoder
+ *
+ * Copyright (c) 2005-2007, Nick Galbreath.
+ * Copyright (c) 2015-2018, Wojciech Muła.
+ * Copyright (c) 2016-2017, Matthieu Darbois.
+ * Copyright (c) 2013-2022, Alfred Klomp.
+ * Copyright (c) 2022, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ */
+#ifndef BASE64_H
+#define BASE64_H
+
+// Modified version of https://github.com/aklomp/base64
+
+struct base64_state {
+	int eof;
+	int bytes;
+	unsigned char carry;
+};
+
+#include <stdint.h>
+#define CHAR62 '+'
+#define CHAR63 '/'
+#define CHARPAD '='
+
+// End-of-file definitions.
+// Almost end-of-file when waiting for the last '=' character:
+#define BASE64_AEOF 1
+// End-of-file when stream end has been reached or invalid input provided:
+#define BASE64_EOF 2
+
+// In the lookup table below, note that the value for '=' (character 61) is
+// 254, not 255. This character is used for in-band signaling of the end of
+// the datastream, and we will use that later. The characters A-Z, a-z, 0-9
+// and + / are mapped to their "decoded" values. The other bytes all map to
+// the value 255, which flags them as "invalid input".
+
+static const uint8_t
+base64_table_dec_8bit[] =
+{
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,		//   0..15
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,		//  16..31
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,		//  32..47
+   52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 254, 255, 255,		//  48..63
+  255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,		//  64..79
+   15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,		//  80..95
+  255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,		//  96..111
+   41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,		// 112..127
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,		// 128..143
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+};
+
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+/* SPECIAL DECODE TABLES FOR LITTLE ENDIAN (INTEL) CPUS */
+
+static const uint32_t base64_table_dec_32bit_d0[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x000000f8, 0xffffffff, 0xffffffff, 0xffffffff, 0x000000fc,
+0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4,
+0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018,
+0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030,
+0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048,
+0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060,
+0x00000064, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078,
+0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090,
+0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8,
+0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0,
+0x000000c4, 0x000000c8, 0x000000cc, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+static const uint32_t base64_table_dec_32bit_d1[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x0000e003, 0xffffffff, 0xffffffff, 0xffffffff, 0x0000f003,
+0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003,
+0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000,
+0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000,
+0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001,
+0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001,
+0x00009001, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001,
+0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002,
+0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002,
+0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003,
+0x00001003, 0x00002003, 0x00003003, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+static const uint32_t base64_table_dec_32bit_d2[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00800f00, 0xffffffff, 0xffffffff, 0xffffffff, 0x00c00f00,
+0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00,
+0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100,
+0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300,
+0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400,
+0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600,
+0x00400600, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700,
+0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900,
+0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00,
+0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00,
+0x00400c00, 0x00800c00, 0x00c00c00, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+static const uint32_t base64_table_dec_32bit_d3[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x003e0000, 0xffffffff, 0xffffffff, 0xffffffff, 0x003f0000,
+0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000,
+0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000,
+0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000,
+0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000,
+0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000,
+0x00190000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000,
+0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000,
+0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000,
+0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000,
+0x00310000, 0x00320000, 0x00330000, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+#elif BYTE_ORDER == BIG_ENDIAN
+
+/* SPECIAL DECODE TABLES FOR BIG ENDIAN (IBM/MOTOROLA/SUN) CPUS */
+
+const uint32_t base64_table_dec_32bit_d0[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xf8000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfc000000,
+0xd0000000, 0xd4000000, 0xd8000000, 0xdc000000, 0xe0000000, 0xe4000000,
+0xe8000000, 0xec000000, 0xf0000000, 0xf4000000, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x04000000, 0x08000000, 0x0c000000, 0x10000000, 0x14000000, 0x18000000,
+0x1c000000, 0x20000000, 0x24000000, 0x28000000, 0x2c000000, 0x30000000,
+0x34000000, 0x38000000, 0x3c000000, 0x40000000, 0x44000000, 0x48000000,
+0x4c000000, 0x50000000, 0x54000000, 0x58000000, 0x5c000000, 0x60000000,
+0x64000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x68000000, 0x6c000000, 0x70000000, 0x74000000, 0x78000000,
+0x7c000000, 0x80000000, 0x84000000, 0x88000000, 0x8c000000, 0x90000000,
+0x94000000, 0x98000000, 0x9c000000, 0xa0000000, 0xa4000000, 0xa8000000,
+0xac000000, 0xb0000000, 0xb4000000, 0xb8000000, 0xbc000000, 0xc0000000,
+0xc4000000, 0xc8000000, 0xcc000000, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+const uint32_t base64_table_dec_32bit_d1[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x03e00000, 0xffffffff, 0xffffffff, 0xffffffff, 0x03f00000,
+0x03400000, 0x03500000, 0x03600000, 0x03700000, 0x03800000, 0x03900000,
+0x03a00000, 0x03b00000, 0x03c00000, 0x03d00000, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00100000, 0x00200000, 0x00300000, 0x00400000, 0x00500000, 0x00600000,
+0x00700000, 0x00800000, 0x00900000, 0x00a00000, 0x00b00000, 0x00c00000,
+0x00d00000, 0x00e00000, 0x00f00000, 0x01000000, 0x01100000, 0x01200000,
+0x01300000, 0x01400000, 0x01500000, 0x01600000, 0x01700000, 0x01800000,
+0x01900000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x01a00000, 0x01b00000, 0x01c00000, 0x01d00000, 0x01e00000,
+0x01f00000, 0x02000000, 0x02100000, 0x02200000, 0x02300000, 0x02400000,
+0x02500000, 0x02600000, 0x02700000, 0x02800000, 0x02900000, 0x02a00000,
+0x02b00000, 0x02c00000, 0x02d00000, 0x02e00000, 0x02f00000, 0x03000000,
+0x03100000, 0x03200000, 0x03300000, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+const uint32_t base64_table_dec_32bit_d2[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x000f8000, 0xffffffff, 0xffffffff, 0xffffffff, 0x000fc000,
+0x000d0000, 0x000d4000, 0x000d8000, 0x000dc000, 0x000e0000, 0x000e4000,
+0x000e8000, 0x000ec000, 0x000f0000, 0x000f4000, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00004000, 0x00008000, 0x0000c000, 0x00010000, 0x00014000, 0x00018000,
+0x0001c000, 0x00020000, 0x00024000, 0x00028000, 0x0002c000, 0x00030000,
+0x00034000, 0x00038000, 0x0003c000, 0x00040000, 0x00044000, 0x00048000,
+0x0004c000, 0x00050000, 0x00054000, 0x00058000, 0x0005c000, 0x00060000,
+0x00064000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00068000, 0x0006c000, 0x00070000, 0x00074000, 0x00078000,
+0x0007c000, 0x00080000, 0x00084000, 0x00088000, 0x0008c000, 0x00090000,
+0x00094000, 0x00098000, 0x0009c000, 0x000a0000, 0x000a4000, 0x000a8000,
+0x000ac000, 0x000b0000, 0x000b4000, 0x000b8000, 0x000bc000, 0x000c0000,
+0x000c4000, 0x000c8000, 0x000cc000, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+const uint32_t base64_table_dec_32bit_d3[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00003e00, 0xffffffff, 0xffffffff, 0xffffffff, 0x00003f00,
+0x00003400, 0x00003500, 0x00003600, 0x00003700, 0x00003800, 0x00003900,
+0x00003a00, 0x00003b00, 0x00003c00, 0x00003d00, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00000100, 0x00000200, 0x00000300, 0x00000400, 0x00000500, 0x00000600,
+0x00000700, 0x00000800, 0x00000900, 0x00000a00, 0x00000b00, 0x00000c00,
+0x00000d00, 0x00000e00, 0x00000f00, 0x00001000, 0x00001100, 0x00001200,
+0x00001300, 0x00001400, 0x00001500, 0x00001600, 0x00001700, 0x00001800,
+0x00001900, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00001a00, 0x00001b00, 0x00001c00, 0x00001d00, 0x00001e00,
+0x00001f00, 0x00002000, 0x00002100, 0x00002200, 0x00002300, 0x00002400,
+0x00002500, 0x00002600, 0x00002700, 0x00002800, 0x00002900, 0x00002a00,
+0x00002b00, 0x00002c00, 0x00002d00, 0x00002e00, 0x00002f00, 0x00003000,
+0x00003100, 0x00003200, 0x00003300, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+#else
+
+# error "byte order unknown"
+
+#endif // LITTLE_ENDIAN
+
+static really_inline int
+dec_loop_generic_32_inner (const uint8_t **s, uint8_t **o, size_t *rounds)
+{
+	const uint32_t str
+		= base64_table_dec_32bit_d0[(*s)[0]]
+		| base64_table_dec_32bit_d1[(*s)[1]]
+		| base64_table_dec_32bit_d2[(*s)[2]]
+		| base64_table_dec_32bit_d3[(*s)[3]];
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+	// LUTs for little-endian set MSB in case of invalid character:
+	if (str & UINT32_C(0x80000000)) {
+		return 0;
+	}
+#else
+	// LUTs for big-endian set LSB in case of invalid character:
+	if (str & UINT32_C(1)) {
+		return 0;
+	}
+#endif
+	// Store the output:
+	memcpy(*o, &str, sizeof (str));
+
+	*s += 4;
+	*o += 3;
+	*rounds -= 1;
+
+	return 1;
+}
+
+static really_inline void
+dec_loop_generic_32 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen)
+{
+	if (*slen < 8) {
+		return;
+	}
+
+	// Process blocks of 4 bytes per round. Because one extra zero byte is
+	// written after the output, ensure that there will be at least 4 bytes
+	// of input data left to cover the gap. (Two data bytes and up to two
+	// end-of-string markers.)
+	size_t rounds = (*slen - 4) / 4;
+
+	*slen -= rounds * 4;	// 4 bytes consumed per round
+	*olen += rounds * 3;	// 3 bytes produced per round
+
+	do {
+		if (rounds >= 8) {
+			if (dec_loop_generic_32_inner(s, o, &rounds) &&
+			    dec_loop_generic_32_inner(s, o, &rounds) &&
+			    dec_loop_generic_32_inner(s, o, &rounds) &&
+			    dec_loop_generic_32_inner(s, o, &rounds) &&
+			    dec_loop_generic_32_inner(s, o, &rounds) &&
+			    dec_loop_generic_32_inner(s, o, &rounds) &&
+			    dec_loop_generic_32_inner(s, o, &rounds) &&
+			    dec_loop_generic_32_inner(s, o, &rounds)) {
+				continue;
+			}
+			break;
+		}
+		if (rounds >= 4) {
+			if (dec_loop_generic_32_inner(s, o, &rounds) &&
+			    dec_loop_generic_32_inner(s, o, &rounds) &&
+			    dec_loop_generic_32_inner(s, o, &rounds) &&
+			    dec_loop_generic_32_inner(s, o, &rounds)) {
+				continue;
+			}
+			break;
+		}
+		if (rounds >= 2) {
+			if (dec_loop_generic_32_inner(s, o, &rounds) &&
+			    dec_loop_generic_32_inner(s, o, &rounds)) {
+				continue;
+			}
+			break;
+		}
+		dec_loop_generic_32_inner(s, o, &rounds);
+		break;
+
+	} while (rounds > 0);
+
+	// Adjust for any rounds that were skipped:
+	*slen += rounds * 4;
+	*olen -= rounds * 3;
+}
+
+nonnull((1,2,4,5))
+static really_inline int base64_stream_decode(
+  struct base64_state *state,
+  const char *src,
+  size_t srclen,
+  uint8_t *out,
+  size_t *outlen)
+{
+  int ret = 0;
+  const uint8_t *s = (const uint8_t *) src;
+  uint8_t *o = (uint8_t *) out;
+  uint8_t q;
+
+  // Use local temporaries to avoid cache thrashing:
+  size_t olen = 0;
+  size_t slen = srclen;
+  struct base64_state st;
+  st.eof = state->eof;
+  st.bytes = state->bytes;
+  st.carry = state->carry;
+
+  // If we previously saw an EOF or an invalid character, bail out:
+  if (st.eof) {
+    *outlen = 0;
+    ret = 0;
+    // If there was a trailing '=' to check, check it:
+    if (slen && (st.eof == BASE64_AEOF)) {
+      state->bytes = 0;
+      state->eof = BASE64_EOF;
+      ret = ((base64_table_dec_8bit[*s++] == 254) && (slen == 1)) ? 1 : 0;
+    }
+    return ret;
+  }
+
+  // Turn four 6-bit numbers into three bytes:
+  // out[0] = 11111122
+  // out[1] = 22223333
+  // out[2] = 33444444
+  
+  // Duff's device again:
+  switch (st.bytes)
+  {
+#if defined(__SUNPRO_C)
+#pragma error_messages(off, E_STATEMENT_NOT_REACHED)
+#endif
+    for (;;)
+#if defined(__SUNPRO_C)
+#pragma error_messages(default, E_STATEMENT_NOT_REACHED)
+#endif
+    {
+    case 0:
+      dec_loop_generic_32(&s, &slen, &o, &olen);
+      if (slen-- == 0) {
+        ret = 1;
+        break;
+      }
+      if ((q = base64_table_dec_8bit[*s++]) >= 254) {
+        st.eof = BASE64_EOF;
+        // Treat character '=' as invalid for byte 0:
+        break;
+      }
+      st.carry = (uint8_t)(q << 2);
+      st.bytes++;
+
+      // fallthrough
+
+    case 1:
+      if (slen-- == 0) {
+        ret = 1;
+        break;
+      }
+      if ((q = base64_table_dec_8bit[*s++]) >= 254) {
+        st.eof = BASE64_EOF;
+        // Treat character '=' as invalid for byte 1:
+        break;
+      }
+      *o++ = st.carry | (q >> 4);
+      st.carry = (uint8_t)(q << 4);
+      st.bytes++;
+      olen++;
+
+  		// fallthrough
+
+    case 2:
+      if (slen-- == 0) {
+        ret = 1;
+        break;
+      }
+      if ((q = base64_table_dec_8bit[*s++]) >= 254) {
+        st.bytes++;
+        // When q == 254, the input char is '='.
+        // Check if next byte is also '=':
+        if (q == 254) {
+          if (slen-- != 0) {
+            st.bytes = 0;
+            // EOF:
+            st.eof = BASE64_EOF;
+            q = base64_table_dec_8bit[*s++];
+            ret = ((q == 254) && (slen == 0)) ? 1 : 0;
+            break;
+          }
+          else {
+            // Almost EOF
+            st.eof = BASE64_AEOF;
+            ret = 1;
+            break;
+          }
+        }
+        // If we get here, there was an error:
+        break;
+      }
+      *o++ = st.carry | (q >> 2);
+      st.carry = (uint8_t)(q << 6);
+      st.bytes++;
+      olen++;
+
+      // fallthrough
+
+    case 3:
+      if (slen-- == 0) {
+        ret = 1;
+        break;
+      }
+      if ((q = base64_table_dec_8bit[*s++]) >= 254) {
+        st.bytes = 0;
+        st.eof = BASE64_EOF;
+        // When q == 254, the input char is '='. Return 1 and EOF.
+        // When q == 255, the input char is invalid. Return 0 and EOF.
+        ret = ((q == 254) && (slen == 0)) ? 1 : 0;
+        break;
+      }
+      *o++ = st.carry | q;
+      st.carry = 0;
+      st.bytes = 0;
+      olen++;
+    }
+  }
+
+  state->eof = st.eof;
+  state->bytes = st.bytes;
+  state->carry = st.carry;
+  *outlen = olen;
+  return ret;
+}
+
+nonnull((1,3,4))
+static really_inline int base64_decode(
+  const char *src,
+  size_t srclen,
+  uint8_t *out,
+  size_t *outlen)
+{
+  struct base64_state state = { .eof = 0, .bytes = 0, .carry = 0 };
+  return base64_stream_decode(&state, src, srclen, out, outlen) & !state.bytes;
+}
+
+nonnull_all
+static really_inline int32_t parse_base64_sequence(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *item,
+  rdata_t *rdata,
+  token_t *token)
+{
+  if (is_contiguous(token)) {
+    struct base64_state state = { .eof = 0, .bytes = 0, .carry = 0 };
+
+    do {
+      size_t length = token->length / 4;
+      if (((uintptr_t)rdata->limit - (uintptr_t)rdata->octets) / 3 < length)
+        SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type));
+      if (!base64_stream_decode(&state, token->data, token->length, rdata->octets, &length))
+        SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type));
+      rdata->octets += length;
+      take(parser, token);
+    } while (is_contiguous(token));
+
+    // incomplete base64 sequence
+    if (state.bytes)
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type));
+  }
+
+  return have_delimiter(parser, type, token);
+}
+
+nonnull_all
+static really_inline int32_t parse_base64(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *item,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  size_t length = token->length / 4;
+  if (((uintptr_t)rdata->limit - (uintptr_t)rdata->octets) / 3 < length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type));
+  if (!base64_decode(token->data, token->length, rdata->octets, &length))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type));
+  rdata->octets += length;
+  return 0;
+}
+
+#endif // BASE64_H
Index: simdzone/src/generic/caa.h
===================================================================
RCS file: simdzone/src/generic/caa.h
diff -N simdzone/src/generic/caa.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/caa.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,81 @@
+/*
+ * caa.h -- CAA (RFC8659) parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef CAA_H
+#define CAA_H
+
+static const uint8_t bad_caa_chars[256] = {
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x00 - 0x07
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x08 - 0x0f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x10 - 0x17
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x18 - 0x10f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x20 - 0x27
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x28 - 0x2f
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x30 - 0x37
+  0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x38 - 0x3f
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x40 - 0x47
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x48 - 0x4f
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 - 0x57
+  0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x58 - 0x5f
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x60 - 0x67
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 - 0x6f
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70 - 0x77
+  0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x78 - 0x7f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x80 - 0x87
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x88 - 0x8f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x90 - 0x97
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x98 - 0x9f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xa0 - 0xa7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xa8 - 0xaf
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xb0 - 0xb7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xb8 - 0xbf
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xc0 - 0xc7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xc8 - 0xcf
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xd0 - 0xd7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xd8 - 0xdf
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xe0 - 0xe7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xe0 - 0xe7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xf8 - 0xff
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xf8 - 0xff
+};
+
+nonnull_all
+static really_inline int32_t parse_caa_tag(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  // RFC8659 section 4.1
+  // https://datatracker.ietf.org/doc/html/rfc8659
+  //
+  // Certification Authority Restriction Properties registered by IANA
+  // https://www.iana.org/assignments/pkix-parameters/pkix-parameters.xhtml
+
+  if (token->length > 255)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  *rdata->octets++ = (uint8_t)token->length;
+
+  uint32_t bad_chars = 0;
+  for (size_t count=0; count < token->length; count++) {
+    const uint8_t octet = (uint8_t)token->data[count];
+    *rdata->octets++ = octet;
+    bad_chars |= bad_caa_chars[octet];
+  }
+
+  // Tags MAY contain ASCII characters "a" through "z", "A" through "Z",
+  // and the numbers 0 through 9. Tags MUST NOT contain any other
+  // characters. Matching of tags is case insensitive.
+  if (bad_chars)
+    SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  return 0;
+}
+
+#endif // CAA_H
Index: simdzone/src/generic/cert.h
===================================================================
RCS file: simdzone/src/generic/cert.h
diff -N simdzone/src/generic/cert.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/cert.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,123 @@
+/*
+ * cert.h
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef CERT_H
+#define CERT_H
+
+// https://www.iana.org/assignments/cert-rr-types/cert-rr-types.xhtml
+
+typedef struct certificate_type certificate_type_t;
+struct certificate_type {
+  struct {
+    char name[8];
+    size_t length;
+  } key;
+  uint16_t value;
+};
+
+#define BAD_CERTIFICATE_TYPE(value) \
+  { { "", 0 }, 0 }
+#define CERTIFICATE_TYPE(name, value) \
+  { { name, sizeof(name) - 1 }, value }
+
+static const certificate_type_t certificate_types[] = {
+  BAD_CERTIFICATE_TYPE(0),
+  CERTIFICATE_TYPE("PKIX", 1),
+  CERTIFICATE_TYPE("SPKI", 2),
+  CERTIFICATE_TYPE("PGP", 3),
+  CERTIFICATE_TYPE("IPKIX", 4),
+  CERTIFICATE_TYPE("ISPKI", 5),
+  CERTIFICATE_TYPE("IPGP", 6),
+  CERTIFICATE_TYPE("ACPKIX", 7),
+  CERTIFICATE_TYPE("IACPKIX", 8),
+  CERTIFICATE_TYPE("URI", 253),
+  CERTIFICATE_TYPE("OID", 254),
+};
+
+static const certificate_type_t *certificate_type_map[16] = {
+  &certificate_types[5],  // ISPKI (0)
+  &certificate_types[0],
+  &certificate_types[0],
+  &certificate_types[0],
+  &certificate_types[0],
+  &certificate_types[0],
+  &certificate_types[10], // OID (6)
+  &certificate_types[0],
+  &certificate_types[3],  // PGP (8)
+  &certificate_types[4],  // IPKIX (9)
+  &certificate_types[2],  // SPKI (10)
+  &certificate_types[1],  // PKIX (11)
+  &certificate_types[8],  // IACPKIX (12)
+  &certificate_types[9],  // URI (13)
+  &certificate_types[6],  // IPGP (14)
+  &certificate_types[7]   // ACPKIX (15)
+};
+
+// magic value generated using certificate-hash.c
+static uint8_t certificate_hash(uint64_t value)
+{
+  value = le64toh(value);
+  uint32_t value32 = (uint32_t)((value >> 32) ^ value);
+  return (uint8_t)((value32 * 98112ull) >> 32) & 0xf;
+}
+
+nonnull_all
+static really_inline int32_t scan_certificate_type(
+  const char *data, size_t length, uint16_t *type)
+{
+  static const int8_t zero_masks[48] = {
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+     0,  0,  0,  0,  0,  0,  0,  0,
+     0,  0,  0,  0,  0,  0,  0,  0
+  };
+
+  if ((uint8_t)*data - '0' > 9) {
+    uint64_t input;
+    memcpy(&input, data, 8);
+    static const uint64_t letter_mask = 0x4040404040404040llu;
+    // convert to upper case
+    input &= ~((input & letter_mask) >> 1);
+    // zero out non-relevant bytes
+    uint64_t zero_mask;
+    memcpy(&zero_mask, &zero_masks[32 - (length & 0xf)], 8);
+    input &= zero_mask;
+    const uint8_t index = certificate_hash(input);
+    assert(index < 16);
+    const certificate_type_t *certificate_type = certificate_type_map[index];
+    uint64_t name;
+    memcpy(&name, certificate_type->key.name, 8);
+    *type = certificate_type->value;
+    return (input == name) &
+      (length == certificate_type->key.length) &
+      (*type != 0);
+  }
+
+  return scan_int16(data, length, type);
+}
+
+nonnull_all
+static really_inline int32_t parse_certificate_type(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  uint16_t cert;
+  if (!scan_certificate_type(token->data, token->length, &cert))
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  cert = htobe16(cert);
+  memcpy(rdata->octets, &cert, 2);
+  rdata->octets += 2;
+  return 0;
+}
+
+#endif // CERT_H
Index: simdzone/src/generic/endian.h
===================================================================
RCS file: simdzone/src/generic/endian.h
diff -N simdzone/src/generic/endian.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/endian.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,226 @@
+/*
+ * endian.h -- byte order abstractions
+ *
+ * Copyright (c) 2023, NLnet Labs.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef ENDIAN_H
+#define ENDIAN_H
+
+#include "config.h"
+
+// https://www.austingroupbugs.net/view.php?id=162#c665
+
+#if _WIN32
+#include <stdlib.h>
+
+#define LITTLE_ENDIAN 1234
+#define BIG_ENDIAN 4321
+#define BYTE_ORDER LITTLE_ENDIAN
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define htobe16(x) _byteswap_ushort(x)
+#define htobe32(x) _byteswap_ulong(x)
+#define htobe64(x) _byteswap_uint64(x)
+#define htole16(x) (x)
+#define htole32(x) (x)
+#define htole64(x) (x)
+
+#define be16toh(x) _byteswap_ushort(x)
+#define be32toh(x) _byteswap_ulong(x)
+#define be64toh(x) _byteswap_uint64(x)
+#define le16toh(x) (x)
+#define le32toh(x) (x)
+#define le64toh(x) (x)
+#else
+#define htobe16(x) (x)
+#define htobe32(x) (x)
+#define htobe64(x) (x)
+#define htole16(x) _byteswap_ushort(x)
+#define htole32(x) _byteswap_ulong(x)
+#define htole64(x) _byteswap_uint64(x)
+
+#define be16toh(x) (x)
+#define be32toh(x) (x)
+#define be64toh(x) (x)
+#define le16toh(x) _byteswap_ushort(x)
+#define le32toh(x) _byteswap_ulong(x)
+#define le64toh(x) _byteswap_uint64(x)
+#endif
+
+#elif __APPLE__
+#include <libkern/OSByteOrder.h>
+
+#if !defined BYTE_ORDER
+# define BYTE_ORDER __BYTE_ORDER__
+#endif
+#if !defined LITTLE_ENDIAN
+# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
+#endif
+#if !defined BIG_ENDIAN
+# define BIG_ENDIAN __ORDER_BIG_ENDIAN__
+#endif
+
+#define htobe16(x) OSSwapHostToBigInt16(x)
+#define htobe32(x) OSSwapHostToBigInt32(x)
+#define htobe64(x) OSSwapHostToBigInt64(x)
+#define htole16(x) OSSwapHostToLittleInt16(x)
+#define htole32(x) OSSwapHostToLittleInt32(x)
+#define htole64(x) OSSwapHostToLittleInt64(x)
+
+#define be16toh(x) OSSwapBigToHostInt16(x)
+#define be32toh(x) OSSwapBigToHostInt32(x)
+#define be64toh(x) OSSwapBigToHostInt64(x)
+#define le16toh(x) OSSwapLittleToHostInt16(x)
+#define le32toh(x) OSSwapLittleToHostInt32(x)
+#define le64toh(x) OSSwapLittleToHostInt64(x)
+
+#else
+#if HAVE_ENDIAN_H
+#include <endian.h>
+#elif defined(__OpenBSD__)
+// endian.h was added in OpenBSD 5.6. machine/endian.h exports optimized
+// bswap routines for use in sys/endian.h, which it includes.
+#include <machine/endian.h>
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
+#include <sys/endian.h>
+#endif
+
+#if defined(__NetBSD__)
+/* Bring bswap{16,32,64} into scope: */
+#include <sys/types.h>
+#include <machine/bswap.h>
+#endif
+
+#if !defined(LITTLE_ENDIAN)
+# if defined(__ORDER_LITTLE_ENDIAN__)
+#   define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
+# else
+#   define LITTLE_ENDIAN 1234
+# endif
+#endif
+
+#if !defined(BIG_ENDIAN)
+# if defined(__ORDER_BIG_ENDIAN__)
+#   define BIG_ENDIAN __ORDER_BIG_ENDIAN__
+# else
+#   define BIG_ENDIAN 4321
+# endif
+#endif
+
+#if !defined(BYTE_ORDER)
+# if defined(__BYTE_ORDER__)
+#   define BYTE_ORDER __BYTE_ORDER__
+# elif defined(__BYTE_ORDER)
+#   define BYTE_ORDER __BYTE_ORDER
+# elif defined(i386) || defined(__i386__) || defined(__i486__) || \
+       defined(__i586__) || defined(__i686__) || \
+       defined(__x86) || defined(__x86_64) || defined(__x86_64__) || \
+       defined(__amd64) || defined(__amd64__)
+#   define BYTE_ORDER LITTLE_ENDIAN
+# elif defined(sparc) || defined(__sparc) || defined(__sparc__) || \
+       defined(POWERPC) || defined(mc68000) || defined(sel)
+#   define BYTE_ORDER BIG_ENDIAN
+# else
+#   error "missing definition of BYTE_ORDER"
+# endif
+#endif
+
+#if !defined(__NetBSD__)
+
+#if !HAVE_DECL_BSWAP16
+static really_inline uint16_t bswap16(uint16_t x)
+{
+  // Copied from src/common/lib/libc/gen/bswap16.c in NetBSD
+  // Written by Manuel Bouyer <bouyer@NetBSD.org>.
+  // Public domain.
+  return ((x << 8) & 0xff00) | ((x >> 8) & 0x00ff);
+}
+#endif
+
+#if !HAVE_DECL_BSWAP32
+static really_inline uint32_t bswap32(uint32_t x)
+{
+  // Copied from src/common/lib/libc/gen/bswap32.c in NetBSD
+  // Written by Manuel Bouyer <bouyer@NetBSD.org>.
+  // Public domain.
+  return ( (x << 24) & 0xff000000 ) |
+         ( (x <<  8) & 0x00ff0000 ) |
+         ( (x >>  8) & 0x0000ff00 ) |
+         ( (x >> 24) & 0x000000ff );
+}
+#endif
+
+#if !HAVE_DECL_BSWAP64
+static really_inline uint64_t bswap64(uint64_t x)
+{
+  // Copied from src/common/lib/libc/gen/bswap64.c in NetBSD
+  // Written by Manuel Bouyer <bouyer@NetBSD.org>.
+  // Public domain.
+  return ( (x << 56) & 0xff00000000000000ull ) |
+         ( (x << 40) & 0x00ff000000000000ull ) |
+         ( (x << 24) & 0x0000ff0000000000ull ) |
+         ( (x <<  8) & 0x000000ff00000000ull ) |
+         ( (x >>  8) & 0x00000000ff000000ull ) |
+         ( (x >> 24) & 0x0000000000ff0000ull ) |
+         ( (x >> 40) & 0x000000000000ff00ull ) |
+         ( (x >> 56) & 0x00000000000000ffull );
+}
+#endif
+
+#endif /* !defined(__NetBSD__) */
+
+# if BYTE_ORDER == LITTLE_ENDIAN
+#   define htobe(bits, x) bswap ## bits((x))
+#   define htole(bits, x) (x)
+#   define betoh(bits, x) bswap ## bits((x))
+#   define letoh(bits, x) (x)
+# else
+#   define htobe(bits, x) (x)
+#   define htole(bits, x) bswap ## bits((x))
+#   define betoh(bits, x) (x)
+#   define letoh(bits, x) bswap ## bits((x))
+# endif
+
+# if !defined htobe16
+#   define htobe16(x) htobe(16,(x))
+# endif
+# if !defined htobe32
+#   define htobe32(x) htobe(32,(x))
+# endif
+# if !defined htobe64
+#   define htobe64(x) htobe(64,(x))
+# endif
+# if !defined htole16
+#   define htole16(x) htole(16,(x))
+# endif
+# if !defined htole32
+#   define htole32(x) htole(32,(x))
+# endif
+# if !defined htole64
+#   define htole64(x) htole(64,(x))
+# endif
+
+# if !defined be16toh
+#   define be16toh(x) betoh(16,(x))
+# endif
+# if !defined be32toh
+#   define be32toh(x) betoh(32,(x))
+# endif
+# if !defined be64toh
+#   define be64toh(x) betoh(64,(x))
+# endif
+# if !defined le16toh
+#   define le16toh(x) letoh(16,(x))
+# endif
+# if !defined le32toh
+#   define le32toh(x) letoh(32,(x))
+# endif
+# if !defined le64toh
+#   define le64toh(x) letoh(64,(x))
+# endif
+#endif
+
+#endif // ENDIAN_H
Index: simdzone/src/generic/eui.h
===================================================================
RCS file: simdzone/src/generic/eui.h
diff -N simdzone/src/generic/eui.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/eui.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,67 @@
+/*
+ * eui.h -- EUI-48 and EUI-64 (RFC7043) parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef EUI_H
+#define EUI_H
+
+nonnull((1,2))
+static really_inline bool
+eui_base16_dec_loop_generic_32_inner(const uint8_t *s, uint8_t *o, bool last)
+{
+  const uint32_t val1 = base16_table_dec_32bit_d0[s[0]]
+                      | base16_table_dec_32bit_d1[s[1]];
+  const uint32_t val2 = base16_table_dec_32bit_d0[s[3]]
+                      | base16_table_dec_32bit_d1[s[4]];
+
+  if (val1 > 0xff || val2 > 0xff || s[2] != '-' || (!last && s[5] != '-'))
+    return false;
+
+  o[0] = (uint8_t)val1;
+  o[1] = (uint8_t)val2;
+
+  return true;
+}
+
+// RFC7043 section 3.2, require xx-xx-xx-xx-xx-xx
+nonnull_all
+static really_inline int32_t parse_eui48(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const uint8_t *input = (const uint8_t *)token->data;
+  if (token->length == 17 &&
+      eui_base16_dec_loop_generic_32_inner(input, rdata->octets, false) &&
+      eui_base16_dec_loop_generic_32_inner(input+6, rdata->octets+2, false) &&
+      eui_base16_dec_loop_generic_32_inner(input+12, rdata->octets+4, true))
+    return (void)(rdata->octets += 6), 0;
+  SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+}
+
+// RFC7043 section 4.2, require xx-xx-xx-xx-xx-xx-xx-xx
+nonnull_all
+static really_inline int32_t parse_eui64(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const uint8_t *input = (const uint8_t *)token->data;
+  if (token->length == 23 &&
+      eui_base16_dec_loop_generic_32_inner(input, rdata->octets, false) &&
+      eui_base16_dec_loop_generic_32_inner(input+6, rdata->octets+2, false) &&
+      eui_base16_dec_loop_generic_32_inner(input+12, rdata->octets+4, false) &&
+      eui_base16_dec_loop_generic_32_inner(input+18, rdata->octets+6, true))
+    return (void)(rdata->octets += 8), 0;
+  SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+}
+
+#endif // EUI_H
Index: simdzone/src/generic/format.h
===================================================================
RCS file: simdzone/src/generic/format.h
diff -N simdzone/src/generic/format.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/format.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,419 @@
+/*
+ * format.h
+ *
+ * Copyright (c) 2022, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef FORMAT_H
+#define FORMAT_H
+
+#define FIELDS(fields) \
+  { (sizeof(fields)/sizeof(fields[0])), fields }
+
+#define FIELD(name) \
+  { { { name, sizeof(name) - 1 } } }
+
+#define ENTRY(name, fields) \
+  { { { name, sizeof(name) - 1 }, 0 }, 0, false, false, fields, 0, 0 }
+
+nonnull_all
+static really_inline int32_t parse_type(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  uint16_t code;
+  const mnemonic_t *mnemonic;
+
+  if (scan_type(token->data, token->length, &code, &mnemonic) != 1)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  code = htobe16(code);
+  memcpy(rdata->octets, &code, 2);
+  rdata->octets += 2;
+  return 0;
+}
+
+nonnull_all
+static really_inline int32_t parse_name(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  size_t length = 0;
+
+  assert(is_contiguous(token));
+
+  // a freestanding "@" denotes the current origin
+  if (unlikely(token->length == 1 && token->data[0] == '@'))
+    goto relative;
+  switch (scan_name(token->data, token->length, rdata->octets, &length)) {
+    case 0:
+      rdata->octets += length;
+      return 0;
+    case 1:
+      goto relative;
+  }
+
+  SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+relative:
+  if (length > 255 - parser->file->origin.length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  memcpy(rdata->octets + length, parser->file->origin.octets, parser->file->origin.length);
+  rdata->octets += length + parser->file->origin.length;
+  return 0;
+}
+
+nonnull_all
+static really_inline int32_t parse_owner(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  const token_t *token)
+{
+  size_t length = 0;
+  uint8_t *octets = parser->file->owner.octets;
+
+  assert(is_contiguous(token));
+
+  // a freestanding "@" denotes the origin
+  if (unlikely(token->length == 1 && token->data[0] == '@'))
+    goto relative;
+  switch (scan_name(token->data, token->length, octets, &length)) {
+    case 0:
+      parser->file->owner.length = length;
+      parser->owner = &parser->file->owner;
+      return 0;
+    case 1:
+      goto relative;
+  }
+
+  SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+relative:
+  if (length > 255 - parser->file->origin.length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  memcpy(octets+length, parser->file->origin.octets, parser->file->origin.length);
+  parser->file->owner.length = length + parser->file->origin.length;
+  parser->owner = &parser->file->owner;
+  return 0;
+}
+
+nonnull_all
+static really_inline int32_t parse_string(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  if (rdata->limit == rdata->octets)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(type), NAME(field));
+  assert(rdata->limit > rdata->octets);
+
+  int32_t length;
+  uint8_t *octets = rdata->octets + 1;
+  const uint8_t *limit = rdata->limit;
+
+  if (rdata->limit - rdata->octets > (1 + 255))
+    limit = rdata->octets + 1 + 255;
+  if ((length = scan_string(token->data, token->length, octets, limit)) == -1)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(type), NAME(field));
+  *rdata->octets = (uint8_t)length;
+  rdata->octets += 1u + (uint32_t)length;
+  return 0;
+}
+
+nonnull_all
+static really_inline int32_t parse_text(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  int32_t length;
+
+  if ((length = scan_string(token->data, token->length, rdata->octets, rdata->limit)) == -1)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(type), NAME(field));
+  rdata->octets += (uint32_t)length;
+  return 0;
+}
+
+nonnull_all
+static really_inline int32_t parse_rr(
+  parser_t *parser, token_t *token)
+{
+  static const rdata_info_t fields[] = {
+    FIELD("OWNER"),
+    FIELD("TYPE"),
+    FIELD("CLASS"),
+    FIELD("TTL")
+  };
+
+  static const type_info_t rr = ENTRY("RR", FIELDS(fields));
+
+  int32_t code;
+  const type_info_t *descriptor;
+  const mnemonic_t *mnemonic;
+  rdata_t rdata = { parser->rdata->octets, parser->rdata->octets + 65535 };
+
+  parser->file->ttl = parser->file->default_ttl;
+
+  if ((uint8_t)token->data[0] - '0' < 10) {
+    parser->file->ttl = &parser->file->last_ttl;
+    if (!scan_ttl(token->data, token->length, parser->options.pretty_ttls, &parser->file->last_ttl))
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(&rr));
+    if (parser->file->last_ttl & (1u << 31))
+      SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(&rr));
+    goto class_or_type;
+  } else {
+    switch (scan_type_or_class(token->data, token->length, &parser->file->last_type, &mnemonic)) {
+      case 1:
+        goto rdata;
+      case 2:
+        parser->file->last_class = parser->file->last_type;
+        goto ttl_or_type;
+      default:
+        SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(&rr));
+    }
+  }
+
+ttl_or_type:
+  if ((code = take_contiguous(parser, &rr, &fields[1], token)) < 0)
+    return code;
+  if ((uint8_t)token->data[0] - '0' < 10) {
+    parser->file->ttl = &parser->file->last_ttl;
+    if (!scan_ttl(token->data, token->length, parser->options.pretty_ttls, &parser->file->last_ttl))
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(&rr));
+    if (parser->file->last_ttl & (1u << 31))
+      SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(&rr));
+    goto type;
+  } else {
+    if (unlikely(scan_type(token->data, token->length, &parser->file->last_type, &mnemonic) != 1))
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(&rr));
+    goto rdata;
+  }
+
+class_or_type:
+  if ((code = take_contiguous(parser, &rr, &fields[1], token)) < 0)
+    return code;
+  switch (scan_type_or_class(token->data, token->length, &parser->file->last_type, &mnemonic)) {
+    case 1:
+      goto rdata;
+    case 2:
+      parser->file->last_class = parser->file->last_type;
+      goto type;
+    default:
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), NAME(&rr));
+  }
+
+type:
+  if ((code = take_contiguous(parser, &rr, &fields[1], token)) < 0)
+    return code;
+  if (unlikely(scan_type(token->data, token->length, &parser->file->last_type, &mnemonic) != 1))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(&rr));
+
+rdata:
+  descriptor = (const type_info_t *)mnemonic;
+
+  // RFC3597
+  // parse generic rdata if rdata starts with "\\#"
+  take(parser, token);
+  if (likely(token->data[0] != '\\'))
+    return descriptor->parse(parser, descriptor, &rdata, token);
+  else if (is_contiguous(token) && strncmp(token->data, "\\#", token->length) == 0)
+    return parse_generic_rdata(parser, descriptor, &rdata, token);
+  else
+    return descriptor->parse(parser, descriptor, &rdata, token);
+}
+
+// RFC1035 section 5.1
+// $INCLUDE <file-name> [<domain-name>] [<comment>]
+nonnull_all
+static really_inline int32_t parse_dollar_include(
+  parser_t *parser, token_t *token)
+{
+  static const rdata_info_t fields[] = {
+    FIELD("file-name"),
+    FIELD("domain-name")
+  };
+
+  static const type_info_t include = ENTRY("$INCLUDE", FIELDS(fields));
+
+  if (parser->options.no_includes)
+    NOT_PERMITTED(parser, "%s is disabled", NAME(&include));
+
+  int32_t code;
+  file_t *file;
+  if ((code = take_quoted_or_contiguous(parser, &include, &fields[0], token)) < 0)
+    return code;
+  if ((code = zone_open_file(parser, token->data, token->length, &file)) < 0)
+    return code;
+
+  name_buffer_t name;
+  const name_buffer_t *origin = &parser->file->origin;
+
+  // $INCLUDE directive MAY specify an origin
+  take(parser, token);
+  if (is_contiguous(token)) {
+    if (scan_name(token->data, token->length, name.octets, &name.length) != 0) {
+      zone_close_file(parser, file);
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(&include));
+    }
+    origin = &name;
+    take(parser, token);
+  }
+
+  // store the current owner to restore later if necessary
+  file_t *includer;
+  includer = parser->file;
+  includer->owner = *parser->owner;
+  file->includer = includer;
+  file->owner = *origin;
+  file->origin = *origin;
+  file->last_type = 0;
+  file->last_class = includer->last_class;
+  file->last_ttl = includer->last_ttl;
+  file->line = 1;
+
+  if (!is_delimiter(token)) {
+    zone_close_file(parser, file);
+    return have_delimiter(parser, &include, token);
+  }
+
+  // check for recursive includes
+  for (uint32_t depth = 1; includer; depth++, includer = includer->includer) {
+    if (strcmp(includer->path, file->path) == 0) {
+      zone_error(parser, "Circular include in %s", file->name);
+      zone_close_file(parser, file);
+      return ZONE_SEMANTIC_ERROR;
+    }
+    if (depth > parser->options.include_limit) {
+      zone_error(parser, "Include %s nested too deeply", file->name);
+      zone_close_file(parser, file);
+      return ZONE_SEMANTIC_ERROR;
+    }
+  }
+
+  // signal $INCLUDE to application
+  if (parser->options.include.callback) {
+    code = parser->options.include.callback(
+      parser, file->name, file->path, parser->user_data);
+    if (code) {
+      zone_close_file(parser, file);
+      return code;
+    }
+  }
+
+  adjust_line_count(parser->file);
+  parser->file = file;
+  return 0;
+}
+
+// RFC1035 section 5.1
+// $ORIGIN <domain-name> [<comment>]
+nonnull_all
+static inline int32_t parse_dollar_origin(
+  parser_t *parser, token_t *token)
+{
+  static const rdata_info_t fields[] = { FIELD("name") };
+  static const type_info_t origin = ENTRY("$ORIGIN", FIELDS(fields));
+  int32_t code;
+
+  if ((code = take_contiguous_or_quoted(parser, &origin, &fields[0], token)) < 0)
+    return code;
+  if (scan_name(token->data, token->length, parser->file->origin.octets, &parser->file->origin.length) != 0)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), NAME(&origin));
+  if ((code = take_delimiter(parser, &origin, token)) < 0)
+    return code;
+
+  adjust_line_count(parser->file);
+  return code;
+}
+
+// RFC2308 section 4
+// $TTL <TTL> [<comment>]
+nonnull_all
+static really_inline int32_t parse_dollar_ttl(
+  parser_t *parser, token_t *token)
+{
+  static const rdata_info_t fields[] = { FIELD("ttl") };
+  static const type_info_t ttl = ENTRY("$TTL", FIELDS(fields));
+  int32_t code;
+
+  if ((code = take_contiguous(parser, &ttl, &fields[0], token)) < 0)
+    return code;
+  if (!scan_ttl(token->data, token->length, parser->options.pretty_ttls, &parser->file->dollar_ttl))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), NAME(&ttl));
+  if (parser->file->dollar_ttl & (1u << 31))
+    SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), NAME(&ttl));
+  if ((code = take_delimiter(parser, &ttl, token)) < 0)
+    return code;
+
+  parser->file->ttl = parser->file->default_ttl = &parser->file->dollar_ttl;
+  adjust_line_count(parser->file);
+  return 0;
+}
+
+static inline int32_t parse(parser_t *parser)
+{
+  static const rdata_info_t fields[] = { FIELD("OWNER") };
+  static const type_info_t rr = ENTRY("RR", FIELDS(fields));
+
+  int32_t code = 0;
+  token_t token;
+
+  while (code >= 0) {
+    take(parser, &token);
+    if (likely(is_contiguous(&token))) {
+      if (likely(parser->file->start_of_line)) {
+        // control entry
+        if (unlikely(token.data[0] == '$')) {
+          if (token.length == 4 && memcmp(token.data, "$TTL", 4) == 0)
+            code = parse_dollar_ttl(parser, &token);
+          else if (token.length == 7 && memcmp(token.data, "$ORIGIN", 7) == 0)
+            code = parse_dollar_origin(parser, &token);
+          else if (token.length == 8 && memcmp(token.data, "$INCLUDE", 8) == 0)
+            code = parse_dollar_include(parser, &token);
+          else
+            SYNTAX_ERROR(parser, "Unknown control entry");
+          continue;
+        }
+
+        if ((code = parse_owner(parser, &rr, &fields[0], &token)) < 0)
+          return code;
+        if ((code = take_contiguous(parser, &rr, &fields[0], &token)) < 0)
+          return code;
+      } else if (unlikely(!parser->owner->length)) {
+        SYNTAX_ERROR(parser, "No last stated owner");
+      }
+
+      code = parse_rr(parser, &token);
+    } else if (is_end_of_file(&token)) {
+      if (parser->file->end_of_file == NO_MORE_DATA) {
+        if (!parser->file->includer)
+          break;
+        file_t *file = parser->file;
+        parser->file = parser->file->includer;
+        parser->owner = &parser->file->owner;
+        zone_close_file(parser, file);
+      }
+    } else if (is_line_feed(&token)) {
+      assert(token.code == LINE_FEED);
+      adjust_line_count(parser->file);
+    } else {
+      code = have_contiguous(parser, &rr, &fields[0], &token);
+    }
+  }
+
+  return code;
+}
+
+#endif // FORMAT_H
Index: simdzone/src/generic/gpos.h
===================================================================
RCS file: simdzone/src/generic/gpos.h
diff -N simdzone/src/generic/gpos.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/gpos.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,194 @@
+/*
+ * gpos.h -- Geographical Location (RFC1712) parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef GPOS_H
+#define GPOS_H
+
+nonnull_all
+static really_inline int32_t parse_latitude(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const char *text = token->data + (token->data[0] == '-');
+  uint32_t degrees;
+  uint8_t digits[4];
+  digits[0] = (uint8_t)text[0] - '0';
+  digits[1] = (uint8_t)text[1] - '0';
+  digits[2] = (uint8_t)text[2] - '0';
+  digits[3] = (uint8_t)text[3] - '0';
+
+  int32_t mask = ((digits[0] <= 9) << 0) | // 0b0001
+                 ((digits[1] <= 9) << 1) | // 0b0010
+                 ((digits[2] <= 9) << 2) | // 0b0100
+                 ((digits[3] <= 9) << 3);  // 0b1000
+
+  if (token->length > 255)
+    goto bad_latitude;
+
+  switch (mask) {
+    case 0x01: // 0b0001 ("d...")
+    case 0x09: // 0b1001 ("d..d")
+      text += 1;
+      break;
+    case 0x03: // 0b0011 ("dd..")
+      // ensure no leading zero and range is between -90 and 90
+      degrees = digits[0] * 10 + digits[1];
+      if (degrees < 10 || degrees > 90)
+        goto bad_latitude;
+      text += 2;
+      break;
+    case 0x05: // 0b1010 ("d.d.")
+    case 0x0d: // 0b1011 ("d.dd")
+      if (text[1] != '.')
+        text += 1;
+      else
+        for (text += 2; 10u > (uint8_t)((uint8_t)text[0] - '0'); text++) ;
+      break;
+    case 0x0b: // 0b1011 ("dd.d")
+      if (text[2] != '.')
+        text += 2;
+      else
+        for (text += 3; 10u > (uint8_t)((uint8_t)text[0] - '0'); text++) ;
+      break;
+    default:
+      goto bad_latitude;
+  }
+
+  if (text != token->data + token->length)
+    goto bad_latitude;
+
+  *rdata->octets = (uint8_t)token->length;
+  memcpy(rdata->octets + 1, token->data, token->length);
+  rdata->octets += 1 + token->length;
+  return 0;
+bad_latitude:
+  SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+}
+
+nonnull_all
+static really_inline int32_t parse_longitude(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const char *text = token->data + (token->data[0] == '-');
+  uint32_t degrees;
+  uint8_t digits[5];
+  digits[0] = (uint8_t)text[0] - '0';
+  digits[1] = (uint8_t)text[1] - '0';
+  digits[2] = (uint8_t)text[2] - '0';
+  digits[3] = (uint8_t)text[3] - '0';
+  digits[4] = (uint8_t)text[4] - '0';
+
+  int32_t mask = ((digits[0] <= 9) << 0) | // 0b00001
+                 ((digits[1] <= 9) << 1) | // 0b00010
+                 ((digits[2] <= 9) << 2) | // 0b00100
+                 ((digits[3] <= 9) << 3) | // 0b01000
+                 ((digits[4] <= 9) << 4);  // 0b10000
+
+  if (token->length > 255)
+    goto bad_longitude;
+
+  switch (mask) {
+    case 0x01: // 0b00001 ("d....")
+    case 0x09: // 0b01001 ("d..d.")
+    case 0x19: // 0b11001 ("d..dd")
+      text += 1;
+       break;
+    case 0x03: // 0b00011 ("dd...")
+    case 0x13: // 0b10011 ("dd..d")
+      degrees = digits[0] * 10 + digits[1];
+      // ensure no leading zero
+      if (degrees < 10)
+        goto bad_longitude;
+      text += 2;
+      break;
+    case 0x07: // 0b00111 ("ddd..")
+      // ensure no leading zero and range is between -180 and 180
+      degrees = digits[0] * 100 + digits[1] * 10 + digits[2];
+      if (degrees < 100 || degrees > 180)
+        goto bad_longitude;
+      text += 3;
+      break;
+    case 0x05: // 0b00101 ("d.d..")
+    case 0x0d: // 0b01101 ("d.dd.")
+    case 0x1d: // 0b11101 ("d.ddd")
+      if (text[1] != '.')
+        text += 1;
+      else
+        for (text += 2; (uint8_t)((uint8_t)text[0] - '0') <= 9u; text++) ;
+      break;
+    case 0x0b: // 0b01011 ("dd.d.")
+    case 0x1b: // 0b11011 ("dd.dd")
+      // ensure no leading zero
+      degrees = digits[0] * 10 + digits[1];
+      if (degrees < 10)
+        goto bad_longitude;
+      if (text[2] != '.')
+        text += 2;
+      else
+        for (text += 3; (uint8_t)((uint8_t)text[0] - '0') <= 9u; text++) ;
+      break;
+    case 0x17: // 0b10111 ("ddd.d")
+      // ensure no leading zero and range is between -180 and 180
+      degrees = digits[0] * 100 + digits[1] * 10 + digits[2];
+      if (degrees < 100 || degrees > 180)
+        goto bad_longitude;
+      if (text[3] != '.')
+        text += 3;
+      else
+        for (text += 4; (uint8_t)((uint8_t)text[0] - '0') <= 9u; text++) ;
+      break;
+    default:
+      goto bad_longitude;
+  }
+
+  if (text != token->data + token->length)
+    goto bad_longitude;
+
+  *rdata->octets = (uint8_t)token->length;
+  memcpy(rdata->octets + 1, token->data, token->length);
+  rdata->octets += 1 + token->length;
+  return 0;
+bad_longitude:
+  SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+}
+
+nonnull_all
+static really_inline int32_t parse_altitude(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const char *text = token->data;
+
+  if (token->length > 255)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  for (; (uint8_t)((uint8_t)*text - '0') <= 9u; text++) ;
+
+  if (*text == '.')
+    for (text++; (uint8_t)((uint8_t)*text - '0') <= 9u; text++) ;
+
+  if (text != token->data + token->length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  *rdata->octets = (uint8_t)token->length;
+  memcpy(rdata->octets + 1, token->data, token->length);
+  rdata->octets += 1 + token->length;
+  return 0;
+}
+
+#endif // GPOS_H
Index: simdzone/src/generic/ilnp64.h
===================================================================
RCS file: simdzone/src/generic/ilnp64.h
diff -N simdzone/src/generic/ilnp64.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/ilnp64.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,58 @@
+/*
+ * ilnp64.h -- 64-bit Locator (RFC6742 section 2.3) parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef ILNP64_H
+#define ILNP64_H
+
+// FIXME: very likely eligable for vectorization (or optimization even), but
+//        gains are small as the type is not frequently used
+nonnull_all
+static really_inline int32_t parse_ilnp64(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  uint16_t a[4] = { 0, 0, 0, 0 };
+  size_t n = 0;
+  const char *p = token->data, *g = p;
+  for (;;) {
+    const uint8_t c = (uint8_t)*p;
+    if (c == ':') {
+      if (n == 3 || p == g || p - g > 4)
+        break;
+      g = p += 1;
+      n += 1;
+    } else {
+      uint16_t x;
+      if (c >= '0' && c <= '9')
+        x = c - '0';
+      else if (c >= 'A' && c <= 'F')
+        x = c - ('A' - 10);
+      else if (c >= 'a' && c <= 'f')
+        x = c - ('a' - 10);
+      else
+        break;
+      a[n] = (uint16_t)(a[n] << 4u) + x;
+      p += 1;
+    }
+  }
+
+  if (n != 3 || p == g || p - g > 4 || classify[(uint8_t)*p] == CONTIGUOUS)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  a[0] = htobe16(a[0]);
+  a[1] = htobe16(a[1]);
+  a[2] = htobe16(a[2]);
+  a[3] = htobe16(a[3]);
+  memcpy(rdata->octets, a, 8);
+  rdata->octets += 8;
+  return 0;
+}
+
+#endif // ILNP64_H
Index: simdzone/src/generic/ip4.h
===================================================================
RCS file: simdzone/src/generic/ip4.h
diff -N simdzone/src/generic/ip4.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/ip4.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,60 @@
+/*
+ * ip4.h -- fallback parser for IPv4 addresses
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef IP4_H
+#define IP4_H
+
+nonnull_all
+static really_inline int32_t scan_ip4(const char *text, uint8_t *wire)
+{
+  const char *start = text;
+  uint32_t round = 0;
+  for (;;) {
+    uint8_t digits[3], count;
+    uint32_t octet;
+    digits[0] = (uint8_t)text[0] - '0';
+    digits[1] = (uint8_t)text[1] - '0';
+    digits[2] = (uint8_t)text[2] - '0';
+    if (digits[0] > 9)
+      return 0;
+    else if (digits[1] > 9)
+      (void)(count = 1), octet = digits[0];
+    else if (digits[2] > 9)
+      (void)(count = 2), octet = digits[0] * 10 + digits[1];
+    else
+      (void)(count = 3), octet = digits[0] * 100 + digits[1] * 10 + digits[2];
+
+    if (octet > 255 || (count > 1 && !digits[0]))
+      return 0;
+    text += count;
+    wire[round++] = (uint8_t)octet;
+    if (text[0] != '.' || round == 4)
+      break;
+    text += 1;
+  }
+
+  if (round != 4)
+    return 0;
+  return (int32_t)(text - start);
+}
+
+nonnull_all
+static really_inline int32_t parse_ip4(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *item,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  if ((size_t)scan_ip4(token->data, rdata->octets) != token->length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type));
+  rdata->octets += 4;
+  return 0;
+}
+
+#endif // IP4_H
Index: simdzone/src/generic/ip6.h
===================================================================
RCS file: simdzone/src/generic/ip6.h
diff -N simdzone/src/generic/ip6.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/ip6.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,203 @@
+/*-
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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 IP6_H
+#define IP6_H
+
+#ifndef NS_INT16SZ
+#define NS_INT16SZ  2
+#endif
+
+#ifndef NS_IN6ADDRSZ
+#define NS_IN6ADDRSZ 16
+#endif
+
+#ifndef NS_INADDRSZ
+#define NS_INADDRSZ 4
+#endif
+
+/* int
+ * inet_pton4(src, dst)
+ *  like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ *  length if `src' is a valid dotted quad, else 0.
+ * notice:
+ *  does not touch `dst' unless it's returning !0.
+ * author:
+ *  Paul Vixie, 1996.
+ */
+static int
+inet_pton4(const char *src, uint8_t *dst)
+{
+  static const char digits[] = "0123456789";
+  int saw_digit, octets, ch;
+  uint8_t tmp[NS_INADDRSZ], *tp;
+  const char *start = src;
+
+  saw_digit = 0;
+  octets = 0;
+  *(tp = tmp) = 0;
+  for (; (ch = *src); src++) {
+    const char *pch;
+
+    if ((pch = strchr(digits, ch)) != NULL) {
+      uint32_t new = *tp * 10 + (uint32_t)(pch - digits);
+
+      if (new > 255)
+        return (0);
+      *tp = (uint8_t)new;
+      if (! saw_digit) {
+        if (++octets > 4)
+          return (0);
+        saw_digit = 1;
+      }
+    } else if (ch == '.' && saw_digit) {
+      if (octets == 4)
+        return (0);
+      *++tp = 0;
+      saw_digit = 0;
+    } else
+      break;
+  }
+  if (octets < 4)
+    return (0);
+
+  memcpy(dst, tmp, NS_INADDRSZ);
+  return (int)(src - start);
+}
+
+/* int
+ * inet_pton6(src, dst)
+ *  convert presentation level address to network order binary form.
+ * return:
+ *  length if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ *  (1) does not touch `dst' unless it's returning !0.
+ *  (2) :: in a full address is silently ignored.
+ * credit:
+ *  inspired by Mark Andrews.
+ * author:
+ *  Paul Vixie, 1996.
+ */
+static int
+inet_pton6(const char *src, uint8_t *dst)
+{
+  static const char xdigits_l[] = "0123456789abcdef",
+        xdigits_u[] = "0123456789ABCDEF";
+  uint8_t tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
+  const char *xdigits, *curtok;
+  int ch, saw_xdigit;
+  uint32_t val;
+  int len;
+  const char *start = src;
+
+  memset((tp = tmp), '\0', NS_IN6ADDRSZ);
+  endp = tp + NS_IN6ADDRSZ;
+  colonp = NULL;
+  /* Leading :: requires some special handling. */
+  if (*src == ':')
+    if (*++src != ':')
+      return (0);
+  curtok = src;
+  saw_xdigit = 0;
+  val = 0;
+  for (; (ch = *src); src++) {
+    const char *pch;
+
+    if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+      pch = strchr((xdigits = xdigits_u), ch);
+    if (pch != NULL) {
+      val <<= 4;
+      val |= (pch - xdigits);
+      if (val > 0xffff)
+        return (0);
+      saw_xdigit = 1;
+      continue;
+    }
+    if (ch == ':') {
+      curtok = src+1;
+      if (!saw_xdigit) {
+        if (colonp)
+          return (0);
+        colonp = tp;
+        continue;
+      }
+      if (tp + NS_INT16SZ > endp)
+        return (0);
+      *tp++ = (uint8_t) (val >> 8) & 0xff;
+      *tp++ = (uint8_t) val & 0xff;
+      saw_xdigit = 0;
+      val = 0;
+      continue;
+    }
+    if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
+        (len = inet_pton4(curtok, tp)) > 0) {
+      src = curtok + len;
+      tp += NS_INADDRSZ;
+      saw_xdigit = 0;
+      break;  /* '\0' was seen by inet_pton4(). */
+    }
+    break;
+  }
+  if (saw_xdigit) {
+    if (tp + NS_INT16SZ > endp)
+      return (0);
+    *tp++ = (uint8_t) (val >> 8) & 0xff;
+    *tp++ = (uint8_t) val & 0xff;
+  }
+  if (colonp != NULL) {
+    /*
+     * Since some memmove()'s erroneously fail to handle
+     * overlapping regions, we'll do the shift by hand.
+     */
+    const int n = (int)(tp - colonp);
+    int i;
+
+    for (i = 1; i <= n; i++) {
+      endp[- i] = colonp[n - i];
+      colonp[n - i] = 0;
+    }
+    tp = endp;
+  }
+  if (tp != endp)
+    return (0);
+  memcpy(dst, tmp, NS_IN6ADDRSZ);
+  return (int)(src - start);
+}
+
+nonnull_all
+static really_inline int32_t scan_ip6(const char *text, uint8_t *wire)
+{
+  return inet_pton6(text, wire);
+}
+
+nonnull_all
+static really_inline int32_t parse_ip6(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *item,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  if ((size_t)inet_pton6(token->data, rdata->octets) != token->length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type));
+  rdata->octets += 16;
+  return 0;
+}
+
+#endif // IP6_H
Index: simdzone/src/generic/loc.h
===================================================================
RCS file: simdzone/src/generic/loc.h
diff -N simdzone/src/generic/loc.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/loc.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,249 @@
+/*
+ * loc.h -- Location Information (RFC1876) parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef LOC_H
+#define LOC_H
+
+nonnull_all
+static really_inline int32_t scan_degrees(
+	const char *text, size_t length, uint32_t *degrees)
+{
+  uint8_t digits[3];
+
+  digits[0] = (uint8_t)text[0] - '0';
+  digits[1] = (uint8_t)text[1] - '0';
+  digits[2] = (uint8_t)text[2] - '0';
+
+  switch (length) {
+    case 1:
+      *degrees = digits[0] * 3600000;
+      if (digits[0] > 9)
+        return -1;
+      return 0;
+    case 2:
+      *degrees = digits[0] * 36000000 + digits[1] * 3600000;
+      if (digits[0] > 9 || digits[1] > 9)
+        return -1;
+      return 0;
+    case 3:
+      *degrees = digits[0] * 360000000 +
+                 digits[1] *  36000000 +
+                 digits[2] *   3600000;
+      if (*degrees > 648000000u)
+        return -1;
+      if (digits[0] > 9 || digits[1] > 9 || digits[2] > 9)
+        return -1;
+      return 0;
+    default:
+      return -1;
+  }
+}
+
+nonnull_all
+static really_inline int64_t scan_minutes(
+	const char *text, size_t length, uint32_t *minutes)
+{
+  uint8_t digits[2];
+
+  digits[0] = (uint8_t)text[0] - '0';
+  digits[1] = (uint8_t)text[1] - '0';
+
+  switch (length) {
+    case 1:
+      *minutes = digits[0] * 60000;
+      if (digits[0] > 9)
+        return -1;
+      return 0;
+    case 2:
+      *minutes = digits[0] * 600000 + digits[1] * 60000;
+      if (*minutes > 3600000 || digits[0] > 9 || digits[1] > 9)
+        return -1;
+      return 0;
+    default:
+      return -1;
+  }
+}
+
+nonnull_all
+static really_inline int64_t scan_seconds(
+	const char *text, size_t length, uint32_t *seconds)
+{
+  uint8_t digits[3];
+  size_t count;
+
+  digits[0] = (uint8_t)text[0] - '0';
+  digits[1] = (uint8_t)text[1] - '0';
+
+  if (length == 1 || text[1] == '.') {
+    count = 1;
+    *seconds = digits[0] * 1000;
+    if (digits[0] > 9)
+      return -1;
+    digits[0] = (uint8_t)text[2] - '0';
+    digits[1] = (uint8_t)text[3] - '0';
+    digits[2] = (uint8_t)text[4] - '0';
+  } else if (length == 2 || text[2] == '.') {
+    count = 2;
+    *seconds = digits[0] * 10000 + digits[1] * 1000;
+    if (*seconds > 60000 || digits[0] > 5 || digits[1] > 9)
+      return -1;
+    digits[0] = (uint8_t)text[3] - '0';
+    digits[1] = (uint8_t)text[4] - '0';
+    digits[2] = (uint8_t)text[5] - '0';
+  } else {
+    return -1;
+  }
+
+  switch (length - count) {
+    case 0:
+      return 0;
+    case 1:
+      return -1;
+    case 2:
+      *seconds += digits[0] * 100u;
+      if (digits[0] > 9)
+        return -1;
+      return 0;
+    case 3:
+      *seconds += digits[0] * 100u + digits[1] * 10u;
+      if (digits[0] > 9 || digits[1] > 9)
+        return -1;
+      return 0;
+    case 4:
+      *seconds += digits[0] * 100u + digits[1] * 10u + digits[2];
+      if (digits[0] > 9 || digits[1] > 9 || digits[0] > 9)
+        return -1;
+      return 0;
+    default:
+      return -1;
+  }
+}
+
+nonnull((1,3))
+static really_inline int32_t scan_altitude(
+  const char *text, size_t length, uint32_t *altitude)
+{
+  uint64_t negative = 0, limit = 11, maximum = 4284967295llu;
+
+  if (text[0] == '-')
+    (void)(negative = 1), (void)(limit = 8), maximum = 10000000llu;
+
+  length -= (text[length - 1] == 'm');
+
+  uint64_t meters = 0, index = negative;
+  for (;; index++) {
+    const uint8_t digit = (uint8_t)text[index] - '0';
+    if (digit > 9)
+      break;
+    meters = meters * 10 + digit;
+  }
+
+  uint64_t centimeters = meters * 100u; // convert to centimeters
+  if (text[index] == '.') {
+    uint8_t digits[2];
+    limit += 1;
+    digits[0] = (uint8_t)text[index+1] - '0';
+    digits[1] = (uint8_t)text[index+2] - '0';
+    switch (length - index) {
+      case 1:
+        index += 1;
+        break;
+      case 2:
+        if (digits[0] > 9)
+          return -1;
+        centimeters += (uint64_t)digits[0] * 10u;
+        index += 2;
+        break;
+      case 3:
+        if (digits[0] > 9 || digits[1] > 9)
+          return -1;
+        centimeters += (uint64_t)digits[0] * 10u + (uint64_t)digits[1];
+        index += 3;
+        break;
+      default:
+        return -1;
+    }
+  }
+
+  if (index == negative || index > limit || index != length || centimeters > maximum)
+    return -1;
+
+  if (negative)
+    *altitude = (uint32_t)(10000000llu - centimeters);
+  else
+    *altitude = (uint32_t)(10000000llu + centimeters);
+
+  return 0;
+}
+
+// converts ascii size/precision X * 10**Y(cm) to 0xXY
+nonnull((1,3))
+static really_inline int32_t scan_precision(
+  const char *text, size_t length, uint8_t *scientific)
+{
+  uint64_t meters = 0, centimeters;
+
+  // RFC1876 conversion routines
+  static uint64_t poweroften[10] = {
+    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
+
+  length -= text[length - 1] == 'm';
+
+  size_t index = 0;
+  for (;; index++) {
+    const uint8_t digit = (uint8_t)text[index] - '0';
+    if (digit > 9)
+      break;
+    meters = meters * 10 + digit;
+  }
+
+  if (index == 0 || index > 8) // 0 .. 90000000.00
+    return -1; // syntax error
+
+  centimeters = meters * 100; // convert to centimeters
+  if (text[index] == '.') {
+    uint8_t digits[2];
+    digits[0] = (uint8_t)text[index+1] - '0';
+    digits[1] = (uint8_t)text[index+2] - '0';
+    switch (length - index) {
+      case 1:
+        index += 1;
+        break;
+      case 2:
+        if (digits[0] > 9)
+          return -1;
+        index += 2;
+        centimeters += digits[0] * 10;
+        break;
+      case 3:
+        if (digits[0] > 9 || digits[1] > 9)
+          return -1;
+        index += 3;
+        centimeters += digits[0] * 10 + digits[1];
+        break;
+      default:
+        return -1;
+    }
+  }
+
+  if (index != length)
+    return -1; // syntax error
+
+  uint8_t exponent = 0;
+  while (centimeters >= poweroften[exponent+1] && exponent < 9)
+    exponent++;
+
+  uint8_t mantissa = (uint8_t)(centimeters / poweroften[exponent]);
+  if (mantissa > 9u)
+    mantissa = 9u;
+
+  *scientific = (uint8_t)(mantissa << 4) | exponent;
+  return 0;
+}
+
+#endif // LOC_H
Index: simdzone/src/generic/name.h
===================================================================
RCS file: simdzone/src/generic/name.h
diff -N simdzone/src/generic/name.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/name.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,158 @@
+/*
+ * name.h -- domain name parser
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef NAME_H
+#define NAME_H
+
+typedef struct name_block name_block_t;
+struct name_block {
+  uint64_t backslashes;
+  uint64_t dots;
+};
+
+nonnull_all
+static really_inline void copy_name_block(
+  name_block_t *block, const char *text, uint8_t *wire)
+{
+  simd_8x32_t input;
+  simd_loadu_8x32(&input, text);
+  simd_storeu_8x32(wire, &input);
+  block->backslashes = simd_find_8x32(&input, '\\');
+  block->dots = simd_find_8x32(&input, '.');
+}
+
+nonnull_all
+static really_inline int32_t scan_name(
+  const char *data,
+  size_t tlength,
+  uint8_t octets[255 + ZONE_BLOCK_SIZE],
+  size_t *lengthp)
+{
+  uint64_t label = 0;
+  const char *text = data;
+  uint8_t *wire = octets + 1;
+  name_block_t block;
+
+  octets[0] = 0;
+
+  // real world domain names quickly exceed 16 octets (www.example.com is
+  // encoded as 3www7example3com0, or 18 octets), but rarely exceed 32
+  // octets. encode in 32-byte blocks.
+  copy_name_block(&block, text, wire);
+
+  uint64_t count = 32, length = 0, base = 0, left = tlength;
+  uint64_t carry = 0;
+  if (tlength < 32)
+    count = tlength;
+  uint64_t mask = (1llu << count) - 1u;
+
+  // check for escape sequences
+  if (unlikely(block.backslashes & mask))
+    goto escaped;
+
+  // check for root, i.e. "."
+  if (unlikely(block.dots & 1llu))
+    return ((*lengthp = tlength) == 1 ? 0 : -1);
+
+  length = count;
+  block.dots &= mask;
+  carry = (block.dots >> (length - 1));
+
+  // check for null labels, i.e. ".."
+  if (unlikely(block.dots & (block.dots >> 1)))
+    return -1;
+
+  if (likely(block.dots)) {
+    count = trailing_zeroes(block.dots);
+    block.dots = clear_lowest_bit(block.dots);
+    octets[label] = (uint8_t)count;
+    label = count + 1;
+    while (block.dots) {
+      count = trailing_zeroes(block.dots);
+      block.dots = clear_lowest_bit(block.dots);
+      octets[label] = (uint8_t)(count - label);
+      label = count + 1;
+    }
+  }
+
+  octets[label] = (uint8_t)(length - label);
+
+  if (tlength <= 32)
+    return (void)(*lengthp = length + 1), carry == 0;
+
+  text += length;
+  wire += length;
+  left -= length;
+
+  do {
+    copy_name_block(&block, text, wire);
+    count = 32;
+    if (left < 32)
+      count = left;
+    mask = (1llu << count) - 1u;
+    base = length;
+
+    // check for escape sequences
+    if (unlikely(block.backslashes & mask)) {
+escaped:
+      block.backslashes &= -block.backslashes;
+      mask = block.backslashes - 1;
+      block.dots &= mask;
+      count = count_ones(mask);
+      const uint32_t octet = unescape(text+count, wire+count);
+      if (!octet)
+        return -1;
+      text += count + octet;
+      wire += count + 1;
+      length += count + 1;
+      left -= count + octet;
+      count += 1; // for correct carry
+    } else {
+      block.dots &= mask;
+      text += count;
+      wire += count;
+      length += count;
+      left -= count;
+    }
+
+    // check for null labels, i.e. ".."
+    if (unlikely(block.dots & ((block.dots >> 1) | carry)))
+      return -1;
+    carry = block.dots >> (count - 1);
+
+    if (likely(block.dots)) {
+      count = trailing_zeroes(block.dots) + base;
+      block.dots = clear_lowest_bit(block.dots);
+      octets[label] = (uint8_t)(count - label);
+      // check if label exceeds 63 octets
+      if (unlikely(count - label > 63))
+        return -1;
+      label = count + 1;
+      while (block.dots) {
+        count = trailing_zeroes(block.dots) + base;
+        block.dots = clear_lowest_bit(block.dots);
+        octets[label] = (uint8_t)(count - label);
+        label = count + 1;
+      }
+    } else {
+      // check if label exceeds 63 octets
+      if (length - label > 63)
+        return -1;
+    }
+
+    octets[label] = (uint8_t)(length - label);
+  } while (left && length < 255);
+
+  if (length >= 255)
+    return -1;
+
+  *lengthp = length + 1;
+  return carry == 0;
+}
+
+#endif // NAME_H
Index: simdzone/src/generic/nsap.h
===================================================================
RCS file: simdzone/src/generic/nsap.h
diff -N simdzone/src/generic/nsap.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/nsap.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,63 @@
+/*
+ * nsap.h -- NSAP (RFC1706) parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef NSAP_H
+#define NSAP_H
+
+// https://datatracker.ietf.org/doc/html/rfc1706 (historic)
+
+nonnull_all
+static really_inline int32_t parse_nsap(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const uint8_t *data = (const uint8_t *)token->data;
+
+  // RFC1706 section 7
+  // NSAP format is "0x" (i.e., a zero followed by an 'x' character) followed
+  // by a variable length string of hex characters (0 to 9, a to f). The hex
+  // string is case-insensitive. "."s (i.e., periods) may be inserted in the
+  // hex string anywhere after "0x" for readability. The "."s have no
+  // significance other than for readability and are not propagated in the
+  // protocol (e.g., queries or zone transfers).
+  if (unlikely(data[0] != '0' || !(data[1] == 'X' || data[1] == 'x')))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  data += 2;
+
+  while (rdata->octets < rdata->limit) {
+    uint32_t d0 = base16_table_dec_32bit_d0[data[0]];
+    uint32_t d1 = base16_table_dec_32bit_d1[data[1]];
+    if ((d0 | d1) > 0xff) {
+      while (*data == '.') data++;
+      d0 = base16_table_dec_32bit_d0[data[0]];
+      if (d0 > 0xff)
+        break;
+      data += 1;
+      while (*data == '.') data++;
+      d1 = base16_table_dec_32bit_d1[data[0]];
+      if (d1 > 0xff)
+        goto bad_sequence;
+      data += 1;
+    } else {
+      data += 2;
+    }
+    *rdata->octets++ = (uint8_t)(d0 | d1);
+  }
+
+  if (rdata->octets <= rdata->limit && data == (uint8_t *)token->data + token->length)
+    return 0;
+
+bad_sequence:
+  SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+}
+
+#endif // NSAP_H
Index: simdzone/src/generic/nsec.h
===================================================================
RCS file: simdzone/src/generic/nsec.h
diff -N simdzone/src/generic/nsec.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/nsec.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,68 @@
+/*
+ * nsec.h -- NSEC (RFC4043) parser
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef NSEC_H
+#define NSEC_H
+
+nonnull_all
+static really_inline int32_t scan_type(
+  const char *, size_t, uint16_t *, const mnemonic_t **);
+
+typedef uint8_t nsec_t[32 /* 256 / 8 */ + 2];
+
+nonnull_all
+static really_inline int32_t parse_nsec(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  token_t *token)
+{
+  if (likely(is_contiguous(token))) {
+    nsec_t *bitmap = (void *)rdata->octets;
+    assert(rdata->octets <= rdata->limit);
+    assert((size_t)(rdata->limit - rdata->octets) >= 256 * sizeof(nsec_t));
+
+    uint32_t highest_window = 0;
+    uint32_t windows[256] = { 0 };
+
+    do {
+      uint16_t code;
+      const mnemonic_t *mnemonic;
+
+      if (scan_type(token->data, token->length, &code, &mnemonic) != 1)
+        SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+      const uint8_t bit = (uint8_t)(code % 256);
+      const uint8_t window = code / 256;
+      const uint8_t block = bit / 8;
+
+      if (!windows[window])
+        memset(bitmap[window], 0, sizeof(bitmap[window]));
+      if (window > highest_window)
+        highest_window = window;
+      windows[window] |= 1 << block;
+      bitmap[window][2 + block] |= (1 << (7 - bit % 8));
+      take(parser, token);
+    } while (is_contiguous(token));
+
+    for (uint32_t window = 0; window <= highest_window; window++) {
+      if (!windows[window])
+        continue;
+      const uint8_t blocks = (uint8_t)(64 - leading_zeroes(windows[window]));
+      memmove(rdata->octets, &bitmap[window], 2 + blocks);
+      rdata->octets[0] = (uint8_t)window;
+      rdata->octets[1] = blocks;
+      rdata->octets += 2 + blocks;
+    }
+  }
+
+  return have_delimiter(parser, type, token);
+}
+
+#endif // NSEC_H
Index: simdzone/src/generic/number.h
===================================================================
RCS file: simdzone/src/generic/number.h
diff -N simdzone/src/generic/number.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/number.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,121 @@
+/*
+ * number.h -- integer parsing routines
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef NUMBER_H
+#define NUMBER_H
+
+nonnull((1,3))
+static really_inline int32_t scan_int8(
+  const char *data, size_t length, uint8_t *number)
+{
+  uint32_t sum = (uint8_t)data[0] - '0';
+
+  if (sum > 9 || !length || length > 3)
+    return 0;
+
+  for (size_t count=1; count < length; count++) {
+    const uint8_t digit = (uint8_t)data[count] - '0';
+    sum = sum * 10 + digit;
+    if (digit > 9)
+      return 0;
+  }
+
+  *number = (uint8_t)sum;
+  return sum <= 255u;
+}
+
+nonnull((1,3))
+static really_inline int32_t scan_int16(
+  const char *data, size_t length, uint16_t *number)
+{
+  uint32_t sum = (uint8_t)data[0] - '0';
+
+  if (sum > 9 || !length || length > 5)
+    return 0;
+
+  for (size_t count=1; count < length; count++) {
+    const uint8_t digit = (uint8_t)data[count] - '0';
+    sum = sum * 10 + digit;
+    if (digit > 9)
+      return 0;
+  }
+
+  *number = (uint16_t)sum;
+  return sum <= 65535u;
+}
+
+nonnull((1,3))
+static really_inline int32_t scan_int32(
+  const char *data, size_t length, uint32_t *number)
+{
+  uint64_t sum = (uint8_t)data[0] - '0';
+
+  if (sum > 9 || !length || length > 10)
+    return 0;
+
+  for (size_t count=1; count < length; count++) {
+    const uint8_t digit = (uint8_t)data[count] - '0';
+    sum = sum * 10 + digit;
+    if (digit > 9)
+      return 0;
+  }
+
+  *number = (uint32_t)sum;
+  return sum <= 4294967295u;
+}
+
+nonnull_all
+static really_inline int32_t parse_int8(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  uint8_t number;
+  if (!scan_int8(token->data, token->length, &number))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  *rdata->octets++ = number;
+  return 0;
+}
+
+nonnull_all
+static really_inline int32_t parse_int16(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  uint16_t number;
+  if (!scan_int16(token->data, token->length, &number))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  number = htobe16(number);
+  memcpy(rdata->octets, &number, 2);
+  rdata->octets += 2;
+  return 0;
+}
+
+nonnull_all
+static really_inline int32_t parse_int32(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  uint32_t number;
+  if (!scan_int32(token->data, token->length, &number))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  number = htobe32(number);
+  memcpy(rdata->octets, &number, 4);
+  rdata->octets += 4;
+  return 0;
+}
+
+#endif // NUMBER_H
Index: simdzone/src/generic/nxt.h
===================================================================
RCS file: simdzone/src/generic/nxt.h
diff -N simdzone/src/generic/nxt.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/nxt.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,59 @@
+/*
+ * nxt.h - NXT (RFC2535) parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef NXT_H
+#define NXT_H
+
+nonnull_all
+static really_inline int32_t scan_type(
+  const char *data,
+  size_t length,
+  uint16_t *code,
+  const mnemonic_t **mnemonic);
+
+nonnull_all
+static really_inline int32_t parse_nxt(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  token_t *token)
+{
+  uint16_t code;
+  const mnemonic_t *mnemonic;
+
+  if (is_contiguous(token)) {
+    if (scan_type(token->data, token->length, &code, &mnemonic) != 1)
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+    uint8_t bit = (uint8_t)(code % 8);
+    uint8_t block = (uint8_t)(code / 8), highest_block = block;
+
+    memset(rdata->octets, 0, block + 1);
+    rdata->octets[block] = (uint8_t)(1 << (7 - bit));
+
+    take(parser, token);
+    while (is_contiguous(token)) {
+      if (scan_type(token->data, token->length, &code, &mnemonic) != 1)
+        SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+      bit = (uint8_t)(code % 8);
+      block = (uint8_t)(code / 8);
+      if (block > highest_block) {
+        memset(&rdata->octets[highest_block+1], 0, block - highest_block);
+        highest_block = block;
+      }
+      rdata->octets[block] |= 1 << (7 - bit);
+      take(parser, token);
+    }
+
+    rdata->octets += highest_block + 1;
+  }
+
+  return have_delimiter(parser, type, token);
+}
+
+#endif // NXT_H
Index: simdzone/src/generic/parser.h
===================================================================
RCS file: simdzone/src/generic/parser.h
diff -N simdzone/src/generic/parser.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/parser.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,985 @@
+/*
+ * parser.h -- base parser definitions
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef PARSER_H
+#define PARSER_H
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if _MSC_VER
+# define strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n))
+#else
+# include <strings.h>
+#endif
+
+typedef zone_parser_t parser_t; // convenience
+typedef zone_file_t file_t;
+typedef zone_name_buffer_t name_buffer_t;
+typedef zone_rdata_buffer_t rdata_buffer_t;
+
+typedef struct token token_t;
+struct token {
+  int32_t code;
+  const char *data;
+  size_t length;
+};
+
+// view of current RDATA buffer
+typedef struct rdata rdata_t;
+struct rdata {
+  uint8_t *octets;
+  uint8_t *limit;
+};
+
+typedef struct string string_t;
+struct string {
+  const char *data;
+  size_t length;
+};
+
+typedef struct mnemonic mnemonic_t;
+struct mnemonic {
+  struct {
+    char data[16]; /* MUST be 16 because of usage with SIMD cmpeq */
+    size_t length;
+  } key;
+  uint32_t value;
+};
+
+#define NAME(info) ((info)->name.key.data)
+
+
+typedef struct svc_param_info svc_param_info_t;
+struct svc_param_info;
+
+typedef struct rdata_info rdata_info_t;
+struct rdata_info;
+
+typedef struct type_info type_info_t;
+struct type_info;
+
+
+typedef int32_t (*parse_svc_param_t)(
+  parser_t *,
+  const type_info_t *,
+  const rdata_info_t *,
+  uint16_t,
+  const svc_param_info_t *,
+  rdata_t *,
+  const token_t *);
+
+struct svc_param_info {
+  struct {
+    struct {
+      char data[24];
+      size_t length;
+    } key;
+    uint32_t value;
+  } name;
+  uint32_t has_value;
+  parse_svc_param_t parse, parse_lax;
+};
+
+struct rdata_info {
+  struct { string_t key; } name; // convenience
+};
+
+typedef struct class_info class_info_t;
+struct class_info {
+  mnemonic_t name;
+};
+
+typedef int32_t (*check_rr_t)(
+  parser_t *,
+  const type_info_t *,
+  const rdata_t *);
+
+typedef int32_t (*parse_rdata_t)(
+  parser_t *,
+  const type_info_t *,
+  rdata_t *,
+  token_t *);
+
+struct type_info {
+  mnemonic_t name;
+  uint16_t defined_in;
+  bool is_obsolete;
+  bool is_experimental;
+  struct {
+    size_t length;
+    const rdata_info_t *fields;
+  } rdata;
+  check_rr_t check;
+  parse_rdata_t parse;
+};
+
+
+#define END_OF_FILE (0)
+#define CONTIGUOUS (1<<0)
+#define QUOTED (1<<1)
+#define LINE_FEED (1<<2)
+#define LEFT_PAREN (1<<3)
+#define RIGHT_PAREN (1<<4)
+#define BLANK (1<<6)
+#define COMMENT (1<<7)
+
+static const uint8_t classify[256] = {
+  // 0x00 = "\0"
+  0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x00 - 0x07
+  // 0x09 = "\t", 0x0a = "\n", 0x0d = "\r"
+  0x01, 0x40, 0x04, 0x01, 0x01, 0x40, 0x01, 0x01,  // 0x08 - 0x0f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x10 - 0x17
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x18 - 0x1f
+  // 0x20 = " ", 0x22 = "\""
+  0x40, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x20 - 0x27
+  // 0x28 = "(", 0x29 = ")"
+  0x08, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x28 - 0x2f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x30 - 0x37
+  // 0x3b = ";"
+  0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01,  // 0x38 - 0x3f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x40 - 0x47
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x48 - 0x4f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x50 - 0x57
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x58 - 0x5f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x60 - 0x67
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x68 - 0x6f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x70 - 0x77
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x78 - 0x7f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x80 - 0x87
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x88 - 0x8f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x90 - 0x97
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0x98 - 0x9f
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0xa0 - 0xa7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0xa8 - 0xaf
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0xb0 - 0xb7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0xb8 - 0xbf
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0xc0 - 0xc7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0xc8 - 0xcf
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0xd0 - 0xd7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0xd8 - 0xdf
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0xe0 - 0xe7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0xe8 - 0xef
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,  // 0xf8 - 0xf7
+  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01   // 0xf8 - 0xff
+};
+
+
+
+// special constant to mark line feeds with additional line count. i.e. CRLF
+// within text. line feeds have no special meaning other than terminating the
+// record and require no further processing
+static const char line_feed[ZONE_BLOCK_SIZE] = { '\n', '\0' };
+
+// special constant used as data on errors
+static const char end_of_file[ZONE_BLOCK_SIZE] = { '\0' };
+
+#define READ_ALL_DATA (1)
+#define NO_MORE_DATA (2)
+#define MISSING_QUOTE (3)
+
+extern int32_t zone_open_file(
+  parser_t *, const char *path, size_t length, zone_file_t **);
+
+extern void zone_close_file(
+  parser_t *, zone_file_t *);
+
+extern void zone_vlog(parser_t *, uint32_t, const char *, va_list);
+
+nonnull((1))
+static really_inline void defer_error(token_t *token, int32_t code)
+{
+  token->code = code;
+  token->data = end_of_file;
+  token->length = 0;
+}
+
+nonnull((1,3))
+warn_unused_result
+static never_inline int32_t raise_error(
+  parser_t *parser, int32_t code, const char *format, ...)
+{
+  va_list arguments;
+  uint32_t category = ZONE_ERROR;
+  if (code == ZONE_SEMANTIC_ERROR && parser->options.secondary)
+    category = ZONE_WARNING;
+  va_start(arguments, format);
+  zone_vlog(parser, category, format, arguments);
+  va_end(arguments);
+  if (category == ZONE_WARNING)
+    return 0;
+  return code;
+}
+
+#define RAISE_ERROR(parser, code, ...) \
+  do { \
+    return raise_error((parser), (code), __VA_ARGS__); \
+  } while (0)
+
+#define SYNTAX_ERROR(parser, ...) \
+  RAISE_ERROR((parser), ZONE_SYNTAX_ERROR, __VA_ARGS__)
+#define OUT_OF_MEMORY(parser, ...) \
+  RAISE_ERROR((parser), ZONE_OUT_OF_MEMORY, __VA_ARGS__)
+#define READ_ERROR(parser, ...) \
+  RAISE_ERROR((parser), ZONE_READ_ERROR, __VA_ARGS__)
+#define NOT_IMPLEMENTED(parser, ...) \
+  RAISE_ERROR((parser), ZONE_NOT_IMPLEMENTED, __VA_ARGS__)
+#define NOT_PERMITTED(parser, ...) \
+  RAISE_ERROR((parser), ZONE_NOT_PERMITTED, __VA_ARGS__)
+#define NOT_A_FILE(parser, ...) \
+  RAISE_ERROR((parser), ZONE_NOT_A_FILE, __VA_ARGS__)
+
+// semantic errors in zone files are special as a secondary may choose
+// to report, but otherwise ignore them. e.g. a TTL with the MSB set. cases
+// where the data can be presented in wire format but is otherwise considered
+// invalid. e.g. a TTL is limited to 32-bits, values that require more bits
+// are invalid without exception, but secondaries may choose to accept values
+// with the MSB set in order to update the zone
+#define SEMANTIC_ERROR(parser, ...) \
+  do { \
+    if (raise_error((parser), ZONE_SEMANTIC_ERROR, __VA_ARGS__)) \
+      return ZONE_SEMANTIC_ERROR; \
+  } while (0)
+
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t reindex(parser_t *parser);
+
+// limit maximum size of buffer to avoid malicious inputs claiming all memory.
+// the maximum size of the buffer is the worst-case size of rdata, or 65535
+// bytes, in presentation format. comma-separated value lists as introduced
+// by RFC 9460 allow for double escaping. a reasonable limit is therefore
+// 65535 (rdata) * 4 (\DDD) * 4 (\DDD) + 64 (sufficiently large enough to
+// cover longest key and ancillary characters) bytes.
+#define MAXIMUM_WINDOW_SIZE (65535u * 4u * 4u + 64u)
+
+nonnull_all
+warn_unused_result
+static int32_t refill(parser_t *parser)
+{
+  // refill if possible (i.e. not if string or if file is empty)
+  if (parser->file->end_of_file)
+    return 0;
+
+  assert(parser->file->handle);
+
+  // move unread data to start of buffer
+  char *data = parser->file->buffer.data + parser->file->buffer.index;
+  // account for non-terminated character-strings
+  if (*parser->file->fields.head[0] != '\0')
+    data = (char *)parser->file->fields.head[0];
+
+  *parser->file->fields.head = parser->file->buffer.data;
+  // account for unread data left in buffer
+  size_t length = (size_t)
+    ((parser->file->buffer.data + parser->file->buffer.length) - data);
+  // account for non-terminated character-string left in buffer
+  assert((parser->file->buffer.data + parser->file->buffer.index) >= data);
+  size_t index = (size_t)
+    ((parser->file->buffer.data + parser->file->buffer.index) - data);
+  memmove(parser->file->buffer.data, data, length);
+  parser->file->buffer.length = length;
+  parser->file->buffer.index = index;
+  parser->file->buffer.data[length] = '\0';
+
+  // allocate extra space if required
+  if (parser->file->buffer.length == parser->file->buffer.size) {
+    size_t size = parser->file->buffer.size;
+    if (parser->file->buffer.size >= MAXIMUM_WINDOW_SIZE)
+      SYNTAX_ERROR(parser, "Impossibly large input, exceeds %zu bytes", size);
+    size += ZONE_WINDOW_SIZE;
+    if (!(data = realloc(parser->file->buffer.data, size + 1 + ZONE_BLOCK_SIZE)))
+      OUT_OF_MEMORY(parser, "Not enough memory to allocate buffer of %zu", size);
+    parser->file->buffer.size = size;
+    parser->file->buffer.data = data;
+    // update reference to partial token
+    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");
+
+  // 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;
+  return 0;
+}
+
+// do not invoke directly
+nonnull_all
+warn_unused_result
+static really_inline int32_t advance(parser_t *parser)
+{
+  int32_t code;
+
+  // save embedded line count (quoted or escaped newlines)
+  parser->file->newlines.tape[0] = parser->file->newlines.tail[0];
+  parser->file->newlines.head = parser->file->newlines.tape;
+  parser->file->newlines.tail = parser->file->newlines.tape;
+  // restore non-terminated token (partial quoted or contiguous)
+  parser->file->fields.tape[0] = parser->file->fields.tail[1];
+  parser->file->fields.head = parser->file->fields.tape;
+  parser->file->fields.tail =
+    parser->file->fields.tape + (*parser->file->fields.tape[0] != '\0');
+  // reset delimiters
+  parser->file->delimiters.head = parser->file->delimiters.tape;
+  parser->file->delimiters.tail = parser->file->delimiters.tape;
+
+  // delayed syntax error
+  if (parser->file->end_of_file == MISSING_QUOTE)
+    SYNTAX_ERROR(parser, "Missing closing quote");
+  if ((code = refill(parser)) < 0)
+    return code;
+
+  if (reindex(parser)) {
+    // save non-terminated token
+    parser->file->fields.tail[0] = parser->file->fields.tail[-1];
+    parser->file->fields.tail--;
+    // delay syntax error so correct line number is available
+    if (parser->file->end_of_file == NO_MORE_DATA &&
+       *parser->file->fields.tail[1] == '"')
+      parser->file->end_of_file = MISSING_QUOTE;
+  } else {
+    parser->file->fields.tail[1] = end_of_file;
+  }
+
+  // FIXME: if tail is still equal to tape, refill immediately?!
+
+  // terminate (end of buffer is null-terminated)
+  parser->file->fields.tail[0] =
+    parser->file->buffer.data + parser->file->buffer.length;
+  parser->file->delimiters.tail[0] =
+    parser->file->buffer.data + parser->file->buffer.length;
+  // start-of-line must be false if start of tape is not start of buffer
+  if (*parser->file->fields.head != parser->file->buffer.data)
+    parser->file->start_of_line = false;
+  return 0;
+}
+
+nonnull_all
+warn_unused_result
+static really_inline bool is_contiguous(const token_t *token)
+{
+  return token->code == CONTIGUOUS;
+}
+
+nonnull_all
+warn_unused_result
+static really_inline bool is_quoted(const token_t *token)
+{
+  return token->code == QUOTED;
+}
+
+nonnull_all
+warn_unused_result
+static really_inline bool is_contiguous_or_quoted(const token_t *token)
+{
+  return (token->code == CONTIGUOUS || token->code == QUOTED);
+}
+
+nonnull_all
+warn_unused_result
+static really_inline bool is_delimiter(const token_t *token)
+{
+  return (token->code == LINE_FEED || token->code == END_OF_FILE);
+}
+
+nonnull_all
+warn_unused_result
+static really_inline bool is_line_feed(const token_t *token)
+{
+  return token->code == LINE_FEED;
+}
+
+nonnull_all
+warn_unused_result
+static really_inline bool is_end_of_file(const token_t *token)
+{
+  return token->code == 0;
+}
+
+
+#undef SYNTAX_ERROR
+#define SYNTAX_ERROR(parser, token, ...) \
+  do { \
+    zone_log((parser), ZONE_ERROR, __VA_ARGS__); \
+    defer_error((token), ZONE_SYNTAX_ERROR); \
+    return; \
+  } while (0)
+
+#define ERROR(parser, token, code) \
+  do { \
+    defer_error(token, code); \
+    return; \
+  } while (0)
+
+
+nonnull_all
+static never_inline void maybe_take(parser_t *parser, token_t *token)
+{
+  for (;;) {
+    token->data = *parser->file->fields.head;
+    token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ];
+    if (likely(token->code == CONTIGUOUS)) {
+      assert(*parser->file->delimiters.head > *parser->file->fields.head);
+      token->length = (uintptr_t)*parser->file->delimiters.head -
+                      (uintptr_t)*parser->file->fields.head;
+      parser->file->fields.head++;
+      parser->file->delimiters.head++;
+      return;
+    } else if (token->code == LINE_FEED) {
+      if (unlikely(token->data == line_feed))
+        parser->file->span += *parser->file->newlines.head++;
+      parser->file->span++;
+      parser->file->fields.head++;
+      if (unlikely(parser->file->grouped))
+        continue;
+      parser->file->start_of_line = classify[ (uint8_t)*(token->data+1) ] != BLANK;
+      token->length = 1;
+      return;
+    } else if (token->code == QUOTED) {
+      assert(*parser->file->delimiters.head > *parser->file->fields.head);
+      token->data++;
+      token->length = ((uintptr_t)*parser->file->delimiters.head -
+                       (uintptr_t)*parser->file->fields.head) - 1;
+      parser->file->fields.head++;
+      parser->file->delimiters.head++;
+      return;
+    } else if (token->code == END_OF_FILE) {
+      int32_t code;
+      if (parser->file->end_of_file == NO_MORE_DATA) {
+        if (parser->file->grouped)
+          SYNTAX_ERROR(parser, token, "Missing closing brace");
+        token->data = end_of_file;
+        token->length = 1;
+        return;
+      } else if (unlikely((code = advance(parser)) < 0)) {
+        ERROR(parser, token, code);
+      }
+    } else if (token->code == LEFT_PAREN) {
+      if (unlikely(parser->file->grouped))
+        SYNTAX_ERROR(parser, token, "Nested opening brace");
+      parser->file->grouped = true;
+      parser->file->fields.head++;
+    } else {
+      assert(token->code == RIGHT_PAREN);
+      if (unlikely(!parser->file->grouped))
+        SYNTAX_ERROR(parser, token, "Missing opening brace");
+      parser->file->grouped = false;
+      parser->file->fields.head++;
+    }
+  }
+}
+
+nonnull_all
+static really_inline void take(parser_t *parser, token_t *token)
+{
+  for (;;) {
+    token->data = *parser->file->fields.head;
+    token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ];
+    if (likely(token->code == CONTIGUOUS)) {
+      assert(*parser->file->delimiters.head > *parser->file->fields.head);
+      token->length = (uintptr_t)*parser->file->delimiters.head -
+                      (uintptr_t)*parser->file->fields.head;
+      parser->file->fields.head++;
+      parser->file->delimiters.head++;
+      return;
+    } else if (token->code == LINE_FEED) {
+      if (unlikely(token->data == line_feed))
+        parser->file->span += *parser->file->newlines.head++;
+      parser->file->span++;
+      parser->file->fields.head++;
+      if (unlikely(parser->file->grouped))
+        continue;
+      parser->file->start_of_line = classify[ (uint8_t)*(token->data+1) ] != BLANK;
+      token->length = 1;
+      return;
+    } else if (token->code == QUOTED) {
+      assert(*parser->file->delimiters.head > *parser->file->fields.head);
+      token->data++;
+      token->length = ((uintptr_t)*parser->file->delimiters.head -
+                       (uintptr_t)*parser->file->fields.head) - 1;
+      parser->file->fields.head++;
+      parser->file->delimiters.head++;
+      return;
+    } else {
+      maybe_take(parser, token);
+      return;
+    }
+  }
+}
+
+#undef SYNTAX_ERROR
+#undef ERROR
+
+// token sequence is predictable. fields typically require a specific type,
+// except names, strings and SvcParams. even then, names are typically not
+// quoted and strings (or text) are typically quoted. implement specialized
+// tape accessors for performance and reduction in binary size.
+
+#define SYNTAX_ERROR(parser, token, ...) \
+  do { \
+    zone_log((parser), ZONE_ERROR, __VA_ARGS__); \
+    defer_error((token), ZONE_SYNTAX_ERROR); \
+    return ZONE_SYNTAX_ERROR; \
+  } while (0)
+
+#define ERROR(parser, token, code) \
+  do { \
+    defer_error((token), (code)); \
+    return (code); \
+  } while (0)
+
+nonnull_all
+warn_unused_result
+static never_inline int32_t dont_have_contiguous(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  if (token->code < 0)
+    return token->code;
+  assert(token->code != CONTIGUOUS);
+  if (token->code == QUOTED)
+    SYNTAX_ERROR(parser, token, "Invalid %s in %s", NAME(field), NAME(type));
+  assert(token->code == END_OF_FILE || token->code == LINE_FEED);
+  SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type));
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t have_contiguous(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  if (unlikely(token->code != CONTIGUOUS))
+    return dont_have_contiguous(parser, type, field, token);
+  return 0;
+}
+
+nonnull_all
+warn_unused_result
+static never_inline int32_t maybe_take_contiguous(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  int32_t code;
+
+  assert(token->code != CONTIGUOUS);
+
+  for (;;) {
+    if (likely(token->code == CONTIGUOUS)) {
+      assert(*parser->file->delimiters.head > *parser->file->fields.head);
+      token->length = (uintptr_t)*parser->file->delimiters.head -
+                      (uintptr_t)*parser->file->fields.head;
+      parser->file->fields.head++;
+      parser->file->delimiters.head++;
+      return 0;
+    } else if (token->code == END_OF_FILE) {
+      if (parser->file->end_of_file == NO_MORE_DATA)
+        SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type));
+      if ((code = advance(parser)) < 0)
+        ERROR(parser, token, code);
+    } else if (token->code == QUOTED) {
+      SYNTAX_ERROR(parser, token, "Invalid %s in %s", NAME(field), NAME(type));
+    } else if (token->code == LEFT_PAREN) {
+      if (parser->file->grouped)
+        SYNTAX_ERROR(parser, token, "Nested opening brace");
+      parser->file->grouped = true;
+      parser->file->fields.head++;
+    } else if (token->code == RIGHT_PAREN) {
+      if (!parser->file->grouped)
+        SYNTAX_ERROR(parser, token, "Missing opening brace");
+      parser->file->grouped = false;
+      parser->file->fields.head++;
+    } else if (token->code == LINE_FEED) {
+      if (token->data == line_feed)
+        parser->file->span += *parser->file->newlines.head++;
+      parser->file->span++;
+      if (!parser->file->grouped)
+        SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type));
+      parser->file->fields.head++;
+    }
+    token->data = *parser->file->fields.head;
+    token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ];
+  }
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t take_contiguous(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  token->data = *parser->file->fields.head;
+  token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ];
+  if (unlikely(token->code != CONTIGUOUS))
+    return maybe_take_contiguous(parser, type, field, token);
+  assert(*parser->file->delimiters.head > *parser->file->fields.head);
+  token->length = (uintptr_t)*parser->file->delimiters.head -
+                  (uintptr_t)*parser->file->fields.head;
+  parser->file->fields.head++;
+  parser->file->delimiters.head++;
+  return 0;
+}
+
+nonnull_all
+warn_unused_result
+static never_inline int32_t dont_have_quoted(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  if (token->code < 0)
+    return token->code;
+  assert(token->code != QUOTED);
+  if (token->code == CONTIGUOUS)
+    SYNTAX_ERROR(parser, token, "Invalid %s in %s", NAME(field), NAME(type));
+  assert(token->code == END_OF_FILE || token->code == LINE_FEED);
+  SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type));
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t have_quoted(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  if (unlikely(token->code != QUOTED))
+    return dont_have_quoted(parser, type, field, token);
+  return 0;
+}
+
+nonnull_all
+warn_unused_result
+static never_inline int32_t maybe_take_quoted(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  int32_t code;
+
+  assert(token->code != QUOTED);
+
+  for (;;) {
+    if (likely(token->code == QUOTED)) {
+      assert(*parser->file->delimiters.head > *parser->file->fields.head);
+      token->data++;
+      token->length = ((uintptr_t)*parser->file->delimiters.head -
+                       (uintptr_t)*parser->file->fields.head) - 1;
+      parser->file->fields.head++;
+      parser->file->delimiters.head++;
+      return 0;
+    } else if (token->code == END_OF_FILE) {
+      if (parser->file->end_of_file == NO_MORE_DATA)
+        SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type));
+      if ((code = advance(parser)) < 0)
+        ERROR(parser, token, code);
+    } else if (token->code == CONTIGUOUS) {
+      SYNTAX_ERROR(parser, token, "Invalid %s in %s", NAME(field), NAME(type));
+    } else if (token->code == LEFT_PAREN) {
+      if (parser->file->grouped)
+        SYNTAX_ERROR(parser, token, "Nested opening brace");
+      parser->file->grouped = true;
+      parser->file->fields.head++;
+    } else if (token->code == RIGHT_PAREN) {
+      if (!parser->file->grouped)
+        SYNTAX_ERROR(parser, token, "Missing opening brace");
+      parser->file->grouped = false;
+      parser->file->fields.head++;
+    } else if (token->code == LINE_FEED) {
+      if (token->data == line_feed)
+        parser->file->span += *parser->file->newlines.head++;
+      parser->file->span++;
+      if (!parser->file->grouped)
+        SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type));
+      parser->file->fields.head++;
+    } else {
+      assert(token->code < 0);
+      return token->code;
+    }
+    token->data = *parser->file->fields.head;
+    token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ];
+  }
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t take_quoted(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  token->data = *parser->file->fields.head;
+  token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ];
+  if (unlikely((token->code != QUOTED)))
+    return maybe_take_quoted(parser, type, field, token);
+  assert(*parser->file->delimiters.head > *parser->file->fields.head);
+  token->data++;
+  token->length = ((uintptr_t)*parser->file->delimiters.head -
+                   (uintptr_t)*parser->file->fields.head) - 1;
+  parser->file->fields.head++;
+  parser->file->delimiters.head++;
+  return 0;
+}
+
+nonnull_all
+warn_unused_result
+static never_inline int32_t dont_have_contiguous_or_quoted(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  if (token->code == QUOTED || token->code < 0)
+    return token->code;
+  assert(token->code == END_OF_FILE || token->code == LINE_FEED);
+  SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type));
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t have_contiguous_or_quoted(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  if (unlikely(token->code != CONTIGUOUS))
+    return dont_have_contiguous_or_quoted(parser, type, field, token);
+  return 0;
+}
+
+nonnull_all
+warn_unused_result
+static never_inline int32_t maybe_take_contiguous_or_quoted(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  int32_t code;
+
+  for (;;) {
+    if (likely(token->code == CONTIGUOUS)) {
+      assert(*parser->file->delimiters.head > *parser->file->fields.head);
+      token->length = (uintptr_t)*parser->file->delimiters.head -
+                      (uintptr_t)*parser->file->fields.head;
+      parser->file->fields.head++;
+      parser->file->delimiters.head++;
+      return 0;
+    } else if (token->code == QUOTED) {
+      assert(*parser->file->delimiters.head > *parser->file->fields.head);
+      token->data++;
+      token->length = ((uintptr_t)*parser->file->delimiters.head -
+                       (uintptr_t)*parser->file->fields.head) - 1;
+      parser->file->fields.head++;
+      parser->file->delimiters.head++;
+      return 0;
+    } else if (token->code == END_OF_FILE) {
+      if (parser->file->end_of_file == NO_MORE_DATA)
+        SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type));
+      if ((code = advance(parser)) < 0)
+        ERROR(parser, token, code);
+    } else if (token->code == LEFT_PAREN) {
+      if (parser->file->grouped)
+        SYNTAX_ERROR(parser, token, "Nested opening brace");
+      parser->file->grouped = true;
+      parser->file->fields.head++;
+    } else if (token->code == RIGHT_PAREN) {
+      if (!parser->file->grouped)
+        SYNTAX_ERROR(parser, token, "Missing opening brace");
+      parser->file->grouped = false;
+      parser->file->fields.head++;
+    } else if (token->code == LINE_FEED) {
+      if (token->data == line_feed)
+        parser->file->span += *parser->file->newlines.head++;
+      parser->file->span++;
+      if (!parser->file->grouped)
+        SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type));
+      parser->file->fields.head++;
+    }
+    token->data = *parser->file->fields.head;
+    token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ];
+  }
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t take_contiguous_or_quoted(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  token->data = *parser->file->fields.head;
+  token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ];
+  if (likely(token->code == CONTIGUOUS)) {
+    assert(*parser->file->delimiters.head > *parser->file->fields.head);
+    token->length = (uintptr_t)*parser->file->delimiters.head -
+                    (uintptr_t)*parser->file->fields.head;
+    parser->file->fields.head++;
+    parser->file->delimiters.head++;
+    return 0;
+  } else {
+    return maybe_take_contiguous_or_quoted(parser, type, field, token);
+  }
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t take_quoted_or_contiguous(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  token_t *token)
+{
+  token->data = *parser->file->fields.head;
+  token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ];
+  if (likely(token->code == QUOTED)) {
+    assert(*parser->file->delimiters.head > *parser->file->fields.head);
+    token->data++;
+    token->length = ((uintptr_t)*parser->file->delimiters.head -
+                     (uintptr_t)*parser->file->fields.head) - 1;
+    parser->file->fields.head++;
+    parser->file->delimiters.head++;
+    return 0;
+  } else {
+    return maybe_take_contiguous_or_quoted(parser, type, field, token);
+  }
+}
+
+diagnostic_push()
+clang_diagnostic_ignored(unused-function)
+gcc_diagnostic_ignored(unused-function)
+
+nonnull_all
+warn_unused_result
+static never_inline int32_t dont_have_delimiter(
+  parser_t *parser, const type_info_t *type, token_t *token)
+{
+  if (token->code == END_OF_FILE || token->code < 0)
+    return token->code;
+  assert(token->code == CONTIGUOUS || token->code == QUOTED);
+  SYNTAX_ERROR(parser, token, "Trailing data in %s", NAME(type));
+}
+
+nonnull_all
+warn_unused_result
+static never_inline int32_t have_delimiter(
+  parser_t *parser, const type_info_t *type, token_t *token)
+{
+  if (unlikely(token->code != LINE_FEED))
+    return dont_have_delimiter(parser, type, token);
+  return 0;
+}
+
+diagnostic_pop()
+
+nonnull_all
+warn_unused_result
+static never_inline int32_t maybe_take_delimiter(
+  parser_t *parser, const type_info_t *type, token_t *token)
+{
+  int32_t code;
+
+  for (;;) {
+    if (likely(token->code == LINE_FEED)) {
+      if (unlikely(token->data == line_feed))
+        parser->file->span += *parser->file->newlines.head++;
+      if (unlikely(parser->file->grouped)) {
+        parser->file->span++;
+        parser->file->fields.head++;
+      } else {
+        token->length = 1;
+        parser->file->span++;
+        parser->file->start_of_line = classify[ (uint8_t)*(token->data+1) ] != BLANK;
+        parser->file->fields.head++;
+        return 0;
+      }
+    } else if (token->code == END_OF_FILE) {
+      if (parser->file->end_of_file == NO_MORE_DATA) {
+        if (parser->file->grouped)
+          SYNTAX_ERROR(parser, token, "Missing closing brace");
+        token->data = end_of_file;
+        token->length = 1;
+        return 0;
+      }
+
+      if ((code = advance(parser)) < 0)
+        ERROR(parser, token, code);
+    } else if (token->code == LEFT_PAREN) {
+      if (parser->file->grouped)
+        SYNTAX_ERROR(parser, token, "Nested opening brace");
+      parser->file->grouped = true;
+      parser->file->fields.head++;
+    } else if (token->code == RIGHT_PAREN) {
+      if (!parser->file->grouped)
+        SYNTAX_ERROR(parser, token, "Missing opening brace");
+      parser->file->grouped = false;
+      parser->file->fields.head++;
+    } else {
+      assert(token->code == CONTIGUOUS || token->code == QUOTED);
+      SYNTAX_ERROR(parser, token, "Trailing data in %s", NAME(type));
+    }
+    token->data = *parser->file->fields.head;
+    token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ];
+  }
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t take_delimiter(
+  parser_t *parser, const type_info_t *type, token_t *token)
+{
+  token->data = *parser->file->fields.head;
+  token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ];
+  if (likely(token->code == LINE_FEED)) {
+    if (unlikely(parser->file->grouped || token->data == line_feed))
+      return maybe_take_delimiter(parser, type, token);
+    token->length = 1;
+    parser->file->span++;
+    parser->file->start_of_line = classify[ (uint8_t)*(*parser->file->fields.head+1) ] != BLANK;
+    parser->file->fields.head++;
+    return 0;
+  } else {
+    return maybe_take_delimiter(parser, type, token);
+  }
+}
+
+#undef SYNTAX_ERROR
+#undef ERROR
+
+// define SYNTAX_ERROR for the rest of the code base
+#define SYNTAX_ERROR(parser, ...) \
+  RAISE_ERROR((parser), ZONE_SYNTAX_ERROR, __VA_ARGS__)
+
+#endif // PARSER_H
Index: simdzone/src/generic/scanner.h
===================================================================
RCS file: simdzone/src/generic/scanner.h
diff -N simdzone/src/generic/scanner.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/scanner.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,338 @@
+/*
+ * scanner.h -- fast lexical analyzer for (DNS) zone files
+ *
+ * Copyright (c) 2022, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef SCANNER_H
+#define SCANNER_H
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+// Copied from simdjson under the terms of The 3-Clause BSD License.
+// Copyright (c) 2018-2023 The simdjson authors
+static really_inline uint64_t find_escaped(
+  uint64_t backslash, uint64_t *is_escaped)
+{
+  backslash &= ~ *is_escaped;
+
+  uint64_t follows_escape = backslash << 1 | *is_escaped;
+
+  // Get sequences starting on even bits by clearing out the odd series using +
+  const uint64_t even_bits = 0x5555555555555555ULL;
+  uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape;
+  uint64_t sequences_starting_on_even_bits;
+  *is_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits);
+  uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes.
+
+  // Mask every other backslashed character as an escaped character
+  // Flip the mask for sequences that start on even bits, to correct them
+  return (even_bits ^ invert_mask) & follows_escape;
+}
+
+// special characters in zone files cannot be identified without branching
+// (unlike json) due to comments (*). no algorithm was found (so far) that
+// can correctly identify quoted and comment regions where a quoted region
+// includes a semicolon (or newline for that matter) and/or a comment region
+// includes one (or more) quote characters. also, for comments, only newlines
+// directly following a non-escaped, non-quoted semicolon must be included
+static really_inline void find_delimiters(
+  uint64_t quotes,
+  uint64_t semicolons,
+  uint64_t newlines,
+  uint64_t in_quoted,
+  uint64_t in_comment,
+  uint64_t *quoted_,
+  uint64_t *comment)
+{
+  uint64_t delimiters, starts = quotes | semicolons;
+  uint64_t end;
+
+  assert(!(quotes & semicolons));
+
+  // carry over state from previous block
+  end = (newlines & in_comment) | (quotes & in_quoted);
+  end &= -end;
+
+  delimiters = end;
+  starts &= ~((in_comment | in_quoted) ^ (-end - end));
+
+  while (starts) {
+    const uint64_t start = -starts & starts;
+    assert(start);
+    const uint64_t quote = quotes & start;
+    const uint64_t semicolon = semicolons & start;
+
+    // FIXME: technically, this introduces a data dependency
+    end = (newlines & -semicolon) | (quotes & (-quote - quote));
+    end &= -end;
+
+    delimiters |= end | start;
+    starts &= -end - end;
+  }
+
+  *quoted_ = delimiters & quotes;
+  *comment = delimiters & ~quotes;
+}
+
+static inline uint64_t follows(const uint64_t match, uint64_t *overflow)
+{
+  const uint64_t result = match << 1 | (*overflow);
+  *overflow = match >> 63;
+  return result;
+}
+
+static const simd_table_t blank = SIMD_TABLE(
+  0x20, // 0x00 :  " " : 0x20 -- space
+  0x00, // 0x01
+  0x00, // 0x02
+  0x00, // 0x03
+  0x00, // 0x04
+  0x00, // 0x05
+  0x00, // 0x06
+  0x00, // 0x07
+  0x00, // 0x08
+  0x09, // 0x09 : "\t" : 0x09 -- tab
+  0x00, // 0x0a
+  0x00, // 0x0b
+  0x00, // 0x0c
+  0x0d, // 0x0d : "\r" : 0x0d -- carriage return
+  0x00, // 0x0e
+  0x00  // 0x0f
+);
+
+static const simd_table_t special = SIMD_TABLE(
+  0x00, // 0x00 : "\0" : 0x00 -- end-of-file
+  0x00, // 0x01
+  0x00, // 0x02
+  0x00, // 0x03
+  0x00, // 0x04
+  0x00, // 0x05
+  0x00, // 0x06
+  0x00, // 0x07
+  0x28, // 0x08 :  "(" : 0x28 -- start grouped
+  0x29, // 0x09 :  ")" : 0x29 -- end grouped
+  0x0a, // 0x0a : "\n" : 0x0a -- end-of-line
+  0x00, // 0x0b
+  0x00, // 0x0c
+  0x00, // 0x0d
+  0x00, // 0x0e
+  0x00  // 0x0f
+);
+
+typedef struct block block_t;
+struct block {
+  simd_8x64_t input;
+  uint64_t newline;
+  uint64_t backslash;
+  uint64_t escaped;
+  uint64_t comment;
+  uint64_t quoted;
+  uint64_t semicolon;
+  uint64_t in_quoted;
+  uint64_t in_comment;
+  uint64_t contiguous;
+  uint64_t follows_contiguous;
+  uint64_t blank;
+  uint64_t special;
+};
+
+static really_inline void scan(parser_t *parser, block_t *block)
+{
+  // escaped newlines are classified as contiguous. however, escape sequences
+  // have no meaning in comments and newlines, escaped or not, have no
+  // special meaning in quoted
+  block->newline = simd_find_8x64(&block->input, '\n');
+  block->backslash = simd_find_8x64(&block->input, '\\');
+  block->escaped = find_escaped(
+    block->backslash, &parser->file->state.is_escaped);
+
+  block->comment = 0;
+  block->quoted = simd_find_8x64(&block->input, '"') & ~block->escaped;
+  block->semicolon = simd_find_8x64(&block->input, ';') & ~block->escaped;
+
+  block->in_quoted = parser->file->state.in_quoted;
+  block->in_comment = parser->file->state.in_comment;
+
+  if (block->in_comment || block->semicolon) {
+    find_delimiters(
+      block->quoted,
+      block->semicolon,
+      block->newline,
+      block->in_quoted,
+      block->in_comment,
+     &block->quoted,
+     &block->comment);
+
+    block->in_quoted ^= prefix_xor(block->quoted);
+    parser->file->state.in_quoted = (uint64_t)((int64_t)block->in_quoted >> 63);
+    block->in_comment ^= prefix_xor(block->comment);
+    parser->file->state.in_comment = (uint64_t)((int64_t)block->in_comment >> 63);
+  } else {
+    block->in_quoted ^= prefix_xor(block->quoted);
+    parser->file->state.in_quoted = (uint64_t)((int64_t)block->in_quoted >> 63);
+  }
+
+  block->blank =
+    simd_find_any_8x64(&block->input, blank) & ~(block->escaped | block->in_quoted | block->in_comment);
+  block->special =
+    simd_find_any_8x64(&block->input, special) & ~(block->escaped | block->in_quoted | block->in_comment);
+
+  block->contiguous =
+    ~(block->blank | block->special | block->quoted) & ~(block->in_quoted | block->in_comment);
+  block->follows_contiguous =
+    follows(block->contiguous, &parser->file->state.follows_contiguous);
+}
+
+static really_inline void write_indexes(parser_t *parser, const block_t *block, uint64_t clear)
+{
+  uint64_t fields = (block->contiguous & ~block->follows_contiguous) |
+                    (block->quoted & block->in_quoted) |
+                    (block->special);
+
+  // delimiters are only important for contigouos and quoted character strings
+  // (all other tokens automatically have a length 1). write out both in
+  // separate vectors and base logic solely on field vector, order is
+  // automatically correct
+  uint64_t delimiters = (~block->contiguous & block->follows_contiguous) |
+                        (block->quoted & ~block->in_quoted);
+
+  fields &= ~clear;
+  delimiters &= ~clear;
+
+  const char *base = parser->file->buffer.data + parser->file->buffer.index;
+  uint64_t field_count = count_ones(fields);
+  uint64_t delimiter_count = count_ones(delimiters);
+  // bulk of the data are contiguous and quoted character strings. field and
+  // delimiter counts are therefore (mostly) equal. select the greater number
+  // and write out indexes in a single loop leveraging superscalar properties
+  // of modern CPUs
+  uint64_t count = field_count;
+  if (delimiter_count > field_count)
+    count = delimiter_count;
+
+  // take slow path if (escaped) newlines appear in contiguous or quoted
+  // character strings. edge case, but must be supported and handled in the
+  // scanner for ease of use and to accommodate for parallel processing in the
+  // parser. escaped newlines may have been present in the last block
+  uint64_t newlines = block->newline & (block->contiguous | block->in_quoted);
+
+  // non-delimiting tokens may contain (escaped) newlines. tracking newlines
+  // within tokens by taping them makes the lex operation more complex, resulting
+  // in a significantly larger binary and slower operation, and may introduce an
+  // infinite loop if the tape may not be sufficiently large enough. tokens
+  // containing newlines is very much an edge case, therefore the scanner
+  // implements an unlikely slow path that tracks the number of escaped newlines
+  // during tokenization and registers them with each consecutive newline token.
+  // this mode of operation nicely isolates location tracking in the scanner and
+  // accommodates parallel processing should that ever be desired
+  if (unlikely(*parser->file->newlines.tail || newlines)) {
+    for (uint64_t i=0; i < count; i++) {
+      const uint64_t field = fields & -fields;
+      const uint64_t delimiter = delimiters & -delimiters;
+      if (field & block->newline) {
+        *parser->file->newlines.tail += count_ones(newlines & (field - 1));
+        if (*parser->file->newlines.tail) {
+          parser->file->fields.tail[i] = line_feed;
+          parser->file->newlines.tail++;
+        } else {
+          parser->file->fields.tail[i] = base + trailing_zeroes(field);
+        }
+        newlines &= -field;
+      } else {
+        parser->file->fields.tail[i] = base + trailing_zeroes(field);
+      }
+      parser->file->delimiters.tail[i] = base + trailing_zeroes(delimiter);
+      fields &= ~field;
+      delimiters &= ~delimiter;
+    }
+
+    *parser->file->newlines.tail += count_ones(newlines);
+    parser->file->fields.tail += field_count;
+    parser->file->delimiters.tail += delimiter_count;
+  } else {
+    for (uint64_t i=0; i < 6; i++) {
+      parser->file->fields.tail[i] = base + trailing_zeroes(fields);
+      parser->file->delimiters.tail[i] = base + trailing_zeroes(delimiters);
+      fields = clear_lowest_bit(fields);
+      delimiters = clear_lowest_bit(delimiters);
+    }
+
+    if (unlikely(count > 6)) {
+      for (uint64_t i=6; i < 12; i++) {
+        parser->file->fields.tail[i] = base + trailing_zeroes(fields);
+        parser->file->delimiters.tail[i] = base + trailing_zeroes(delimiters);
+        fields = clear_lowest_bit(fields);
+        delimiters = clear_lowest_bit(delimiters);
+      }
+
+      if (unlikely(count > 12)) {
+        for (uint64_t i=12; i < count; i++) {
+          parser->file->fields.tail[i] = base + trailing_zeroes(fields);
+          parser->file->delimiters.tail[i] = base + trailing_zeroes(delimiters);
+          fields = clear_lowest_bit(fields);
+          delimiters = clear_lowest_bit(delimiters);
+        }
+      }
+    }
+
+    parser->file->fields.tail += field_count;
+    parser->file->delimiters.tail += delimiter_count;
+  }
+}
+
+nonnull_all
+warn_unused_result
+static really_inline int32_t reindex(parser_t *parser)
+{
+  block_t block = { 0 };
+
+  assert(parser->file->buffer.index <= parser->file->buffer.length);
+  size_t left = parser->file->buffer.length - parser->file->buffer.index;
+  const char *data = parser->file->buffer.data + parser->file->buffer.index;
+  const char **tape = parser->file->fields.tail;
+  const char **tape_limit = parser->file->fields.tape + ZONE_TAPE_SIZE;
+
+  if (left >= ZONE_BLOCK_SIZE) {
+    const char *data_limit = parser->file->buffer.data +
+                            (parser->file->buffer.length - ZONE_BLOCK_SIZE);
+    while (data <= data_limit && ((uintptr_t)tape_limit - (uintptr_t)tape) >= ZONE_BLOCK_SIZE) {
+      simd_loadu_8x64(&block.input, (const uint8_t *)data);
+      scan(parser, &block);
+      write_indexes(parser, &block, 0);
+      parser->file->buffer.index += ZONE_BLOCK_SIZE;
+      data += ZONE_BLOCK_SIZE;
+      tape = parser->file->fields.tail;
+    }
+
+    assert(parser->file->buffer.index <= parser->file->buffer.length);
+    left = parser->file->buffer.length - parser->file->buffer.index;
+  }
+
+  // only scan partial blocks after reading all data
+  if (parser->file->end_of_file) {
+    assert(left < ZONE_BLOCK_SIZE);
+    if (!left) {
+      parser->file->end_of_file = NO_MORE_DATA;
+    } else if (((uintptr_t)tape_limit - (uintptr_t)tape) >= left) {
+      // input is required to be padded, but may contain garbage
+      uint8_t buffer[ZONE_BLOCK_SIZE] = { 0 };
+      memcpy(buffer, data, left);
+      const uint64_t clear = ~((1llu << left) - 1);
+      simd_loadu_8x64(&block.input, buffer);
+      scan(parser, &block);
+      block.contiguous &= ~clear;
+      write_indexes(parser, &block, clear);
+      parser->file->end_of_file = NO_MORE_DATA;
+      parser->file->buffer.index += left;
+    }
+  }
+
+  return (uint64_t)((int64_t)(block.contiguous | block.in_quoted) >> 63) != 0;
+}
+
+#endif // SCANNER_H
Index: simdzone/src/generic/svcb.h
===================================================================
RCS file: simdzone/src/generic/svcb.h
diff -N simdzone/src/generic/svcb.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/svcb.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,1050 @@
+/*
+ * svcb.h -- svcb (RFC9460) parser
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef SVCB_H
+#define SVCB_H
+
+#include <inttypes.h>
+
+// RFC9460 section 7.1:
+//   The "alpn" and "no-default-alpn" SvcParamKeys together indicate the set
+//   of Application-Layer Protocol Negotiation (ALPN) protocol identifiers
+//   [ALPN] and associated transport protocols supported by this service
+//   endpoint (the "SVCB ALPN set").
+//
+// RFC9460 section 7.1.1:
+//   ALPNs are identified by their registered "Identification Sequence"
+//   (alpn-id), which is a sequence of 1-255 octets. For "alpn", the
+//   presentation value SHALL be a comma-separated list (Appendix A.1) of
+//   one or more alpn-ids. Zone-file implementations MAY disallow the ","
+//   and "\" characters in ALPN IDs instead of implementing the value-list
+//   escaping procedure, relying on the opaque key format (e.g., key=\002h2)
+//   in the event that these characters are needed.
+//
+// Application-Layer Protocol Negotiation (ALPN) protocol identifiers are
+// maintained by IANA:
+//
+// https://www.iana.org/assignments/tls-extensiontype-values#alpn-protocol-ids
+//
+// RFC9460 section 7.1.1:
+//   For "alpn", the presentation value SHALL be a comma-separated list
+//   (Appendix A.1) of one or more alpn-ids. Zone-file implementations MAY
+//   disallow the "," and "\" characters in ALPN IDs instead of implementing
+//   the value-list escaping procedure, relying on the opaque key format
+//   (e.g., key1=\002h2) in the event that these characters are needed.
+//
+// RFC9460 appendix A.1:
+//   A value-list parser that splits on "," and prohibits items containing
+//   "\\" is sufficient to comply with all requirements in this document.
+//
+// RFC9460 appendix A.1:
+//   Decoding of value-lists happens after character-string decoding.
+//
+//
+//
+// RFC9460 (somewhat incorrectly) states that an SvcParamValue is a
+// character-string. An SvcParamValue is just that, an SvcParamValue. The
+// presentation format is not a context-free format like JSON. Tokens can be
+// identified, not classified, by syntax.
+//
+// Context-free languages (e.g. C, JSON) classify a token as a string if it is
+// quoted, an identifier or keyword if it is a contiguous set of characters,
+// etc. Unescaping is done by the scanner because the token is classified
+// during that stage. The presentation format defines basic syntax to identify
+// tokens, but as the format is NOT context-free and intentionally extensible,
+// the token is classified by the parser. Conversion of domain names from text
+// format to wire format is a prime example.
+//
+// Example:
+// "foo. NS bar\." defines "bar\." as a relative domain. The "\" (backslash)
+// is important because it signals that the trailing dot does not serve as a
+// label separator.
+//
+// Note that RFC9460 is contradicts itself by stating that the value-list
+// escaping procedure may rely on the opaque key format (e.g., key1=\002h2)
+// in the event that these characters are needed. Escaping using \DDD, if
+// SvcParamValue is indeed to be interpreted as a string, would produce
+// 0x03 0x02 0x68 0x32 in wire format.
+//
+// IETF mailing list discussion on this topic:
+// https://mailarchive.ietf.org/arch/msg/dnsop/SXnlsE1B8gmlDjn4HtOo1lwtqAI/
+//
+//
+// BIND disallows any escape sequences in port, ipv4hint, etc. Regular
+// (single stage) escaping rules are applied to dohpath. Special (two stage)
+// escaping rules apply for alpn.
+//
+// Knot disallows escape sequences in port, ipv4hint, etc. kzonecheck 3.3.4
+// does not to accept dohpath. Special (two stage) escaping rules apply for
+// alpn.
+nonnull_all
+static int32_t parse_alpn(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *param,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  assert(rdata->octets < rdata->limit);
+
+  (void)field;
+  (void)key;
+  (void)param;
+
+  uint8_t *comma = rdata->octets++;
+  const char *data = token->data, *limit = token->data + token->length;
+
+  while (data < limit && rdata->octets < rdata->limit) {
+    *rdata->octets = (uint8_t)*data;
+    if (unlikely(*rdata->octets == '\\')) {
+      uint32_t length;
+      if (!(length = unescape(data, rdata->octets)))
+        SYNTAX_ERROR(parser, "Invalid alpn in %s", NAME(type));
+      data += length;
+      // second level escape processing
+      if (*rdata->octets == '\\') {
+        assert(length);
+        if (*data == '\\') {
+          if (!(length = unescape(data, rdata->octets)))
+            SYNTAX_ERROR(parser, "Invalid alpn in %s", NAME(type));
+          data += length;
+        } else {
+          *rdata->octets = (uint8_t)*data;
+          data++;
+        }
+        rdata->octets++;
+        continue;
+      }
+    } else {
+      data++;
+    }
+
+    if (*rdata->octets == ',') {
+      assert(comma < rdata->octets);
+      const size_t length = ((uintptr_t)rdata->octets - (uintptr_t)comma) - 1;
+      if (!length || length > 255)
+        SYNTAX_ERROR(parser, "Invalid alpn in %s", NAME(type));
+      *comma = (uint8_t)length;
+      comma = rdata->octets;
+    }
+
+    rdata->octets++;
+  }
+
+  if (data != limit || rdata->octets > rdata->limit)
+    SYNTAX_ERROR(parser, "Invalid alpn in %s", NAME(type));
+  const size_t length = ((uintptr_t)rdata->octets - (uintptr_t)comma) - 1;
+  if (!length || length > 255)
+    SYNTAX_ERROR(parser, "Invalid alpn in %s", NAME(type));
+  *comma = (uint8_t)length;
+  return 0;
+}
+
+nonnull_all
+static int32_t parse_port(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *param,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const char *data = token->data;
+
+  (void)field;
+  (void)key;
+  (void)param;
+
+  if (!token->length || token->length > 5)
+    SYNTAX_ERROR(parser, "Invalid port in %s", NAME(type));
+
+  uint64_t number = 0;
+  for (;; data++) {
+    const uint64_t digit = (uint8_t)*data - '0';
+    if (digit > 9)
+      break;
+    number = number * 10 + digit;
+  }
+
+  uint16_t port = (uint16_t)number;
+  port = htobe16(port);
+  memcpy(rdata->octets, &port, 2);
+  rdata->octets += 2;
+
+  if (rdata->octets > rdata->limit)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  if (data != token->data + token->length || number > 65535)
+    SYNTAX_ERROR(parser, "Invalid port in %s", NAME(type));
+  return 0;
+}
+
+nonnull_all
+static int32_t parse_ipv4hint(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *param,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const char *t = token->data, *te = token->data + token->length;
+  size_t n = 0;
+
+  (void)field;
+  (void)key;
+  (void)param;
+
+  if ((n = (size_t)scan_ip4(t, rdata->octets)) == 0)
+    SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", NAME(type));
+  rdata->octets += 4;
+  t += n;
+
+  while (*t == ',') {
+    if (rdata->octets > rdata->limit)
+      SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", NAME(type));
+    if ((n = (size_t)scan_ip4(t + 1, rdata->octets)) == 0)
+      SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", NAME(type));
+    rdata->octets += 4;
+    t += n + 1;
+  }
+
+  if (t != te || rdata->octets > rdata->limit)
+    SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", NAME(type));
+  return 0;
+}
+
+nonnull_all
+static int32_t parse_ech(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *param,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  size_t size = (uintptr_t)rdata->limit - (uintptr_t)rdata->octets;
+  size_t length;
+
+  (void)field;
+  (void)key;
+  (void)param;
+
+  if (token->length / 4 > size / 3)
+    SYNTAX_ERROR(parser, "maximum size exceeded");
+
+  struct base64_state state = { .eof = 0, .bytes = 0, .carry = 0 };
+  if (!base64_stream_decode(
+    &state, token->data, token->length, rdata->octets, &length))
+    SYNTAX_ERROR(parser, "Invalid ech in %s", NAME(type));
+
+  rdata->octets += length;
+  if (state.bytes)
+    SYNTAX_ERROR(parser, "Invalid ech in %s", NAME(type));
+
+  return 0;
+}
+
+nonnull_all
+static int32_t parse_ipv6hint(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *param,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const char *t = token->data, *te = token->data + token->length;
+  size_t n = 0;
+
+  (void)field;
+  (void)key;
+  (void)param;
+
+  if ((n = (size_t)scan_ip6(t, rdata->octets)) == 0)
+    SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", NAME(type));
+  rdata->octets += 16;
+  t += n;
+
+  while (*t == ',') {
+    if (rdata->octets >= rdata->limit)
+      SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", NAME(type));
+    if ((n = (size_t)scan_ip6(t + 1, rdata->octets)) == 0)
+      SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", NAME(type));
+    rdata->octets += 16;
+    t += n + 1;
+  }
+
+  if (t != te || rdata->octets > rdata->limit)
+    SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", NAME(type));
+  return 0;
+}
+
+// RFC9461 section 5:
+//   "dohpath" is a single-valued SvcParamKey whose value (in both
+//   presentation format and wire format) MUST be a URI Template in
+//   relative form ([RFC6570], Section 1.1) encoded in UTF-8 [RFC3629].
+nonnull_all
+static int32_t parse_dohpath(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *param,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const char *t = token->data, *te = t + token->length;
+
+  (void)field;
+  (void)key;
+  (void)param;
+
+  // FIXME: easily optimized using SIMD (and possibly SWAR)
+  while ((t < te) & (rdata->octets < rdata->limit)) {
+    *rdata->octets = (uint8_t)*t;
+    if (*t == '\\') {
+      uint32_t o;
+      if (!(o = unescape(t, rdata->octets)))
+        SYNTAX_ERROR(parser, "Invalid dohpath in %s", NAME(type));
+      rdata->octets += 1; t += o;
+    } else {
+      rdata->octets += 1; t += 1;
+    }
+  }
+
+  // RFC9461 section 5:
+  //   The URI Template MUST contain a "dns" variable, and MUST be chosen such
+  //   that the result after DoH URI Template expansion (RFC8484 section 6)
+  //   is always a valid and function ":path" value (RFC9113 section 8.3.1)
+  // FIXME: implement
+
+  if (t != te || rdata->octets >= rdata->limit)
+    SYNTAX_ERROR(parser, "Invalid dohpath in %s", NAME(type));
+  return 0;
+}
+
+nonnull_all
+static int32_t parse_unknown(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *param,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const char *t = token->data, *te = t + token->length;
+
+  (void)key;
+  (void)param;
+
+  while ((t < te) & (rdata->octets < rdata->limit)) {
+    *rdata->octets = (uint8_t)*t;
+    if (*t == '\\') {
+      uint32_t o;
+      if (!(o = unescape(t, rdata->octets)))
+        SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+      rdata->octets += 1; t += o;
+    } else {
+      rdata->octets += 1; t += 1;
+    }
+  }
+
+  if (t != te || rdata->octets >= rdata->limit)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  return 0;
+}
+
+nonnull_all
+static int32_t parse_tls_supported_groups(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *param,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  const char *t = token->data, *te = token->data + token->length;
+  const uint8_t *rdata_start = rdata->octets;
+
+  (void)field;
+  (void)key;
+  (void)param;
+
+  while (t < te && rdata->octets+2 <= rdata->limit) {
+    uint64_t number = 0;
+    for (;; t++) {
+      const uint64_t digit = (uint8_t)*t - '0';
+      if (digit > 9)
+        break;
+      number = number * 10 + digit;
+    }
+
+    uint16_t group = (uint16_t)number;
+    group = htobe16(group);
+    memcpy(rdata->octets, &group, 2);
+    rdata->octets += 2;
+    if (number > 65535)
+      SYNTAX_ERROR(parser, "Invalid tls-supported-group in %s", NAME(type));
+
+    const uint8_t *g;
+    for (g = rdata_start; g < rdata->octets - 2; g += 2) {
+      if (memcmp(g, &group, 2) == 0)
+        SEMANTIC_ERROR(parser, "Duplicate group in tls-supported-groups in %s", NAME(type));
+    }
+    if (*t != ',')
+      break;
+    else
+      t++;
+  }
+
+  if (t != te || rdata->octets > rdata->limit)
+    SYNTAX_ERROR(parser, "Invalid tls-supported-groups in %s", NAME(type));
+  return 0;
+}
+
+nonnull_all
+static int32_t parse_mandatory_lax(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *svc_param,
+  rdata_t *rdata,
+  const token_t *token);
+
+nonnull_all
+static int32_t parse_mandatory(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *svc_param,
+  rdata_t *rdata,
+  const token_t *token);
+
+#define SVC_PARAM(name, key, value, parse, parse_lax) \
+  { { { name, sizeof(name) - 1 }, key }, value, parse, parse_lax }
+
+#define NO_VALUE (0u)
+#define OPTIONAL_VALUE (1u << 1)
+#define MANDATORY_VALUE (1u << 2)
+
+static const svc_param_info_t svc_params[] = {
+  // RFC9460 section 8:
+  //   The presentation value SHALL be a comma-separated list (Appendix A.1)
+  //   of one or more valid SvcParamKeys ...
+  SVC_PARAM("mandatory", 0u, MANDATORY_VALUE,
+            parse_mandatory, parse_mandatory_lax),
+  SVC_PARAM("alpn", 1u, MANDATORY_VALUE, parse_alpn, parse_alpn),
+  // RFC9460 section 7.1.1:
+  //   For "no-default-alpn", the presentation and wire format values MUST be
+  //   empty. When "no-default-alpn" is specified in an RR, "alpn" must also be
+  //   specified in order for the RR to be "self-consistent" (Section 2.4.3).
+  SVC_PARAM("no-default-alpn", 2u, NO_VALUE, parse_unknown, parse_unknown),
+  // RFC9460 section 7.2:
+  //   The presentation value of the SvcParamValue is a single decimal integer
+  //   between 0 and 65535 in ASCII. ...
+  SVC_PARAM("port", 3u, MANDATORY_VALUE, parse_port, parse_port),
+  // RFC9460 section 7.3:
+  //   The presentation value SHALL be a comma-separated list (Appendix A.1)
+  //   of one or more IP addresses ...
+  SVC_PARAM("ipv4hint", 4u, MANDATORY_VALUE, parse_ipv4hint, parse_ipv4hint),
+  SVC_PARAM("ech", 5u, OPTIONAL_VALUE, parse_ech, parse_ech),
+  // RFC9460 section 7.3:
+  //   See "ipv4hint".
+  SVC_PARAM("ipv6hint", 6u, MANDATORY_VALUE, parse_ipv6hint, parse_ipv6hint),
+  // RFC9461 section 5:
+  //   If the "alpn" SvcParam indicates support for HTTP, "dohpath" MUST be
+  //   present.
+  SVC_PARAM("dohpath", 7u, MANDATORY_VALUE, parse_dohpath, parse_dohpath),
+  // RFC9540 section 4.
+  //   Both the presentation and wire-format values for the "ohttp" parameter
+  //   MUST be empty.
+  SVC_PARAM("ohttp", 8u, NO_VALUE, parse_unknown, parse_unknown),
+  // draft-ietf-tls-key-share-prediction-01 section 3.1
+  SVC_PARAM("tls-supported-groups", 9u, MANDATORY_VALUE,
+            parse_tls_supported_groups, parse_tls_supported_groups),
+};
+
+static const svc_param_info_t unknown_svc_param =
+  SVC_PARAM("unknown", 0u, OPTIONAL_VALUE, parse_unknown, parse_unknown);
+
+#undef SVC_PARAM
+
+nonnull_all
+static really_inline size_t scan_unknown_svc_param_key(
+  const char *data, uint16_t *key, const svc_param_info_t **param)
+{
+  size_t length = 4;
+  uint32_t number = (uint8_t)data[3] - '0';
+
+  if (number > 9)
+    return 0;
+
+  uint32_t leading_zero = number == 0;
+
+  for (;; length++) {
+    const uint32_t digit = (uint8_t)data[length] - '0';
+    if (digit > 9)
+      break;
+    number = number * 10 + digit;
+  }
+
+  leading_zero &= length > 4;
+  if (leading_zero || length > 3 + 5)
+    return 0;
+  if (number < (sizeof(svc_params) / sizeof(svc_params[0])))
+    return (void)(*param = &svc_params[(*key = (uint16_t)number)]), length;
+  if (number < 65535)
+    return (void)(*key = (uint16_t)number), (void)(*param = &unknown_svc_param), length;
+  return 0;
+}
+
+nonnull_all
+static really_inline size_t scan_svc_param(
+  const char *data, uint16_t *key, const svc_param_info_t **param)
+{
+  // draft-ietf-dnsop-svcb-https-12 section 2.1:
+  // alpha-lc    = %x61-7A   ;  a-z
+  // SvcParamKey = 1*63(alpha-lc / DIGIT / "-")
+  //
+  // FIXME: naive implementation
+  if (memcmp(data, "mandatory", 9) == 0)
+    return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_MANDATORY)]), 9;
+  else if (memcmp(data, "alpn", 4) == 0)
+    return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_ALPN)]), 4;
+  else if (memcmp(data, "no-default-alpn", 15) == 0)
+    return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_NO_DEFAULT_ALPN)]), 15;
+  else if (memcmp(data, "port", 4) == 0)
+    return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_PORT)]), 4;
+  else if (memcmp(data, "ipv4hint", 8) == 0)
+    return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_IPV4HINT)]), 8;
+  else if (memcmp(data, "ech", 3) == 0)
+    return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_ECH)]), 3;
+  else if (memcmp(data, "ipv6hint", 8) == 0)
+    return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_IPV6HINT)]), 8;
+  else if (memcmp(data, "dohpath", 7) == 0)
+    return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_DOHPATH)]), 7;
+  else if (memcmp(data, "ohttp", 5) == 0)
+    return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_OHTTP)]), 5;
+  else if (memcmp(data, "tls-supported-groups", 20) == 0)
+    return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_TLS_SUPPORTED_GROUPS)]), 20;
+  else if (memcmp(data, "key", 3) == 0)
+    return scan_unknown_svc_param_key(data, key, param);
+  else
+    return 0;
+}
+
+nonnull_all
+static really_inline size_t scan_svc_param_key(
+  const char *data, uint16_t *key)
+{
+  // FIXME: improve implementation
+  const svc_param_info_t *param;
+  return scan_svc_param(data, key, &param);
+}
+
+nonnull_all
+static int32_t parse_mandatory(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *param,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  (void)field;
+  (void)param;
+
+  // RFC9460 section 8:
+  //   This SvcParamKey is always automatically mandatory and MUST NOT appear
+  //   in its own value-list. Other automatically mandatory keys SHOULD NOT
+  //   appear in the list either.
+  uint64_t mandatory = (1u << ZONE_SVC_PARAM_KEY_MANDATORY);
+  uint64_t keys = 0u;
+  int32_t highest_key = -1;
+  const char *data = token->data;
+  uint8_t *whence = rdata->octets;
+  size_t skip;
+
+  // RFC9460 section 9:
+  //   The "automatically mandatory" keys (Section 8) are "port" and
+  //   "no-default-alpn".
+  if (type->name.value == ZONE_TYPE_HTTPS)
+    mandatory = (1u << ZONE_SVC_PARAM_KEY_MANDATORY) |
+                (1u << ZONE_SVC_PARAM_KEY_NO_DEFAULT_ALPN) |
+                (1u << ZONE_SVC_PARAM_KEY_PORT);
+
+  // RFC9460 section 8:
+  //   The presentation value SHALL be a comma-seperatred list of one or more
+  //   valid SvcParamKeys, ...
+  if (!(skip = scan_svc_param_key(data, &key)))
+    SYNTAX_ERROR(parser, "Invalid mandatory in %s", NAME(type));
+  if (key < 64)
+    keys = 1llu << key;
+
+  highest_key = key;
+  key = htobe16(key);
+  memcpy(rdata->octets, &key, sizeof(key));
+  rdata->octets += sizeof(key);
+  data += skip;
+
+  while (*data == ',' && rdata->octets < rdata->limit) {
+    if (!(skip = scan_svc_param_key(data + 1, &key)))
+      SYNTAX_ERROR(parser, "Invalid mandatory of %s", NAME(type));
+
+    // check if key appears in automatically mandatory key list
+    if (key < 64)
+      keys |= 1llu << key;
+
+    data += skip + 1;
+    if (key > highest_key) {
+      highest_key = key;
+      key = htobe16(key);
+      memcpy(rdata->octets, &key, 2);
+      rdata->octets += 2;
+    } else {
+      // RFC9460 section 8:
+      //   In wire format, the keys are represented by their numeric values in
+      //   network byte order, concatenated in ascending order.
+      uint8_t *octets = whence;
+      uint16_t smaller_key = 0;
+      while (octets < rdata->octets) {
+        memcpy(&smaller_key, octets, sizeof(smaller_key));
+        smaller_key = be16toh(smaller_key);
+        if (key <= smaller_key)
+          break;
+        octets += 2;
+      }
+      assert(octets <= rdata->octets);
+      // RFC9460 section 8:
+      //   Keys MAY appear in any order, but MUST NOT appear more than once.
+      if (key == smaller_key)
+        SEMANTIC_ERROR(parser, "Duplicate key in mandatory of %s", NAME(type));
+      assert(key < smaller_key);
+      uint16_t length = (uint16_t)(rdata->octets - octets);
+      memmove(octets + 2, octets, length);
+      key = htobe16(key);
+      memcpy(octets, &key, 2);
+      rdata->octets += 2;
+    }
+  }
+
+  if (keys & mandatory)
+    SEMANTIC_ERROR(parser, "Automatically mandatory key(s) in mandatory of %s", NAME(type));
+  if (rdata->octets >= rdata->limit)
+    SYNTAX_ERROR(parser, "Invalid mandatory in %s", NAME(type));
+  if (data != token->data + token->length)
+    SYNTAX_ERROR(parser, "Invalid mandatory in %s", NAME(type));
+  return 0;
+}
+
+nonnull_all
+static int32_t parse_mandatory_lax(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *param,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  (void)field;
+
+  // RFC9460 section 8:
+  //   This SvcParamKey is always automatically mandatory and MUST NOT appear
+  //   in its own value-list. Other automatically mandatory keys SHOULD NOT
+  //   appear in the list either.
+  uint64_t mandatory = (1u << ZONE_SVC_PARAM_KEY_MANDATORY);
+  uint64_t keys = 0;
+  // RFC9460 section 8:
+  //   In wire format, the keys are represented by their numeric values in
+  //   network byte order, concatenated in strictly increasing numeric order.
+  //
+  // cannot reorder in secondary mode, print an error
+  bool out_of_order = false;
+  int32_t highest_key = -1;
+  const uint8_t *whence = rdata->octets;
+  const char *data = token->data;
+  size_t skip;
+
+  // RFC9460 section 8:
+  //   The presentation value SHALL be a comma-seperatred list of one or more
+  //   valid SvcParamKeys, ...
+  if (!(skip = scan_svc_param_key(data, &key)))
+    SYNTAX_ERROR(parser, "Invalid key in %s of %s", NAME(param), NAME(type));
+  if (key < 64)
+    keys |= 1llu << key;
+
+  // RFC9460 section 9:
+  //   The "automatically mandatory" keys (Section 8) are "port" and
+  //   "no-default-alpn".
+  if (type->name.value == ZONE_TYPE_HTTPS)
+    mandatory = (1u << ZONE_SVC_PARAM_KEY_MANDATORY) |
+                (1u << ZONE_SVC_PARAM_KEY_NO_DEFAULT_ALPN) |
+                (1u << ZONE_SVC_PARAM_KEY_PORT);
+
+  key = htobe16(key);
+  memcpy(rdata->octets, &key, 2);
+  rdata->octets += 2;
+  data += skip;
+
+  while (*data == ',' && rdata->octets < rdata->limit) {
+    if (!(skip = scan_svc_param_key(data + 1, &key)))
+      SYNTAX_ERROR(parser, "Invalid key in %s of %s", NAME(param), NAME(type));
+
+    // check if key appears in automatically mandatory key list
+    if (key < 64)
+      keys |= (1llu << key);
+
+    if ((int32_t)key <= highest_key) {
+      // RFC9460 section 8:
+      //   In wire format, the keys are represented by their numeric values in
+      //   network byte order, concatenated in ascending order.
+      const uint8_t *octets = whence;
+      uint16_t smaller_key = 0;
+      while (octets < rdata->octets) {
+        memcpy(&smaller_key, octets, sizeof(smaller_key));
+        smaller_key = be16toh(smaller_key);
+        if (key <= smaller_key)
+          break;
+        octets += 2;
+      }
+      assert(octets <= rdata->octets);
+      // RFC9460 section 8:
+      //   Keys MAY appear in any order, but MUST NOT appear more than once.
+      if (key == smaller_key)
+        SEMANTIC_ERROR(parser, "Duplicate key in mandatory of %s", NAME(type));
+      assert(key < smaller_key);
+      out_of_order = true;
+    }
+
+    data += skip + 1;
+    key = htobe16(key);
+    memcpy(rdata->octets, &key, 2);
+    rdata->octets += 2;
+  }
+
+  if (keys & mandatory)
+    SEMANTIC_ERROR(parser, "Automatically mandatory key(s) in mandatory of %s", NAME(type));
+  if (out_of_order)
+    SEMANTIC_ERROR(parser, "Out of order keys in mandatory of %s", NAME(type));
+  if (rdata->octets >= rdata->limit - 2)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  if (data != token->data + token->length)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return 0;
+}
+
+nonnull_all
+static really_inline int32_t check_mandatory(
+  zone_parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  const rdata_t *rdata,
+  const uint8_t *parameters)
+{
+  // parameters are guaranteed to be in order (automatic in strict mode)
+  if (parameters[0] || parameters[1])
+    return 0; // no mandatory parameter
+
+  uint16_t length;
+  memcpy(&length, parameters + 2, sizeof(length));
+  length = be16toh(length);
+  assert(rdata->octets - parameters >= 4 + length);
+
+  bool missing_keys = false;
+  const uint8_t *limit = parameters + 4 + length;
+  const uint8_t *keys = parameters + 4;
+  parameters += 4 + length;
+
+  assert(parameters <= rdata->octets);
+
+  for (; keys < limit; keys += 2) {
+    uint16_t key;
+    memcpy(&key, keys, sizeof(key));
+    // no byteswap, compare big endian
+
+    // mandatory is guaranteed to exist
+    if (key == 0)
+      continue;
+    if ((missing_keys = (parameters == rdata->octets)))
+      break;
+    assert(rdata->octets - parameters >= 4);
+    memcpy(&length, parameters + 2, 2);
+    length = be16toh(length);
+    assert(rdata->octets - parameters >= 4 + length);
+    // parameters are guaranteed to be sorted
+    if (memcmp(parameters, &key, 2) == 0) {
+      parameters += 4 + length;
+    } else {
+      const uint8_t *parameter = parameters + 4 + length;
+      assert(rdata->octets - parameters >= 4);
+      while (parameter < rdata->octets) {
+        if (memcmp(parameter, &key, 2) == 0)
+          break;
+        memcpy(&length, parameter + 2, 2);
+        length = be16toh(length);
+        assert(rdata->octets - parameters >= 4 + length);
+        parameter += 4 + length;
+      }
+
+      if ((missing_keys = (parameter == rdata->octets)))
+        break;
+    }
+  }
+
+  if (missing_keys)
+    SEMANTIC_ERROR(parser, "Mandatory %s missing in %s", NAME(field), NAME(type));
+  return 0;
+}
+
+nonnull((1, 2, 3, 5, 7))
+static really_inline int32_t parse_svc_param(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  uint16_t key,
+  const svc_param_info_t *param,
+  const parse_svc_param_t parse,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  switch ((token != NULL) | param->has_value) {
+    case 0: // void parameter without value
+      return 0;
+    case 1: // void parameter with value
+      assert(token);
+      SEMANTIC_ERROR(parser, "%s with value in %s", NAME(field), NAME(type));
+      if (unlikely(!token->length))
+        return 0;
+      break;
+    case 2: // parameter without optional value
+      return 0;
+    case 3: // parameter with optional value
+      assert(token);
+      if (unlikely(!token->length))
+        return 0;
+      break;
+    case 4: // parameter without value
+      SEMANTIC_ERROR(parser, "%s without value in %s", NAME(field), NAME(type));
+      return 0;
+    case 5: // parameter with value
+      assert(token);
+      if (unlikely(!token->length))
+        SEMANTIC_ERROR(parser, "%s without value in %s", NAME(field), NAME(type));
+      break;
+  }
+
+  return parse(parser, type, field, key, param, rdata, token);
+}
+
+nonnull_all
+static int32_t parse_svc_params_lax(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  token_t *token)
+{
+  bool out_of_order = false;
+  int32_t code, highest_key = -1;
+  uint32_t errors = 0;
+  const uint8_t *whence = rdata->octets;
+  uint64_t keys = 0;
+
+  // FIXME: check if parameter even fits
+  while (is_contiguous(token)) {
+    size_t count;
+    uint16_t key;
+    const svc_param_info_t *param;
+    const token_t *value = token;
+
+    if (!(count = scan_svc_param(token->data, &key, &param)))
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+    assert(param);
+
+    if (likely(key > highest_key))
+      highest_key = key;
+    else
+      out_of_order = true;
+
+    if (key < 64)
+      keys |= (1llu << key);
+
+    if (token->data[count] != '=')
+      value = NULL;
+    else if (token->data[count+1] != '"')
+      (void)(token->data += count + 1), token->length -= count + 1;
+    else if ((code = take_quoted(parser, type, field, token)) < 0)
+      return code;
+
+    uint8_t *octets = rdata->octets;
+    uint16_t length;
+    rdata->octets += 4;
+    if ((code = parse_svc_param(
+         parser, type, field, key, param, param->parse_lax, rdata, value)) < 0)
+      return code;
+
+    errors += (code != 0);
+    key = htobe16(key);
+    memcpy(octets, &key, sizeof(key));
+    assert(rdata->octets >= octets && (rdata->octets - octets) <= 65535 + 4);
+    length = (uint16_t)((rdata->octets - octets) - 4);
+    length = htobe16(length);
+    memcpy(octets + 2, &length, sizeof(length));
+    take(parser, token);
+  }
+
+  if (likely(errors == 0 && whence != rdata->octets)) {
+    assert(whence <= rdata->octets + 4);
+
+    if (unlikely(out_of_order)) {
+      SEMANTIC_ERROR(parser, "Out of order %s in %s", NAME(field), NAME(type));
+    } else { // warn about missing or out-of-order parameters
+      if (keys & 0x01)
+        check_mandatory(parser, type, field, rdata, whence);
+      // RFC9460 section 7.2:
+      //   For "no-default-alpn", the presentation and wire-format values MUST
+      //   be empty. When "no-default-alpn" is specified in an RR, "alpn" must
+      //   also be specified in order for the RR to be "self-consistent"
+      //   (Section 2.4.3).
+      if ((keys & 0x04) && !(keys & 0x02))
+        SEMANTIC_ERROR(parser, "%s with no-default-alpn but without alpn in %s",
+                       NAME(field), NAME(type));
+    }
+  }
+
+  return have_delimiter(parser, type, token);
+}
+
+// https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml
+nonnull_all
+static really_inline int32_t parse_svc_params(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  token_t *token)
+{
+  // propagate data as-is if secondary
+  if (parser->options.secondary)
+    return parse_svc_params_lax(parser, type, field, rdata, token);
+
+  int32_t code;
+  int32_t highest_key = -1;
+  uint8_t *whence = rdata->octets;
+  uint64_t keys = 0;
+
+  while (is_contiguous(token)) {
+    size_t count;
+    uint16_t key;
+    const token_t *value = token;
+    const svc_param_info_t *param;
+
+    if (!(count = scan_svc_param(token->data, &key, &param)))
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+    assert(param);
+
+    if (key < 64)
+      keys |= (1llu << key);
+
+    if (token->data[count] != '=')
+      value = NULL;
+    else if (token->data[count+1] != '"')
+      (void)(token->data += count + 1), token->length -= count + 1;
+    else if ((code = take_quoted(parser, type, field, token)) < 0)
+      return code;
+
+    uint8_t *octets;
+    uint16_t length;
+
+    if (likely(key > highest_key)) {
+      highest_key = key;
+      octets = rdata->octets;
+      rdata->octets += 4;
+      if ((code = parse_svc_param(
+           parser, type, field, key, param, param->parse, rdata, value)))
+        return code;
+      assert(rdata->octets >= octets && (rdata->octets - octets) <= 65535 + 4);
+      length = (uint16_t)((rdata->octets - octets) - 4);
+    } else {
+      octets = whence;
+      uint16_t smaller_key = 65535;
+
+      // this can probably be done in a function or something
+      while (octets < rdata->octets) {
+        memcpy(&smaller_key, octets, sizeof(smaller_key));
+        smaller_key = be16toh(smaller_key);
+        if (key <= smaller_key)
+          break;
+        memcpy(&length, octets + 2, sizeof(length));
+        length = be16toh(length);
+        octets += length + 4;
+      }
+
+      assert(octets < rdata->octets);
+      if (key == smaller_key)
+        SEMANTIC_ERROR(parser, "Duplicate key in %s", NAME(type));
+
+      rdata_t rdata_view;
+      // RFC9460 section 2.2:
+      //   SvcParamKeys SHALL appear in increasing numeric order.
+      //
+      // move existing data to end of the buffer and reset limit to
+      // avoid allocating memory
+      assert(rdata->octets - octets < ZONE_RDATA_SIZE);
+      length = (uint16_t)(rdata->octets - octets);
+      rdata_view.octets = octets + 4u;
+      rdata_view.limit = parser->rdata->octets + (ZONE_RDATA_SIZE - length);
+      // move data PADDING_SIZE past limit to ensure SIMD operatations
+      // do not overwrite existing data
+      memmove(rdata_view.limit + ZONE_BLOCK_SIZE, octets, length);
+      if ((code = parse_svc_param(
+           parser, type, field, key, param, param->parse, &rdata_view, token)))
+        return code;
+      assert(rdata_view.octets < rdata_view.limit);
+      memmove(rdata_view.octets, rdata_view.limit + ZONE_BLOCK_SIZE, length);
+      rdata->octets = rdata_view.octets + length;
+      length = (uint16_t)(rdata_view.octets - octets) - 4u;
+    }
+
+    key = htobe16(key);
+    memcpy(octets, &key, sizeof(key));
+    length = htobe16(length);
+    memcpy(octets + 2, &length, sizeof(length));
+    take(parser, token);
+  }
+
+  if ((code = have_delimiter(parser, type, token)))
+    return code;
+  assert(whence);
+  if ((keys & (1u << ZONE_SVC_PARAM_KEY_MANDATORY)) &&
+      (code = check_mandatory(parser, type, field, rdata, whence)) < 0)
+    return code;
+  // RFC9460 section 7.2:
+  //   For "no-default-alpn", the presentation and wire-format values MUST be
+  //   empty. When "no-default-alpn" is specified in an RR, "alpn" must also
+  //   be specified in order for the RR to be "self-consistent"
+  //   (Section 2.4.3).
+  if ((keys & 0x04) && !(keys & 0x02))
+    SEMANTIC_ERROR(parser, "%s with no-default-alpn but without alpn in %s",
+                   NAME(field), NAME(type));
+  return 0;
+}
+
+#endif // SVCB_H
Index: simdzone/src/generic/text.h
===================================================================
RCS file: simdzone/src/generic/text.h
diff -N simdzone/src/generic/text.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/text.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,107 @@
+/*
+ * text.h -- string parser
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef TEXT_H
+#define TEXT_H
+
+nonnull_all
+static really_inline uint32_t unescape(const char *text, uint8_t *wire)
+{
+  uint8_t d[3];
+  uint32_t o;
+
+  if ((d[0] = (uint8_t)text[1] - '0') > 9) {
+    o = (uint8_t)text[1];
+    *wire = (uint8_t)o;
+    return 2u;
+  } else {
+    d[1] = (uint8_t)text[2] - '0';
+    d[2] = (uint8_t)text[3] - '0';
+    o = d[0] * 100 + d[1] * 10 + d[2];
+    *wire = (uint8_t)o;
+    return (o > 255 || d[1] > 9 || d[2] > 9) ? 0 : 4u;
+  }
+}
+
+typedef struct string_block string_block_t;
+struct string_block {
+  uint64_t backslashes;
+};
+
+nonnull_all
+static really_inline void copy_string_block(
+  string_block_t *block, const char *text, uint8_t *wire)
+{
+  simd_8x32_t input;
+  simd_loadu_8x32(&input, text);
+  simd_storeu_8x32(wire, &input);
+  block->backslashes = simd_find_8x32(&input, '\\');
+}
+
+nonnull_all
+static really_inline int32_t scan_string(
+  const char *data,
+  size_t length,
+  uint8_t *octets,
+  const uint8_t *limit)
+{
+  const char *text = data;
+  uint8_t *wire = octets;
+  string_block_t block;
+
+  copy_string_block(&block, text, octets);
+
+  uint64_t count = 32;
+  if (length < 32)
+    count = length;
+  uint64_t mask = (1llu << count) - 1u;
+
+  // check for escape sequences
+  if (unlikely(block.backslashes & mask))
+    goto escaped;
+
+  if (length < 32)
+    return (int32_t)count;
+
+  text += count;
+  wire += count;
+  length -= count;
+
+  do {
+    copy_string_block(&block, text, wire);
+    count = 32;
+    if (length < 32)
+      count = length;
+    mask = (1llu << count) - 1u;
+
+    // check for escape sequences
+    if (unlikely(block.backslashes & mask)) {
+escaped:
+      block.backslashes &= -block.backslashes;
+      mask = block.backslashes - 1;
+      count = count_ones(mask);
+      const uint32_t octet = unescape(text+count, wire+count);
+      if (!octet)
+        return -1;
+      text += count + octet;
+      wire += count + 1;
+      length -= count + octet;
+    } else {
+      text += count;
+      wire += count;
+      length -= count;
+    }
+  } while (length && wire < limit);
+
+  if (length || (wire > limit))
+    return -1;
+  assert(!length);
+  return (int32_t)(wire - octets);
+}
+
+#endif // TEXT_H
Index: simdzone/src/generic/time.h
===================================================================
RCS file: simdzone/src/generic/time.h
diff -N simdzone/src/generic/time.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/time.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,85 @@
+/*
+ * time.h -- timestamp parser
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef TIME_H
+#define TIME_H
+
+/* number of days per month (except for February in leap years) */
+static const uint8_t days_in_month[13] = {
+  0 /* no --month */, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+static const uint16_t days_to_month[13] = {
+  0 /* no --month */, 0, 31, 59,  90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
+static uint64_t is_leap_year(uint64_t year)
+{
+  return (year % 4 == 0) & ((year % 100 != 0) | (year % 400 == 0));
+}
+
+static uint64_t leap_days(uint64_t y1, uint64_t y2)
+{
+  --y1;
+  --y2;
+  return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400);
+}
+
+nonnull_all
+static really_inline int32_t parse_time(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  if (token->length != 14)
+    return parse_int32(parser, type, field, rdata, token);
+
+  uint64_t d[14]; // YYYYmmddHHMMSS
+  const char *p = token->data;
+  for (int i = 0; i < 14; i++) {
+    d[i] = (uint8_t)p[i] - '0';
+    if (d[i] > 9)
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  }
+
+  // code adapted from Python 2.4.1 sources (Lib/calendar.py)
+  const uint64_t year = (d[0] * 1000) + (d[1] * 100) + (d[2] * 10) + d[3];
+  const uint64_t mon = (d[4] * 10) + d[5];
+  const uint64_t mday = (d[6] * 10) + d[7];
+  const uint64_t hour = (d[8] * 10) + d[9];
+  const uint64_t min = (d[10] * 10) + d[11];
+  const uint64_t sec = (d[12] * 10) + d[13];
+
+  if (year < 1970)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  uint64_t leap_year = is_leap_year(year);
+  uint64_t days = 365 * (year - 1970) + leap_days(1970, year);
+
+  if (!mon || mon > 12)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  if (!mday || mday > days_in_month[mon] + (leap_year & (mon == 2)))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  if (hour > 23 || min > 59 || sec > 59)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  days += days_to_month[mon];
+  days += (mon > 2) & leap_year;
+  days += mday - 1;
+
+  const uint64_t hours = days * 24 + hour;
+  const uint64_t minutes = hours * 60 + min;
+  const uint64_t seconds = minutes * 60 + sec;
+
+  uint32_t time = htobe32((uint32_t)seconds);
+  memcpy(rdata->octets, &time, sizeof(time));
+  rdata->octets += 4;
+  return 0;
+}
+
+#endif // TIME_H
Index: simdzone/src/generic/ttl.h
===================================================================
RCS file: simdzone/src/generic/ttl.h
diff -N simdzone/src/generic/ttl.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/ttl.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,117 @@
+/*
+ * ttl.h -- Time to Live (TTL) parser
+ *
+ * Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef TTL_H
+#define TTL_H
+
+// [sS] = 1, [mM] = 60, [hH] = 60*60, [dD] = 24*60*60, [wW] = 7*24*60*60
+static const uint32_t ttl_units[256] = {
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         // 0x00 - 0x0f
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         // 0x10 - 0x1f
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         // 0x20 - 0x2f
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         // 0x30 - 0x3f
+  0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0, // 0x40 - 0x4f
+  0, 0, 0, 1, 0, 0, 0, 604800, 0, 0, 0, 0, 0, 0, 0, 0,    // 0x50 - 0xf5
+  0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0, // 0x60 - 0x6f
+  0, 0, 0, 1, 0, 0, 0, 604800, 0, 0, 0, 0, 0, 0, 0, 0,    // 0x70 - 0x7f
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         // 0x80 - 0x8f
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         // 0x90 - 0x9f
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         // 0xa0 - 0xaf
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         // 0xb0 - 0xbf
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         // 0xc0 - 0xcf
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         // 0xd0 - 0xdf
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,         // 0xe0 - 0xef
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0          // 0xf0 - 0xff
+};
+
+nonnull_all
+static really_inline int32_t scan_ttl(
+  const char *data, size_t length, bool allow_units, uint32_t *ttl)
+{
+  if (scan_int32(data, length, ttl))
+    return 1;
+  if (!allow_units)
+    return 0;
+
+  uint64_t sum = 0, number = (uint8_t)data[0] - '0';
+  // ttls must start with a number. e.g. 1h not h1
+  if (number > 9)
+    return 0;
+
+  uint64_t unit = 0, last_unit = 0;
+  enum { NUMBER, UNIT } state = NUMBER;
+
+  for (size_t count = 1; count < length; count++) {
+    const uint64_t digit = (uint8_t)data[count] - '0';
+
+    if (state == NUMBER) {
+      if (digit < 10) {
+        number = number * 10 + digit;
+        if (number > UINT32_MAX)
+          return 0;
+      } else if (!(unit = ttl_units[ (uint8_t)data[count] ])) {
+        return 0;
+      // units must not be repeated e.g. 1m1m
+      } else if (unit == last_unit) {
+        return 0;
+      // greater units must precede smaller units. e.g. 1m1s, not 1s1m
+      } else if (unit < last_unit) {
+        return 0;
+      } else {
+        if (UINT32_MAX / unit < number)
+          return 0;
+        number *= unit;
+        if (UINT32_MAX - sum < number)
+          return 0;
+        last_unit = unit;
+        sum += number;
+        number = 0;
+        state = UNIT;
+      }
+    } else if (state == UNIT) {
+      // units must be followed by a number. e.g. 1h30m, not 1hh
+      if (digit > 9)
+        return 0;
+      // units must not be followed by a number if smallest unit,
+      // i.e. seconds, was previously specified
+      if (last_unit == 1)
+        return 0;
+      number = digit;
+      state = NUMBER;
+    }
+  }
+
+  if (UINT32_MAX - sum < number)
+    return 0;
+
+  sum += number;
+  *ttl = (uint32_t)sum;
+  return 1;
+}
+
+nonnull_all
+static really_inline int32_t parse_ttl(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  uint32_t ttl;
+  if (!scan_ttl(token->data, token->length, parser->options.pretty_ttls, &ttl))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  // FIXME: comment RFC2308 msb
+  if (ttl & (1u << 31))
+    SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  ttl = htobe32(ttl);
+  memcpy(rdata->octets, &ttl, sizeof(ttl));
+  rdata->octets += 4;
+  return 0;
+}
+
+#endif // TTL_H
Index: simdzone/src/generic/type.h
===================================================================
RCS file: simdzone/src/generic/type.h
diff -N simdzone/src/generic/type.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/type.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,205 @@
+/*
+ * type.h -- RRTYPE parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef TYPE_H
+#define TYPE_H
+
+#define V(code) { &(types[0].name),      0 }
+#define T(code) { &(types[code].name),   1 }
+#define C(code) { &(classes[code].name), 2 }
+
+// map hash to type or class descriptor (generated using hash.c)
+static const struct {
+  const mnemonic_t *mnemonic;
+  int32_t code;
+} types_and_classes[256] = {
+    V(0),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),  T(20),
+    T(3),   V(0),   V(0),   V(0),   V(0), T(261),   V(0),   V(0),
+   T(60),   V(0),   V(0), T(105),   V(0),   V(0),   V(0), T(258),
+    V(0),   V(0),   V(0),   V(0),  T(30),   V(0),  T(28),   V(0),
+    V(0),  T(16),   V(0),   V(0),  T(56),  T(14),  T(22),   V(0),
+    V(0),  T(13),   V(0),  T(47),  T(21),   V(0),  T(65),  T(27),
+    V(0),   V(0),   V(0),   V(0),   V(0),   T(1),  T(62),   V(0),
+    V(0),   C(1),   V(0),  T(44),   V(0),   V(0),  T(33),   V(0),
+    V(0),   V(0),   V(0),   V(0),  T(63),   V(0), T(266),   V(0),
+    C(3),  T(99),  T(37),   V(0),   V(0),   V(0),   C(2),  T(43),
+    V(0),  T(50),   C(4),  T(51),   V(0),   V(0),   V(0),   T(2),
+   T(49),  T(42),  T(19),  T(23),   V(0),   T(6),   V(0),   V(0),
+    V(0),   V(0),  T(29),   V(0),   T(7),   V(0),   V(0),   V(0),
+    V(0),  T(57),   V(0),   V(0),   V(0),   V(0),   V(0),  T(36),
+   T(15),   V(0),   V(0),  T(26),  T(11),   V(0),   V(0),   V(0),
+    V(0),   V(0),   V(0),   V(0), T(104),   V(0),   T(8),   V(0),
+    V(0),   V(0),  T(38),   V(0),   T(9),   V(0),  T(64),   V(0),
+    V(0),   V(0),   V(0),   V(0),   V(0),   V(0),  T(39),  T(52),
+   T(24),   V(0),   T(5), T(106),   V(0),   V(0),   V(0),   V(0),
+  T(265),   V(0),   V(0),   V(0),   V(0),  T(25),   V(0),  T(18),
+   T(48),   V(0),  T(53),   V(0),   V(0),   V(0),  T(59),   V(0),
+    V(0),   V(0),   V(0),   V(0),   T(4),   V(0),  T(10),   V(0),
+    V(0),   V(0),   V(0),   V(0),   V(0),  T(55),   V(0),   V(0),
+    V(0),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),
+    V(0),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),
+    V(0),   V(0),  T(61),  T(12),   V(0),   V(0),   V(0),   V(0),
+    V(0), T(108),   V(0),   V(0), T(257),   V(0),   V(0),   V(0),
+   T(35),   V(0), T(263),   V(0),   V(0),   V(0),   V(0), T(107),
+    V(0),   V(0),   V(0),   V(0),  T(17),   V(0),  T(45),   V(0),
+    V(0),   V(0),   V(0),   V(0),   V(0),   V(0),  T(46),   V(0),
+    V(0), T(109),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),
+    V(0),   V(0),   V(0),   V(0), T(262),   V(0), T(256),   V(0)
+};
+
+#undef V
+#undef T
+#undef C
+
+nonnull_all
+static really_inline int32_t scan_generic_type(
+  const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic)
+{
+  if (scan_int16(data + 4, length - 4, code) == 0)
+    return -1;
+  if (*code <= 258)
+    *mnemonic = &types[*code].name;
+  else if (*code == 32769)
+    *mnemonic = &types[259].name;
+  else
+    *mnemonic = &types[0].name;
+  return 1;
+}
+
+nonnull_all
+static really_inline int32_t scan_generic_class(
+  const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic)
+{
+  if (scan_int16(data + 5, length - 5, code) == 0)
+    return -1;
+  if (*code <= 4)
+    *mnemonic = &classes[*code].name;
+  else
+    *mnemonic = &classes[0].name;
+  return 2;
+}
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+# define TYPE (0x45505954llu)
+# define TYPE_MASK (0xffffffffllu)
+# define CLASS (0x5353414c43llu)
+# define CLASS_MASK (0xffffffffffllu)
+#else
+# define TYPE (0x5459504500000000llu)
+# define TYPE_MASK (0xffffffff00000000llu)
+# define CLASS (0x434c415353000000llu)
+# define CLASS_MASK (0xffffffffff000000llu)
+#endif
+
+static const int8_t zero_masks[48] = {
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
+   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0
+};
+
+static really_inline uint8_t hash(uint64_t prefix)
+{
+  prefix = le64toh(prefix);
+  uint32_t value = (uint32_t)((prefix >> 32) ^ prefix);
+  // magic value is generated using hash.c, rerun when adding types
+  return (uint8_t)((value * 3523548378ull) >> 32);
+}
+
+nonnull_all
+static really_inline int32_t scan_type_or_class(
+  const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic)
+{
+  uint64_t input0, input1;
+  static const uint64_t letter_mask = 0x4040404040404040llu;
+
+  // safe, input is padded
+  memcpy(&input0, data, 8);
+  memcpy(&input1, data + 8, 8);
+
+  // convert to upper case
+  input0 = input0 & ~((input0 & letter_mask) >> 1);
+  input1 = input1 & ~((input1 & letter_mask) >> 1);
+
+  length &= 0x1f;
+  const uint8_t *zero_mask = (const uint8_t *)&zero_masks[32 - (length & 0x1f)];
+  uint64_t zero_mask0, zero_mask1;
+
+  // sanitize input
+  memcpy(&zero_mask0, zero_mask, 8);
+  memcpy(&zero_mask1, zero_mask + 8, 8);
+
+  input0 &= zero_mask0;
+  input1 &= zero_mask1;
+
+  const uint8_t index = hash(input0);
+  *code = (uint16_t)types_and_classes[index].mnemonic->value;
+  *mnemonic = types_and_classes[index].mnemonic;
+
+  uint64_t name0, name1;
+  memcpy(&name0, (*mnemonic)->key.data, 8);
+  memcpy(&name1, (*mnemonic)->key.data + 8, 8);
+
+  if (likely(((input0 ^ name0) | (input1 ^ name1)) == 0) && *code)
+    return types_and_classes[index].code;
+  else if ((input0 & TYPE_MASK) == TYPE)
+    return scan_generic_type(data, length, code, mnemonic);
+  else if ((input0 & CLASS_MASK) == CLASS)
+    return scan_generic_class(data, length, code, mnemonic);
+  return 0;
+}
+
+nonnull_all
+static really_inline int32_t scan_type(
+  const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic)
+{
+  uint64_t input0, input1;
+  static const uint64_t letter_mask = 0x4040404040404040llu;
+
+  // safe, input is padded
+  memcpy(&input0, data, 8);
+  memcpy(&input1, data + 8, 8);
+
+  // convert to upper case
+  input0 = input0 & ~((input0 & letter_mask) >> 1);
+  input1 = input1 & ~((input1 & letter_mask) >> 1);
+
+  length &= 0x1f;
+  const uint8_t *zero_mask = (const uint8_t *)&zero_masks[32 - (length & 0x1f)];
+  uint64_t zero_mask0, zero_mask1;
+
+  // sanitize input
+  memcpy(&zero_mask0, zero_mask, 8);
+  memcpy(&zero_mask1, zero_mask + 8, 8);
+
+  input0 &= zero_mask0;
+  input1 &= zero_mask1;
+
+  const uint8_t index = hash(input0);
+  *code = (uint16_t)types_and_classes[index].mnemonic->value;
+  *mnemonic = types_and_classes[index].mnemonic;
+
+  uint64_t name0, name1;
+  memcpy(&name0, (*mnemonic)->key.data, 8);
+  memcpy(&name1, (*mnemonic)->key.data + 8, 8);
+
+  if (likely(((input0 ^ name0) | (input1 ^ name1)) == 0) && *code)
+    return types_and_classes[index].code;
+  else if ((input0 & TYPE_MASK) == TYPE)
+    return scan_generic_type(data, length, code, mnemonic);
+  return 0;
+}
+
+#undef TYPE
+#undef TYPE_MASK
+#undef CLASS
+#undef CLASS_MASK
+
+#endif // TYPE_H
Index: simdzone/src/generic/types.h
===================================================================
RCS file: simdzone/src/generic/types.h
diff -N simdzone/src/generic/types.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/types.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,3306 @@
+/*
+ * types.h
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef TYPES_H
+#define TYPES_H
+
+nonnull_all
+static really_inline int32_t scan_type_or_class(
+  const char *data,
+  size_t length,
+  uint16_t *code,
+  const mnemonic_t **mnemonic);
+
+nonnull_all
+static really_inline int32_t parse_type(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token);
+
+nonnull_all
+static really_inline int32_t parse_name(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token);
+
+nonnull_all
+static really_inline int32_t parse_string(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token);
+
+nonnull_all
+static really_inline int32_t parse_text(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token);
+
+#define FIELDS(fields) \
+  { (sizeof(fields)/sizeof(fields[0])), fields }
+
+#define FIELD(name) \
+  { { { name, sizeof(name) - 1 } } }
+
+#define CLASS(name, code) \
+  { { { name, sizeof(name) - 1 }, code } }
+
+#define UNKNOWN_CLASS(code) \
+  { { { "", 0 }, code } }
+
+#define TYPE(name, code, _class, fields, check, parse) \
+  { { { name, sizeof(name) - 1 }, code }, _class, false, false, fields, check, parse }
+
+#define UNKNOWN_TYPE(code) \
+  { { { "", 0 }, code }, 0, false, false, { 0, NULL }, check_generic_rr, parse_unknown_rdata }
+
+nonnull((1,2,3,4))
+static really_inline int32_t check_bytes(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  const uint8_t *data,
+  const size_t length,
+  const size_t size)
+{
+  (void)data;
+  if (length < size)
+    SYNTAX_ERROR(parser, "Missing %s in %s", NAME(field), NAME(type));
+  return (int32_t)size;
+}
+
+#define check_int8(...) check_bytes(__VA_ARGS__, sizeof(uint8_t))
+
+#define check_int16(...) check_bytes(__VA_ARGS__, sizeof(uint16_t))
+
+#define check_int32(...) check_bytes(__VA_ARGS__, sizeof(uint32_t))
+
+#define check_ip4(...) check_bytes(__VA_ARGS__, 4)
+
+#define check_ip6(...) check_bytes(__VA_ARGS__, 16)
+
+#define check_ilnp64(...) check_bytes(__VA_ARGS__, sizeof(uint64_t))
+
+nonnull((1,2,3,4))
+static really_inline int32_t check_ttl(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  const uint8_t *data,
+  const size_t length)
+{
+  uint32_t number;
+
+  if (length < sizeof(number))
+    SYNTAX_ERROR(parser, "Missing %s in %s", NAME(field), NAME(type));
+
+  memcpy(&number, data, sizeof(number));
+  number = be32toh(number);
+
+  if (number > INT32_MAX)
+    SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  return 4;
+}
+
+zone_nonnull((1,2,3,4))
+static really_inline int32_t check_name(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  const uint8_t *data,
+  const size_t length)
+{
+  int32_t label = 0, count = 0;
+  while (count < (int32_t)length) {
+    label = data[count];
+    count += 1 + label;
+    if (!label)
+      break;
+  }
+
+  if (!count || count > (int32_t)length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  return count;
+}
+
+zone_nonnull((1,2,3,4))
+static really_inline int32_t check_string(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  const uint8_t *data,
+  const size_t length)
+{
+  int32_t count;
+
+  if (!length || (count = 1 + (int32_t)data[0]) > (int32_t)length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  return count;
+}
+
+zone_nonnull((1,2,3,4))
+static really_inline int32_t check_nsec(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  const uint8_t *data,
+  const size_t length)
+{
+  int32_t count = 0;
+  int32_t last_window = -1;
+
+  while ((count + 2) < (int32_t)length) {
+    const int32_t window = (int32_t)data[0];
+    const int32_t blocks = (int32_t)data[1];
+    if (window <= last_window)
+      SYNTAX_ERROR(parser, "Invalid %s in %s, windows are out-of-order",
+                   NAME(field), NAME(type));
+    if (!blocks || blocks > 32)
+      SYNTAX_ERROR(parser, "Invalid %s in %s, blocks are out-of-bounds",
+                   NAME(field), NAME(type));
+    count += 2 + blocks;
+    last_window = window;
+  }
+
+  if (count != (int32_t)length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  return count;
+}
+
+zone_nonnull((1))
+static really_inline int32_t check(size_t *length, int32_t count)
+{
+  if (count < 0)
+    return count;
+  *length += (size_t)count;
+  return 0;
+}
+
+nonnull_all
+static really_inline void adjust_line_count(file_t *file)
+{
+  file->line += file->span;
+  file->span = 0;
+}
+
+nonnull_all
+static really_inline int32_t accept_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  (void)type;
+
+  assert(rdata->octets <= rdata->limit);
+  assert(rdata->octets >= parser->rdata->octets);
+  size_t length = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+
+  assert(length <= UINT16_MAX);
+  assert(parser->owner->length <= UINT8_MAX);
+  int32_t code = parser->options.accept.callback(
+    parser,
+    &(zone_name_t){ (uint8_t)parser->owner->length, parser->owner->octets },
+    parser->file->last_type,
+    parser->file->last_class,
+    *parser->file->ttl,
+    (uint16_t)length,
+    parser->rdata->octets,
+    parser->user_data);
+
+  adjust_line_count(parser->file);
+  return code;
+}
+
+nonnull_all
+static int32_t check_a_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  assert(rdata->octets >= parser->rdata->octets);
+  if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets == 4)
+    return accept_rr(parser, type, rdata);
+  SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+}
+
+nonnull_all
+static int32_t parse_a_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_ip4(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_ns_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_name(parser, type, &f[0], o, n))) < 0)
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_ns_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_soa_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_name(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_name(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_int32(parser, type, &f[2], o+c, n-c))) ||
+      (r = check(&c, check_ttl(parser, type, &f[3], o+c, n-c))) ||
+      (r = check(&c, check_ttl(parser, type, &f[4], o+c, n-c))) ||
+      (r = check(&c, check_ttl(parser, type, &f[5], o+c, n-c))) ||
+      (r = check(&c, check_ttl(parser, type, &f[6], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_soa_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_int32(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[3], token)) < 0)
+    return code;
+  if ((code = parse_ttl(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[4], token)) < 0)
+    return code;
+  if ((code = parse_ttl(parser, type, &fields[4], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[5], token)) < 0)
+    return code;
+  if ((code = parse_ttl(parser, type, &fields[5], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[6], token)) < 0)
+    return code;
+  if ((code = parse_ttl(parser, type, &fields[6], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_wks_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_ip4(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int8(parser, type, &f[0], o+c, n-c))))
+    return r;
+
+  // any bit may, or may not, be set. confirm the bitmap does not exceed the
+  // maximum number of ports
+  if (n > 8192 + 5)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_wks_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_ip4(parser, type, &fields[0], rdata, token) < 0))
+    return code;
+
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+
+  uint8_t protocol;
+  if (!scan_protocol(token->data, token->length, &protocol))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(type));
+
+  *rdata->octets++ = protocol;
+  uint8_t *bitmap = rdata->octets;
+  int32_t highest_port = -1;
+
+  take(parser, token);
+  while (is_contiguous(token)) {
+    uint16_t port;
+    if (!scan_service(token->data, token->length, protocol, &port))
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&type->rdata.fields[2]), NAME(type));
+
+    if (port > highest_port) {
+      // ensure newly used octets are zeroed out before use
+      size_t offset = highest_port < 0 ? 0 : (size_t)highest_port / 8 + 1;
+      size_t length = (size_t)port / 8 + 1;
+      memset(bitmap + offset, 0, length - offset);
+      highest_port = port;
+    }
+
+    // bits are counted from left to right, so bit 0 is the left most bit
+    bitmap[port / 8] |= (1 << (7 - port % 8));
+    take(parser, token);
+  }
+
+  rdata->octets += (size_t)highest_port / 8 + 1;
+
+  if (have_delimiter(parser, type, token) < 0)
+    return token->code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_hinfo_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_string(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_string(parser, type, &f[1], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_hinfo_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_string(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_quoted_or_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_string(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_minfo_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_name(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_name(parser, type, &f[1], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_minfo_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_mx_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_name(parser, type, &f[1], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_mx_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_txt_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_string(parser, type, &f[0], o, n))))
+    return r;
+
+  while (c < n)
+    if ((r = check(&c, check_string(parser, type, &f[0], o+c, n-c))))
+      return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_txt_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  while (is_contiguous_or_quoted(token)) {
+    if ((code = parse_string(parser, type, &fields[0], rdata, token)) < 0)
+      return code;
+    take(parser, token);
+  }
+
+  if ((code = have_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_x25_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_string(parser, type, &f[0], o, n))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_x25_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_string(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_isdn_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_string(parser, type, &f[0], o, n))))
+    return r;
+  // subaddress is optional
+  if (c < n && (r = check(&c, check_string(parser, type, &f[1], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_isdn_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_string(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+
+  // subaddress is optional
+  take(parser, token);
+  if (is_contiguous_or_quoted(token)) {
+    if ((code = parse_string(parser, type, &fields[1], rdata, token)) < 0)
+      return code;
+    take(parser, token);
+  }
+
+  if ((code = have_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_rt_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_name(parser, type, &f[1], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_rt_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_nsap_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  if (rdata->octets == parser->rdata->octets)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_nsap_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_nsap(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_nsap_ptr_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  {
+    int32_t r;
+    size_t c = 0;
+    const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+    const uint8_t *o = parser->rdata->octets;
+    const rdata_info_t *f = type->rdata.fields;
+
+    if ((r = check(&c, check_name(parser, type, &f[0], o, n))))
+      return r;
+
+    if (c != n)
+      SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  }
+
+  {
+    // RFC1706 section 6
+    // A domain name is generated from an NSAP by reversing the hex nibbles of
+    // the NSAP, treating each nibble as a separate subdomain, and appending
+    // the top-level subdomain name "NSAP.INT" to it. For example, the domain
+    // name used in the reverse lookup for the NSAP
+    //
+    //    47.0005.80.005a00.0000.0001.e133.ffffff000162.00
+    //
+    // would appear as
+    //
+    //    0.0.2.6.1.0.0.0.f.f.f.f.f.f.3.3.1.e.1.0.0.0.0.0.0.0.0.0.a.5.0.0.
+    //                        0.8.5.0.0.0.7.4.NSAP.INT.
+    size_t i = 0;
+    const size_t n = parser->file->owner.length;
+    const uint8_t *o = parser->file->owner.octets;
+    for (; i < n; i += 2)
+      if (o[i] != 1 || (base16_table_dec_32bit_d1[o[i+1]] > 0xff))
+        break;
+
+    const uint8_t nsap_int[] = { 4, 'n', 's', 'a', 'p', 3, 'i', 'n', 't', 0 };
+    if (strncasecmp((const char *)o + i, (const char *)nsap_int, 9) != 0 || !i || i + 10 != n)
+      SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  }
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_nsap_ptr_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+
+  // RFC1706 section 6 states each nibble is treated as a separate subdomain
+  return check_nsap_ptr_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_key_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+#if 0
+  int32_t r;
+  size_t c = 0;
+  const size_t n = parser->rdata->length;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  //
+  // FIXME: implement (RFC2065)
+  //
+  // FIXME: verify the flag, algorithm and protocol combination is valid
+  // FIXME: verify the key is valid for type(3)+algorithm(1)
+  //
+  // The combination is of course subject to secondary checks!
+  //
+#endif
+  (void)type;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_key_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_base64_sequence(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+
+  return check_key_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_px_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_name(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_name(parser, type, &f[2], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s record", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_px_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_gpos_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_string(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_string(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_string(parser, type, &f[2], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s record", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+static int32_t parse_gpos_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_latitude(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_longitude(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_altitude(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_aaaa_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_ip6(parser, type, &f[0], o, n))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s record", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_aaaa_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_ip6(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_loc_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets != 16)
+    SYNTAX_ERROR(parser, "Invalid %s record", NAME(type));
+  return accept_rr(parser, type, rdata);
+
+  // FIXME: check validity of latitude, longitude and latitude?
+}
+
+nonnull_all
+static int32_t parse_loc_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  uint32_t degrees, minutes, seconds;
+  uint32_t latitude, longitude, altitude;
+  const rdata_info_t *fields = type->rdata.fields;
+  static const uint8_t defaults[4] = { 0x00, 0x12, 0x16, 0x13 };
+
+  // RFC1876 section 3:
+  // If omitted, minutes and seconds default to zero, size defaults to 1m,
+  // horizontal precision defaults to 10000m, and vertical precision defaults
+  // to 10m.
+  memcpy(rdata->octets, &defaults, sizeof(defaults));
+
+  // latitude
+  if ((code = have_contiguous(parser, type, &fields[4], token)) < 0)
+    return code;
+  if (scan_degrees(token->data, token->length, &degrees) == -1)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[4]), NAME(type));
+  if ((code = take_contiguous(parser, type, &fields[4], token)) < 0)
+    return code;
+  if (scan_minutes(token->data, token->length, &minutes) == -1)
+    goto north_south; // minutes default to zero
+  degrees += minutes;
+  if ((code = take_contiguous(parser, type, &fields[4], token)) < 0)
+    return code;
+  if (scan_seconds(token->data, token->length, &seconds) == -1)
+    goto north_south; // seconds default to zero
+  degrees += seconds;
+
+  if ((code = take_contiguous(parser, type, &fields[4], token)) < 0)
+    return code;
+north_south:
+  if (token->data[0] == 'N')
+    latitude = htobe32((1u<<31) + degrees);
+  else if (token->data[0] == 'S')
+    latitude = htobe32((1u<<31) - degrees);
+  else
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[4]), NAME(type));
+
+  memcpy(&rdata->octets[4], &latitude, sizeof(latitude));
+
+  // longitude
+  if ((code = take_contiguous(parser, type, &fields[5], token)) < 0)
+    return code;
+  if (scan_degrees(token->data, token->length, &degrees) == -1)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[5]), NAME(type));
+  if ((code = take_contiguous(parser, type, &fields[5], token)) < 0)
+    return code;
+  if (scan_minutes(token->data, token->length, &minutes) == -1)
+    goto east_west; // minutes default to zero
+  degrees += minutes;
+  if ((code = take_contiguous(parser, type, &fields[5], token)) < 0)
+    return code;
+  if (scan_seconds(token->data, token->length, &seconds) == -1)
+    goto east_west; // seconds default to zero
+  degrees += seconds;
+
+  if ((code = take_contiguous(parser, type, &fields[5], token)) < 0)
+    return code;
+east_west:
+  if (token->data[0] == 'E')
+    longitude = htobe32((1u<<31) + degrees);
+  else if (token->data[0] == 'W')
+    longitude = htobe32((1u<<31) - degrees);
+  else
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[5]), NAME(type));
+
+  memcpy(&rdata->octets[8], &longitude, sizeof(longitude));
+
+  // altitude
+  if ((code = take_contiguous(parser, type, &fields[6], token)) < 0)
+    return code;
+  if (scan_altitude(token->data, token->length, &altitude) == -1)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[6]), NAME(type));
+
+  altitude = htobe32(altitude);
+  memcpy(&rdata->octets[12], &altitude, sizeof(altitude));
+
+  // size
+  take(parser, token);
+  if (!is_contiguous(token))
+    goto skip_optional;
+  if (scan_precision(token->data, token->length, &rdata->octets[1]))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(type));
+
+  // horizontal precision
+  take(parser, token);
+  if (!is_contiguous(token))
+    goto skip_optional;
+  if (scan_precision(token->data, token->length, &rdata->octets[2]))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[2]), NAME(type));
+
+  // vertical precision
+  take(parser, token);
+  if (!is_contiguous(token))
+    goto skip_optional;
+  if (scan_precision(token->data, token->length, &rdata->octets[3]))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(type));
+
+  take(parser, token);
+skip_optional:
+  if ((code = have_delimiter(parser, type, token)) < 0)
+    return code;
+
+  rdata->octets += 16;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_nxt_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_name(parser, type, &f[0], o+c, n-c))))
+    return r;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_nxt_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_nxt(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_srv_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int16(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_int16(parser, type, &f[2], o+c, n-c))) ||
+      (r = check(&c, check_name(parser, type, &f[3], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_srv_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[3], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_naptr_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  // FIXME: implement actual checks
+  (void)type;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_naptr_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_quoted_or_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_string(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  if ((code = take_quoted_or_contiguous(parser, type, &fields[3], token)) < 0)
+    return code;
+  if ((code = parse_string(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+  if ((code = take_quoted_or_contiguous(parser, type, &fields[4], token)) < 0)
+    return code;
+  if ((code = parse_string(parser, type, &fields[4], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[5], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[5], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_cert_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  // FIXME: implement actual checks
+  (void)type;
+
+  assert(rdata->octets >= parser->rdata->octets);
+  if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets < 6)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_cert_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_certificate_type(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_algorithm(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_base64_sequence(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_apl_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  // FIXME: check correctness of fields and total length
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_apl_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  // RDATA section for APL consists of zero or more fields
+  while (is_contiguous(token)) {
+    int32_t length;
+    const size_t size = (uintptr_t)rdata->limit - (uintptr_t)rdata->octets;
+    if ((length = scan_apl(token->data, token->length, rdata->octets, size)) < 0)
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), NAME(type));
+    assert(length == 8 /* ipv4 */ || length == 20 /* ipv6 */);
+    rdata->octets += (size_t)length;
+    take(parser, token);
+  }
+
+  if ((code = have_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_ds_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c))))
+    return r;
+
+  const uint8_t digest_algorithm = parser->rdata->octets[3];
+
+  if ((digest_algorithm & 0x7) == digest_algorithm) {
+    // https://www.iana.org/assignments/ds-rr-types
+    static const uint8_t digest_sizes[8] = {
+      0,  // 0: Reserved
+      20, // 1: SHA-1
+      32, // 2: SHA-256
+      32, // 3: GOST R 34.11-94
+      48, // 4: SHA-384
+      48, // 5: GOST R 34.10-2012
+      48, // 6: SM3
+      0   // 7: Unassigned
+    };
+
+    const uint8_t digest_size = digest_sizes[ digest_algorithm ];
+
+    if (digest_size && n - 4 != digest_size)
+      SEMANTIC_ERROR(parser, "Invalid digest in %s", NAME(type));
+  }
+
+  if (c >= n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_ds_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_algorithm(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_base16_sequence(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+
+  const uint8_t digest_algorithm = parser->rdata->octets[3];
+
+  if ((digest_algorithm & 0x7) == digest_algorithm) {
+    // https://www.iana.org/assignments/ds-rr-types
+    static const uint8_t digest_sizes[8] = {
+      0,  // 0: Reserved
+      20, // 1: SHA-1
+      32, // 2: SHA-256
+      32, // 3: GOST R 34.11-94
+      48, // 4: SHA-384
+      48, // 5: GOST R 34.10-2012
+      48, // 6: SM3
+      0   // 7: Unassigned
+    };
+
+    const uint8_t digest_size = digest_sizes[ digest_algorithm ];
+    size_t length = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+
+    if (digest_size && length - 4 != digest_size)
+      SEMANTIC_ERROR(parser, "Invalid digest in %s", NAME(type));
+  }
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_sshfp_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))))
+    return r;
+
+  // https://www.iana.org/assignments/dns-sshfp-rr-parameters
+
+  if (c == n)
+    SYNTAX_ERROR(parser, "Missing %s in %s", NAME((&f[n!=0])), NAME(type));
+  else if (o[1] == 1 && (n - c) != 20)
+    SEMANTIC_ERROR(parser, "Wrong fingerprint size for type %s in %s",
+                           "SHA1", NAME(type));
+  else if (o[1] == 2 && (n - c) != 32)
+    SEMANTIC_ERROR(parser, "Wrong fingerprint size for type %s in %s",
+                           "SHA256", NAME(type));
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_sshfp_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+
+  const uint8_t *fingerprint_type = rdata->octets;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+
+  const uint8_t *fingerprint = rdata->octets;
+  take(parser, token);
+  if ((code = parse_base16_sequence(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+
+  // https://www.iana.org/assignments/dns-sshfp-rr-parameters
+  size_t fingerprint_size = (uintptr_t)rdata->octets - (uintptr_t)fingerprint;
+  if (unlikely(*fingerprint_type == 1 && fingerprint_size != 20))
+    SEMANTIC_ERROR(parser, "Wrong fingerprint size for type %s in %s",
+                           "SHA1", NAME(type));
+  if (unlikely(*fingerprint_type == 2 && fingerprint_size != 32))
+    SEMANTIC_ERROR(parser, "Wrong fingerprint size for type %s in %s",
+                           "SHA256", NAME(type));
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_ipseckey_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata);
+
+nonnull_all
+static int32_t parse_ipseckey_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token);
+
+diagnostic_push()
+gcc_diagnostic_ignored(missing-field-initializers)
+clang_diagnostic_ignored(missing-field-initializers)
+
+static const rdata_info_t ipseckey_ipv4_rdata_fields[] = {
+  FIELD("precedence"),
+  FIELD("gateway type"),
+  FIELD("algorithm"),
+  FIELD("gateway"),
+  FIELD("public key")
+};
+
+static const type_info_t ipseckey_ipv4[] = {
+  TYPE("IPSECKEY", ZONE_TYPE_IPSECKEY, ZONE_CLASS_IN, FIELDS(ipseckey_ipv4_rdata_fields),
+                   check_ipseckey_rr, parse_ipseckey_rdata),
+};
+
+static const rdata_info_t ipseckey_ipv6_rdata_fields[] = {
+  FIELD("precedence"),
+  FIELD("gateway type"),
+  FIELD("algorithm"),
+  FIELD("gateway"),
+  FIELD("public key")
+};
+
+static const type_info_t ipseckey_ipv6[] = {
+  TYPE("IPSECKEY", ZONE_TYPE_IPSECKEY, ZONE_CLASS_IN, FIELDS(ipseckey_ipv6_rdata_fields),
+                   check_ipseckey_rr, parse_ipseckey_rdata),
+};
+
+diagnostic_pop()
+
+nonnull_all
+static int32_t check_ipseckey_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const type_info_t *t = type;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c))))
+    return r;
+
+  switch (parser->rdata->octets[1]) {
+    case 1: /* IPv4 address */
+      t = (const type_info_t *)ipseckey_ipv4;
+      f = ipseckey_ipv4_rdata_fields;
+      if ((r = check(&c, check_ip4(parser, t, &f[3], o+c, n-c))) < 0)
+        return r;
+      break;
+    case 2: /* IPv6 address */
+      t = (const type_info_t *)ipseckey_ipv6;
+      f = ipseckey_ipv6_rdata_fields;
+      if ((r = check(&c, check_ip6(parser, t, &f[3], o+c, n-c))) < 0)
+        return r;
+      break;
+    case 0: /* no gateway */
+      break;
+    case 3: /* domain name */
+      if ((r = check(&c, check_name(parser, t, &f[3], o+c, n-c))) < 0)
+        return r;
+      break;
+    default:
+      SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  }
+
+  switch (parser->rdata->octets[2]) {
+    case 0:
+      if (c < n)
+        SYNTAX_ERROR(parser, "Trailing data in %s", NAME(t));
+      break;
+    default:
+      if (c >= n)
+        SYNTAX_ERROR(parser, "Missing %s in %s", NAME(&f[4]), NAME(t));
+      break;
+  }
+
+  return accept_rr(parser, t, rdata);
+}
+
+nonnull_all
+static int32_t parse_ipseckey_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+  uint8_t *octets = rdata->octets;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[3], token)) < 0)
+    return code;
+
+  switch (octets[1]) {
+    case 0: /* no gateway */
+      if (token->length != 1 || token->data[0] != '.')
+        SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(type));
+      break;
+    case 1: /* IPv4 address */
+      type = (const type_info_t *)ipseckey_ipv4;
+      fields = type->rdata.fields;
+      if ((code = parse_ip4(parser, type, &fields[3], rdata, token)) < 0)
+        return code;
+      break;
+    case 2: /* IPv6 address */
+      type = (const type_info_t *)ipseckey_ipv6;
+      fields = type->rdata.fields;
+      if ((code = parse_ip6(parser, type, &fields[3], rdata, token)) < 0)
+        return code;
+      break;
+    case 3: /* domain name */
+      if ((code = parse_name(parser, type, &fields[3], rdata, token)) < 0)
+        return code;
+      break;
+    default:
+      SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(type));
+  }
+
+  take(parser, token);
+  switch (octets[2]) {
+    case 0:
+      if ((code = have_delimiter(parser, type, token)) < 0)
+        return code;
+      break;
+    default:
+      if ((code = parse_base64_sequence(parser, type, &fields[4], rdata, token)) < 0)
+        return code;
+      break;
+  }
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_rrsig_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c))) ||
+      (r = check(&c, check_ttl(parser, type, &f[3], o+c, n-c))) ||
+      (r = check(&c, check_int32(parser, type, &f[4], o+c, n-c))) ||
+      (r = check(&c, check_int32(parser, type, &f[5], o+c, n-c))) ||
+      (r = check(&c, check_int16(parser, type, &f[6], o+c, n-c))) ||
+      (r = check(&c, check_name(parser, type, &f[7], o+c, n-c))))
+    return r;
+
+  if (c > n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_rrsig_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_type(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_algorithm(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[3], token)) < 0)
+    return code;
+  if ((code = parse_ttl(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[4], token)) < 0)
+    return code;
+  if ((code = parse_time(parser, type, &fields[4], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[5], token)) < 0)
+    return code;
+  if ((code = parse_time(parser, type, &fields[5], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[6], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[6], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[7], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[7], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_base64_sequence(parser, type, &fields[8], rdata, token)) < 0)
+    return code;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_nsec_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_name(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_nsec(parser, type, &f[1], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_nsec_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_nsec(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_dnskey_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c))))
+    return r;
+
+  if (c >= n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_dnskey_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_algorithm(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_base64_sequence(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_dhcid_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  // RFC4701 section 3.1:
+  // 2-octet identifier type, 1-octet digest type, followed by one or more
+  // octets representing the actual identifier
+  if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets < 4)
+    SEMANTIC_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_dhcid_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = parse_base64_sequence(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+
+  return check_dhcid_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_nsec3_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_int16(parser, type, &f[2], o+c, n-c))) ||
+      (r = check(&c, check_string(parser, type, &f[3], o+c, n-c))) ||
+      (r = check(&c, check_string(parser, type, &f[4], o+c, n-c))) ||
+      (r = check(&c, check_nsec(parser, type, &f[5], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_nsec3_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[3], token)) < 0)
+    return code;
+  if ((code = parse_salt(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[4], token)) < 0)
+    return code;
+  if ((code = parse_base32(parser, type, &fields[4], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_nsec(parser, type, &fields[5], rdata, token)) < 0)
+    return code;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_nsec3param_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_int16(parser, type, &f[2], o+c, n-c))) ||
+      (r = check(&c, check_string(parser, type, &f[3], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_nsec3param_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[3], token)) < 0)
+    return code;
+  if ((code = parse_salt(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_tlsa_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c))))
+    return r;
+
+  if (c >= n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_tlsa_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_base16_sequence(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_hip_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  // FIXME: verify field lengths etc
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_hip_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+  uint8_t *octets = rdata->octets;
+
+  // reserve octet for HIT length
+  rdata->octets += 1;
+
+  // PK algorithm
+  if ((code = have_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+
+  // reserve octets for PK length
+  rdata->octets += 2;
+
+  // HIT
+  if ((code = take_contiguous(parser, type, &fields[3], token)) < 0)
+    return code;
+  if ((code = parse_base16(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+
+  if ((rdata->octets - octets) > 255 + 4)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(type));
+  uint8_t hit_length = (uint8_t)((rdata->octets - octets) - 4);
+  octets[0] = hit_length;
+
+  // Public Key
+  if ((code = take_contiguous(parser, type, &fields[4], token)) < 0)
+    return code;
+  if ((code = parse_base64(parser, type, &fields[4], rdata, token)) < 0)
+    return code;
+
+  uint16_t pk_length = htobe16((uint16_t)(((rdata->octets - octets) - hit_length) - 4));
+  memcpy(&octets[2], &pk_length, sizeof(pk_length));
+
+  take(parser, token);
+  while (is_contiguous(token)) {
+    if ((code = parse_name(parser, type, &fields[5], rdata, token)) < 0)
+      return code;
+    take(parser, token);
+  }
+
+  if ((code = have_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_openpgpkey_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  // FIXME: as the RDATA contains a digest, it is likely we can make this
+  //        check stricter, at least, for known algorithms
+  if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets < 4)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_openpgpkey_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = parse_base64_sequence(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_csync_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int32(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int16(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_nsec(parser, type, &f[2], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_csync_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int32(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_nsec(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_zonemd_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int32(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) ||
+      (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c))))
+    return r;
+
+  const uint8_t digest_algorithm = parser->rdata->octets[5];
+  if ((digest_algorithm & 0x3) == digest_algorithm) {
+    // https://www.iana.org/assignments/dns-parameters#zonemd-hash-algorithms
+    static const uint8_t digest_sizes[4] = {
+      0,  // 0: Reserved
+      48, // 1: SHA-384
+      64, // 2: SHA-512
+      0   // 3: Unassigned
+    };
+
+    const uint8_t digest_size = digest_sizes[ digest_algorithm ];
+    if (digest_size && n - 6 != digest_size)
+      SEMANTIC_ERROR(parser, "Invalid digest in %s", NAME(type));
+  }
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_zonemd_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int32(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_base16_sequence(parser, type, &fields[3], rdata, token)) < 0)
+    return code;
+
+  const uint8_t digest_algorithm = parser->rdata->octets[5];
+  if ((digest_algorithm & 0x3) == digest_algorithm) {
+    // https://www.iana.org/assignments/dns-parameters#zonemd-hash-algorithms
+    static const uint8_t digest_sizes[4] = {
+      0,  // 0: Reserved
+      48, // 1: SHA-384
+      64, // 2: SHA-512
+      0   // 3: Unassigned
+    };
+
+    const uint8_t digest_size = digest_sizes[ digest_algorithm ];
+    size_t length = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+    if (digest_size && length - 6 != digest_size)
+      SEMANTIC_ERROR(parser, "Invalid digest in %s", NAME(type));
+  }
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_svcb_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  //
+  // FIXME: implement checking parameters etc
+  //
+  // - check if all keys in mandatory exist
+  // - check if at least keys and key lengths are valid
+  //
+  // FIXME: implement reordering parameters in strict (primary) mode
+  // FIXME: note that when reordering or checking, rdata may not actually
+  //        contain valid parameters
+  //
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_svcb_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_svc_params(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_https_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  //
+  // FIXME: incorporate fixes mentioned in check_svcb_rr
+  //
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_https_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  take(parser, token);
+  if ((code = parse_svc_params(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_nid_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_ilnp64(parser, type, &f[1], o+c, n-c))))
+    return r;
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_nid_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_ilnp64(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_l32_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_ip4(parser, type, &f[1], o+c, n-c))))
+    return r;
+
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_l32_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_ip4(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_l64_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_ilnp64(parser, type, &f[1], o+c, n-c))))
+    return r;
+  if (c != n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_l64_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_ilnp64(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_eui48_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets != 6)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_eui48_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_eui48(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_eui64_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets != 8)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_eui64_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_eui64(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_uri_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int16(parser, type, &f[1], o+c, n-c))))
+    return r;
+  if (c >= n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_uri_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_int16(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_quoted(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_text(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_caa_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  int32_t r;
+  size_t c = 0;
+  const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets;
+  const uint8_t *o = parser->rdata->octets;
+  const rdata_info_t *f = type->rdata.fields;
+
+  if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) ||
+      (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))))
+    return r;
+  if (c >= n)
+    SYNTAX_ERROR(parser, "Invalid %s", NAME(type));
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_caa_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  const rdata_info_t *fields = type->rdata.fields;
+
+  if ((code = have_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0)
+    return code;
+  if ((code = take_contiguous(parser, type, &fields[1], token)) < 0)
+    return code;
+  if ((code = parse_caa_tag(parser, type, &fields[1], rdata, token)) < 0)
+    return code;
+  if ((code = take_quoted_or_contiguous(parser, type, &fields[2], token)) < 0)
+    return code;
+  if ((code = parse_text(parser, type, &fields[2], rdata, token)) < 0)
+    return code;
+  if ((code = take_delimiter(parser, type, token)) < 0)
+    return code;
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t check_generic_rr(
+  parser_t *parser, const type_info_t *type, const rdata_t *rdata)
+{
+  return accept_rr(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_generic_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  int32_t code;
+  uint16_t rdlength;
+  static const rdata_info_t fields[] = {
+    FIELD("rdlength"),
+    FIELD("rdata")
+  };
+
+  // discard '\#'
+  if ((code = take_contiguous(parser, type, &fields[0], token)) < 0)
+    return code;
+  if (!scan_int16(token->data, token->length, &rdlength))
+    SYNTAX_ERROR(parser, "Invalid RDLENGTH in %s", NAME(type));
+
+  take(parser, token);
+  if (is_contiguous(token)) {
+    struct base16_state state = { .eof = 0, .bytes = 0, .carry = 0 };
+
+    do {
+      size_t length = token->length + 1 / 2;
+      if (length > (uintptr_t)rdata->limit - (uintptr_t)rdata->octets)
+        SYNTAX_ERROR(parser, "Invalid RDATA in %s", NAME(type));
+      if (!base16_stream_decode(&state, token->data, token->length, rdata->octets, &length))
+        SYNTAX_ERROR(parser, "Invalid RDATA in %s", NAME(type));
+      rdata->octets += length;
+      take(parser, token);
+    } while (is_contiguous(token));
+
+    if (state.bytes)
+      *rdata->octets++ = state.carry;
+  }
+
+  if ((code = have_delimiter(parser, type, token)) < 0)
+    return code;
+  if (rdata->octets - parser->rdata->octets != rdlength)
+    SYNTAX_ERROR(parser, "Invalid RDATA in %s", NAME(type));
+  return type->check(parser, type, rdata);
+}
+
+nonnull_all
+static int32_t parse_unknown_rdata(
+  parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token)
+{
+  (void)type;
+  (void)rdata;
+  (void)token;
+  SYNTAX_ERROR(parser, "Unknown record type");
+}
+
+diagnostic_push()
+gcc_diagnostic_ignored(missing-field-initializers)
+clang_diagnostic_ignored(missing-field-initializers)
+
+static const class_info_t classes[] = {
+  UNKNOWN_CLASS(0),
+  CLASS("IN", ZONE_CLASS_IN),
+  CLASS("CS", ZONE_CLASS_CS),
+  CLASS("CH", ZONE_CLASS_CH),
+  CLASS("HS", ZONE_CLASS_HS)
+};
+
+static const rdata_info_t a_rdata_fields[] = {
+  FIELD("address")
+};
+
+static const rdata_info_t ns_rdata_fields[] = {
+  FIELD("host")
+};
+
+static const rdata_info_t md_rdata_fields[] = {
+  FIELD("madname")
+};
+
+static const rdata_info_t mf_rdata_fields[] = {
+  FIELD("madname")
+};
+
+static const rdata_info_t cname_rdata_fields[] = {
+  FIELD("host")
+};
+
+static const rdata_info_t soa_rdata_fields[] = {
+  FIELD("primary"),
+  FIELD("mailbox"),
+  FIELD("serial"),
+  FIELD("refresh"),
+  FIELD("retry"),
+  FIELD("expire"),
+  FIELD("minimum")
+};
+
+static const rdata_info_t mb_rdata_fields[] = {
+  FIELD("madname")
+};
+
+static const rdata_info_t mg_rdata_fields[] = {
+  FIELD("mgmname")
+};
+
+static const rdata_info_t mr_rdata_fields[] = {
+  FIELD("newname")
+};
+
+static const rdata_info_t ptr_rdata_fields[] = {
+  FIELD("ptrdname")
+};
+
+static const rdata_info_t hinfo_rdata_fields[] = {
+  FIELD("cpu"),
+  FIELD("os")
+};
+
+static const rdata_info_t minfo_rdata_fields[] = {
+  FIELD("rmailbx"),
+  FIELD("emailbx")
+};
+
+static const rdata_info_t null_rdata_fields[] = {
+  FIELD("anything")
+};
+
+static const rdata_info_t wks_rdata_fields[] = {
+  FIELD("address"),
+  FIELD("protocol"),
+  FIELD("bitmap")
+};
+
+static const rdata_info_t mx_rdata_fields[] = {
+  FIELD("priority"),
+  FIELD("hostname")
+};
+
+static const rdata_info_t txt_rdata_fields[] = {
+  FIELD("text")
+};
+
+static const rdata_info_t rp_rdata_fields[] = {
+  FIELD("mailbox"),
+  FIELD("text")
+};
+
+static const rdata_info_t afsdb_rdata_fields[] = {
+  FIELD("subtype"),
+  FIELD("hostname")
+};
+
+static const rdata_info_t x25_rdata_fields[] = {
+  FIELD("address")
+};
+
+static const rdata_info_t isdn_rdata_fields[] = {
+  FIELD("address"),
+  FIELD("subaddress")
+};
+
+static const rdata_info_t rt_rdata_fields[] = {
+  FIELD("preference"),
+  FIELD("hostname")
+};
+
+static const rdata_info_t nsap_rdata_fields[] = {
+  FIELD("address")
+};
+
+static const rdata_info_t nsap_ptr_rdata_fields[] = {
+  FIELD("hostname")
+};
+
+static const rdata_info_t key_rdata_fields[] = {
+  FIELD("flags"),
+  FIELD("protocol"),
+  FIELD("algorithm"),
+  FIELD("publickey")
+};
+
+static const rdata_info_t px_rdata_fields[] = {
+  FIELD("preference"),
+  FIELD("map822"),
+  FIELD("mapx400")
+};
+
+static const rdata_info_t gpos_rdata_fields[] = {
+  FIELD("latitude"),
+  FIELD("longitude"),
+  FIELD("altitude")
+};
+
+static const rdata_info_t aaaa_rdata_fields[] = {
+  FIELD("address")
+};
+
+static const rdata_info_t loc_rdata_fields[] = {
+  FIELD("version"),
+  FIELD("size"),
+  FIELD("horizontal precision"),
+  FIELD("vertical precision"),
+  FIELD("latitude"),
+  FIELD("longitude"),
+  FIELD("altitude")
+};
+
+static const rdata_info_t nxt_rdata_fields[] = {
+  FIELD("next domain name"),
+  FIELD("type bit map")
+};
+
+static const rdata_info_t srv_rdata_fields[] = {
+  FIELD("priority"),
+  FIELD("weight"),
+  FIELD("port"),
+  FIELD("target")
+};
+
+static const rdata_info_t naptr_rdata_fields[] = {
+  FIELD("order"),
+  FIELD("preference"),
+  FIELD("flags"),
+  FIELD("services"),
+  FIELD("regex"),
+  FIELD("replacement"),
+};
+
+static const rdata_info_t kx_rdata_fields[] = {
+  FIELD("preference"),
+  FIELD("exchanger")
+};
+
+static const rdata_info_t sig_rdata_fields[] = {
+  FIELD("sigtype"),
+  FIELD("algorithm"),
+  FIELD("labels"),
+  FIELD("origttl"),
+  FIELD("expire"),
+  FIELD("inception"),
+  FIELD("keytag"),
+  FIELD("signer"),
+  FIELD("signature")
+};
+
+static const rdata_info_t cert_rdata_fields[] = {
+  FIELD("type"),
+  FIELD("key tag"),
+  FIELD("algorithm"),
+  FIELD("certificate")
+};
+
+static const rdata_info_t dname_rdata_fields[] = {
+  FIELD("source")
+};
+
+static const rdata_info_t apl_rdata_fields[] = {
+  FIELD("prefix")
+};
+
+static const rdata_info_t ds_rdata_fields[] = {
+  FIELD("keytag"),
+  FIELD("algorithm"),
+  FIELD("digtype"),
+  FIELD("digest")
+};
+
+static const rdata_info_t sshfp_rdata_fields[] = {
+  FIELD("algorithm"),
+  FIELD("ftype"),
+  FIELD("fingerprint")
+};
+
+// IPSECKEY is different because the rdata depends on the algorithm
+static const rdata_info_t ipseckey_rdata_fields[] = {
+  FIELD("precedence"),
+  FIELD("gateway type"),
+  FIELD("algorithm"),
+  FIELD("gateway"),
+  FIELD("public key")
+};
+
+static const rdata_info_t rrsig_rdata_fields[] = {
+  FIELD("rrtype"),
+  FIELD("algorithm"),
+  FIELD("labels"),
+  FIELD("origttl"),
+  FIELD("expire"),
+  FIELD("inception"),
+  FIELD("keytag"),
+  FIELD("signer"),
+  FIELD("signature")
+};
+
+static const rdata_info_t nsec_rdata_fields[] = {
+  FIELD("next"),
+  FIELD("types")
+};
+
+static const rdata_info_t dnskey_rdata_fields[] = {
+  FIELD("flags"),
+  FIELD("protocol"),
+  FIELD("algorithm"),
+  FIELD("publickey")
+};
+
+static const rdata_info_t dhcid_rdata_fields[] = {
+  FIELD("dhcpinfo")
+};
+
+static const rdata_info_t nsec3_rdata_fields[] = {
+  FIELD("algorithm"),
+  FIELD("flags"),
+  FIELD("iterations"),
+  FIELD("salt"),
+  FIELD("next"),
+  FIELD("types")
+};
+
+static const rdata_info_t nsec3param_rdata_fields[] = {
+  FIELD("algorithm"),
+  FIELD("flags"),
+  FIELD("iterations"),
+  FIELD("salt")
+};
+
+static const rdata_info_t tlsa_rdata_fields[] = {
+  FIELD("usage"),
+  FIELD("selector"),
+  FIELD("matching type"),
+  FIELD("certificate association data")
+};
+
+static const rdata_info_t smimea_rdata_fields[] = {
+  FIELD("usage"),
+  FIELD("selector"),
+  FIELD("matching type"),
+  FIELD("certificate association data")
+};
+
+static const rdata_info_t cds_rdata_fields[] = {
+  FIELD("keytag"),
+  FIELD("algorithm"),
+  FIELD("digtype"),
+  FIELD("digest")
+};
+
+static const rdata_info_t cdnskey_rdata_fields[] = {
+  FIELD("flags"),
+  FIELD("protocol"),
+  FIELD("algorithm"),
+  FIELD("publickey")
+};
+
+static const rdata_info_t hip_rdata_fields[] = {
+  FIELD("HIT length"),
+  FIELD("PK algorithm"),
+  FIELD("PK length"),
+  FIELD("HIT"),
+  FIELD("Public Key"),
+  FIELD("Rendezvous Servers")
+};
+
+// https://www.iana.org/assignments/dns-parameters/NINFO/ninfo-completed-template
+static const rdata_info_t ninfo_rdata_fields[] = {
+  FIELD("text")
+};
+
+// https://www.iana.org/assignments/dns-parameters/RKEY/rkey-completed-template
+static const rdata_info_t rkey_rdata_fields[] = {
+  FIELD("flags"),
+  FIELD("protocol"),
+  FIELD("algorithm"),
+  FIELD("publickey")
+};
+
+static const rdata_info_t openpgpkey_rdata_fields[] = {
+  FIELD("key")
+};
+
+static const rdata_info_t csync_rdata_fields[] = {
+  FIELD("serial"),
+  FIELD("flags"),
+  FIELD("types")
+};
+
+static const rdata_info_t zonemd_rdata_fields[] = {
+  FIELD("serial"),
+  FIELD("scheme"),
+  FIELD("algorithm"),
+  FIELD("digest"),
+};
+
+static const rdata_info_t svcb_rdata_fields[] = {
+  FIELD("priority"),
+  FIELD("target"),
+  FIELD("params")
+};
+
+static const rdata_info_t https_rdata_fields[] = {
+  FIELD("priority"),
+  FIELD("target"),
+  FIELD("params")
+};
+
+static const rdata_info_t spf_rdata_fields[] = {
+  FIELD("text")
+};
+
+static const rdata_info_t nid_rdata_fields[] = {
+  FIELD("preference"),
+  FIELD("nodeid")
+};
+
+// RFC6742 specifies the syntax for the locator is compatible with the syntax
+// for IPv4 addresses, but then proceeds to provide an example with leading
+// zeroes. The example is corrected in the errata.
+static const rdata_info_t l32_rdata_fields[] = {
+  FIELD("preference"),
+  FIELD("locator")
+};
+
+static const rdata_info_t l64_rdata_fields[] = {
+  FIELD("preference"),
+  FIELD("locator")
+};
+
+static const rdata_info_t lp_rdata_fields[] = {
+  FIELD("preference"),
+  FIELD("pointer")
+};
+
+static const rdata_info_t eui48_rdata_fields[] = {
+  FIELD("address")
+};
+
+static const rdata_info_t eui64_rdata_fields[] = {
+  FIELD("address")
+};
+
+static const rdata_info_t uri_rdata_fields[] = {
+  FIELD("priority"),
+  FIELD("weight"),
+  FIELD("target")
+};
+
+static const rdata_info_t caa_rdata_fields[] = {
+  FIELD("flags"),
+  FIELD("tag"),
+  FIELD("value")
+};
+
+// https://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template
+static const rdata_info_t avc_rdata_fields[] = {
+  FIELD("text")
+};
+
+// RFC 9606
+static const rdata_info_t resinfo_rdata_fields[] = {
+  FIELD("text")
+};
+
+// https://www.iana.org/assignments/dns-parameters/WALLET/wallet-completed-template
+static const rdata_info_t wallet_rdata_fields[] = {
+  FIELD("text")
+};
+
+// https://www.iana.org/assignments/dns-parameters/CLA/cla-completed-template
+static const rdata_info_t cla_rdata_fields[] = {
+  FIELD("text")
+};
+
+static const rdata_info_t ta_rdata_fields[] = {
+  FIELD("key"),
+  FIELD("algorithm"),
+  FIELD("type"),
+  FIELD("digest")
+};
+
+static const rdata_info_t dlv_rdata_fields[] = {
+  FIELD("key"),
+  FIELD("algorithm"),
+  FIELD("type"),
+  FIELD("digest")
+};
+
+// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
+static const type_info_t types[] = {
+  UNKNOWN_TYPE(0),
+
+  TYPE("A", ZONE_TYPE_A, ZONE_CLASS_ANY, FIELDS(a_rdata_fields),
+            check_a_rr, parse_a_rdata),
+  TYPE("NS", ZONE_TYPE_NS, ZONE_CLASS_ANY, FIELDS(ns_rdata_fields),
+             check_ns_rr, parse_ns_rdata),
+  TYPE("MD", ZONE_TYPE_MD, ZONE_CLASS_ANY, FIELDS(md_rdata_fields), // obsolete
+             check_ns_rr, parse_ns_rdata),
+  TYPE("MF", ZONE_TYPE_MF, ZONE_CLASS_ANY, FIELDS(mf_rdata_fields), // obsolete
+             check_ns_rr, parse_ns_rdata),
+  TYPE("CNAME", ZONE_TYPE_CNAME, ZONE_CLASS_ANY, FIELDS(cname_rdata_fields),
+                check_ns_rr, parse_ns_rdata),
+  TYPE("SOA", ZONE_TYPE_SOA, ZONE_CLASS_ANY, FIELDS(soa_rdata_fields),
+              check_soa_rr, parse_soa_rdata),
+  TYPE("MB", ZONE_TYPE_MB, ZONE_CLASS_ANY, FIELDS(mb_rdata_fields), // experimental
+             check_ns_rr, parse_ns_rdata),
+  TYPE("MG", ZONE_TYPE_MG, ZONE_CLASS_ANY, FIELDS(mg_rdata_fields), // experimental
+             check_ns_rr, parse_ns_rdata),
+  TYPE("MR", ZONE_TYPE_MR, ZONE_CLASS_ANY, FIELDS(mr_rdata_fields), // experimental
+             check_ns_rr, parse_ns_rdata),
+  TYPE("NULL", ZONE_TYPE_NULL, ZONE_CLASS_ANY, FIELDS(null_rdata_fields), // experimetal
+               check_generic_rr, parse_unknown_rdata),
+  TYPE("WKS", ZONE_TYPE_WKS, ZONE_CLASS_IN, FIELDS(wks_rdata_fields),
+              check_wks_rr, parse_wks_rdata),
+  TYPE("PTR", ZONE_TYPE_PTR, ZONE_CLASS_ANY, FIELDS(ptr_rdata_fields),
+              check_ns_rr, parse_ns_rdata),
+  TYPE("HINFO", ZONE_TYPE_HINFO, ZONE_CLASS_ANY, FIELDS(hinfo_rdata_fields),
+                check_hinfo_rr, parse_hinfo_rdata),
+  TYPE("MINFO", ZONE_TYPE_MINFO, ZONE_CLASS_ANY, FIELDS(minfo_rdata_fields),
+                check_minfo_rr, parse_minfo_rdata),
+  TYPE("MX", ZONE_TYPE_MX, ZONE_CLASS_ANY, FIELDS(mx_rdata_fields),
+             check_mx_rr, parse_mx_rdata),
+  TYPE("TXT", ZONE_TYPE_TXT, ZONE_CLASS_ANY, FIELDS(txt_rdata_fields),
+              check_txt_rr, parse_txt_rdata),
+  TYPE("RP", ZONE_TYPE_RP, ZONE_CLASS_ANY, FIELDS(rp_rdata_fields),
+             check_minfo_rr, parse_minfo_rdata),
+  TYPE("AFSDB", ZONE_TYPE_AFSDB, ZONE_CLASS_ANY, FIELDS(afsdb_rdata_fields),
+                check_mx_rr, parse_mx_rdata),
+  TYPE("X25", ZONE_TYPE_X25, ZONE_CLASS_ANY, FIELDS(x25_rdata_fields),
+              check_x25_rr, parse_x25_rdata),
+  TYPE("ISDN", ZONE_TYPE_ISDN, ZONE_CLASS_ANY, FIELDS(isdn_rdata_fields),
+               check_isdn_rr, parse_isdn_rdata),
+  TYPE("RT", ZONE_TYPE_RT, ZONE_CLASS_ANY, FIELDS(rt_rdata_fields),
+             check_rt_rr, parse_rt_rdata),
+  TYPE("NSAP", ZONE_TYPE_NSAP, ZONE_CLASS_IN, FIELDS(nsap_rdata_fields),
+               check_nsap_rr, parse_nsap_rdata),
+  TYPE("NSAP-PTR", ZONE_TYPE_NSAP_PTR, ZONE_CLASS_IN, FIELDS(nsap_ptr_rdata_fields),
+                   check_nsap_ptr_rr, parse_nsap_ptr_rdata),
+  TYPE("SIG", ZONE_TYPE_SIG, ZONE_CLASS_ANY, FIELDS(sig_rdata_fields),
+              check_rrsig_rr, parse_rrsig_rdata),
+  TYPE("KEY", ZONE_TYPE_KEY, ZONE_CLASS_ANY, FIELDS(key_rdata_fields),
+              check_key_rr, parse_key_rdata),
+  TYPE("PX", ZONE_TYPE_PX, ZONE_CLASS_IN, FIELDS(px_rdata_fields),
+             check_px_rr, parse_px_rdata),
+  TYPE("GPOS", ZONE_TYPE_GPOS, ZONE_CLASS_ANY, FIELDS(gpos_rdata_fields),
+               check_gpos_rr, parse_gpos_rdata),
+  TYPE("AAAA", ZONE_TYPE_AAAA, ZONE_CLASS_IN, FIELDS(aaaa_rdata_fields),
+               check_aaaa_rr, parse_aaaa_rdata),
+  TYPE("LOC", ZONE_TYPE_LOC, ZONE_CLASS_ANY, FIELDS(loc_rdata_fields),
+              check_loc_rr, parse_loc_rdata),
+  TYPE("NXT", ZONE_TYPE_NXT, ZONE_CLASS_ANY, FIELDS(nxt_rdata_fields), // obsolete
+              check_nxt_rr, parse_nxt_rdata),
+
+  UNKNOWN_TYPE(31),
+  UNKNOWN_TYPE(32),
+
+  TYPE("SRV", ZONE_TYPE_SRV, ZONE_CLASS_IN, FIELDS(srv_rdata_fields),
+              check_srv_rr, parse_srv_rdata),
+
+  UNKNOWN_TYPE(34),
+
+  TYPE("NAPTR", ZONE_TYPE_NAPTR, ZONE_CLASS_IN, FIELDS(naptr_rdata_fields),
+                check_naptr_rr, parse_naptr_rdata),
+  TYPE("KX", ZONE_TYPE_KX, ZONE_CLASS_IN, FIELDS(kx_rdata_fields),
+             check_mx_rr, parse_mx_rdata),
+  TYPE("CERT", ZONE_TYPE_CERT, ZONE_CLASS_ANY, FIELDS(cert_rdata_fields),
+               check_cert_rr, parse_cert_rdata),
+
+  UNKNOWN_TYPE(38),
+
+  TYPE("DNAME", ZONE_TYPE_DNAME, ZONE_CLASS_ANY, FIELDS(dname_rdata_fields),
+                check_ns_rr, parse_ns_rdata),
+
+  UNKNOWN_TYPE(40),
+  UNKNOWN_TYPE(41),
+
+  TYPE("APL", ZONE_TYPE_APL, ZONE_CLASS_IN, FIELDS(apl_rdata_fields),
+             check_apl_rr, parse_apl_rdata),
+  TYPE("DS", ZONE_TYPE_DS, ZONE_CLASS_ANY, FIELDS(ds_rdata_fields),
+             check_ds_rr, parse_ds_rdata),
+  TYPE("SSHFP", ZONE_TYPE_SSHFP, ZONE_CLASS_ANY, FIELDS(sshfp_rdata_fields),
+                check_sshfp_rr, parse_sshfp_rdata),
+  TYPE("IPSECKEY", ZONE_TYPE_IPSECKEY, ZONE_CLASS_IN, FIELDS(ipseckey_rdata_fields),
+                   check_ipseckey_rr, parse_ipseckey_rdata),
+  TYPE("RRSIG", ZONE_TYPE_RRSIG, ZONE_CLASS_ANY, FIELDS(rrsig_rdata_fields),
+                check_rrsig_rr, parse_rrsig_rdata),
+  TYPE("NSEC", ZONE_TYPE_NSEC, ZONE_CLASS_ANY, FIELDS(nsec_rdata_fields),
+               check_nsec_rr, parse_nsec_rdata),
+  TYPE("DNSKEY", ZONE_TYPE_DNSKEY, ZONE_CLASS_ANY, FIELDS(dnskey_rdata_fields),
+                 check_dnskey_rr, parse_dnskey_rdata),
+  TYPE("DHCID", ZONE_TYPE_DHCID, ZONE_CLASS_IN, FIELDS(dhcid_rdata_fields),
+                check_dhcid_rr, parse_dhcid_rdata),
+  TYPE("NSEC3", ZONE_TYPE_NSEC3, ZONE_CLASS_ANY, FIELDS(nsec3_rdata_fields),
+                check_nsec3_rr, parse_nsec3_rdata),
+  TYPE("NSEC3PARAM", ZONE_TYPE_NSEC3PARAM, ZONE_CLASS_ANY, FIELDS(nsec3param_rdata_fields),
+                     check_nsec3param_rr, parse_nsec3param_rdata),
+  TYPE("TLSA", ZONE_TYPE_TLSA, ZONE_CLASS_ANY, FIELDS(tlsa_rdata_fields),
+               check_tlsa_rr, parse_tlsa_rdata),
+  TYPE("SMIMEA", ZONE_TYPE_SMIMEA, ZONE_CLASS_ANY, FIELDS(smimea_rdata_fields),
+                 check_tlsa_rr, parse_tlsa_rdata),
+
+  UNKNOWN_TYPE(54),
+
+  TYPE("HIP", ZONE_TYPE_HIP, ZONE_CLASS_ANY, FIELDS(hip_rdata_fields),
+              check_hip_rr, parse_hip_rdata),
+  TYPE("NINFO", ZONE_TYPE_NINFO, ZONE_CLASS_ANY, FIELDS(ninfo_rdata_fields),
+              check_txt_rr, parse_txt_rdata),
+  TYPE("RKEY", ZONE_TYPE_RKEY, ZONE_CLASS_ANY, FIELDS(rkey_rdata_fields),
+                 check_dnskey_rr, parse_dnskey_rdata),
+
+  UNKNOWN_TYPE(58),
+
+  TYPE("CDS", ZONE_TYPE_CDS, ZONE_CLASS_ANY, FIELDS(cds_rdata_fields),
+              check_ds_rr, parse_ds_rdata),
+  TYPE("CDNSKEY", ZONE_TYPE_CDNSKEY, ZONE_CLASS_ANY, FIELDS(cdnskey_rdata_fields),
+                  check_dnskey_rr, parse_dnskey_rdata),
+  TYPE("OPENPGPKEY", ZONE_TYPE_OPENPGPKEY, ZONE_CLASS_ANY, FIELDS(openpgpkey_rdata_fields),
+                     check_openpgpkey_rr, parse_openpgpkey_rdata),
+  TYPE("CSYNC", ZONE_TYPE_CSYNC, ZONE_CLASS_ANY, FIELDS(csync_rdata_fields),
+                check_csync_rr, parse_csync_rdata),
+  TYPE("ZONEMD", ZONE_TYPE_ZONEMD, ZONE_CLASS_ANY, FIELDS(zonemd_rdata_fields),
+                 check_zonemd_rr, parse_zonemd_rdata),
+  TYPE("SVCB", ZONE_TYPE_SVCB, ZONE_CLASS_IN, FIELDS(svcb_rdata_fields),
+               check_svcb_rr, parse_svcb_rdata),
+  TYPE("HTTPS", ZONE_TYPE_HTTPS, ZONE_CLASS_IN, FIELDS(https_rdata_fields),
+                check_https_rr, parse_https_rdata),
+
+  UNKNOWN_TYPE(66),
+  UNKNOWN_TYPE(67),
+  UNKNOWN_TYPE(68),
+  UNKNOWN_TYPE(69),
+  UNKNOWN_TYPE(70),
+  UNKNOWN_TYPE(71),
+  UNKNOWN_TYPE(72),
+  UNKNOWN_TYPE(73),
+  UNKNOWN_TYPE(74),
+  UNKNOWN_TYPE(75),
+  UNKNOWN_TYPE(76),
+  UNKNOWN_TYPE(77),
+  UNKNOWN_TYPE(78),
+  UNKNOWN_TYPE(79),
+  UNKNOWN_TYPE(80),
+  UNKNOWN_TYPE(81),
+  UNKNOWN_TYPE(82),
+  UNKNOWN_TYPE(83),
+  UNKNOWN_TYPE(84),
+  UNKNOWN_TYPE(85),
+  UNKNOWN_TYPE(86),
+  UNKNOWN_TYPE(87),
+  UNKNOWN_TYPE(88),
+  UNKNOWN_TYPE(89),
+  UNKNOWN_TYPE(90),
+  UNKNOWN_TYPE(91),
+  UNKNOWN_TYPE(92),
+  UNKNOWN_TYPE(93),
+  UNKNOWN_TYPE(94),
+  UNKNOWN_TYPE(95),
+  UNKNOWN_TYPE(96),
+  UNKNOWN_TYPE(97),
+  UNKNOWN_TYPE(98),
+
+  TYPE("SPF", ZONE_TYPE_SPF, ZONE_CLASS_ANY, FIELDS(spf_rdata_fields), // obsolete
+              check_txt_rr, parse_txt_rdata),
+
+  UNKNOWN_TYPE(100),
+  UNKNOWN_TYPE(101),
+  UNKNOWN_TYPE(102),
+  UNKNOWN_TYPE(103),
+
+  TYPE("NID", ZONE_TYPE_NID, ZONE_CLASS_ANY, FIELDS(nid_rdata_fields),
+              check_nid_rr, parse_nid_rdata),
+  TYPE("L32", ZONE_TYPE_L32, ZONE_CLASS_ANY, FIELDS(l32_rdata_fields),
+              check_l32_rr, parse_l32_rdata),
+  TYPE("L64", ZONE_TYPE_L64, ZONE_CLASS_ANY, FIELDS(l64_rdata_fields),
+              check_l64_rr, parse_l64_rdata),
+  TYPE("LP", ZONE_TYPE_LP, ZONE_CLASS_ANY, FIELDS(lp_rdata_fields),
+             check_mx_rr, parse_mx_rdata),
+  TYPE("EUI48", ZONE_TYPE_EUI48, ZONE_CLASS_ANY, FIELDS(eui48_rdata_fields),
+                check_eui48_rr, parse_eui48_rdata),
+  TYPE("EUI64", ZONE_TYPE_EUI64, ZONE_CLASS_ANY, FIELDS(eui64_rdata_fields),
+                check_eui64_rr, parse_eui64_rdata),
+
+  UNKNOWN_TYPE(110),
+  UNKNOWN_TYPE(111),
+  UNKNOWN_TYPE(112),
+  UNKNOWN_TYPE(113),
+  UNKNOWN_TYPE(114),
+  UNKNOWN_TYPE(115),
+  UNKNOWN_TYPE(116),
+  UNKNOWN_TYPE(117),
+  UNKNOWN_TYPE(118),
+  UNKNOWN_TYPE(119),
+  UNKNOWN_TYPE(120),
+  UNKNOWN_TYPE(121),
+  UNKNOWN_TYPE(122),
+  UNKNOWN_TYPE(123),
+  UNKNOWN_TYPE(124),
+  UNKNOWN_TYPE(125),
+  UNKNOWN_TYPE(126),
+  UNKNOWN_TYPE(127),
+  UNKNOWN_TYPE(128),
+  UNKNOWN_TYPE(129),
+  UNKNOWN_TYPE(130),
+  UNKNOWN_TYPE(131),
+  UNKNOWN_TYPE(132),
+  UNKNOWN_TYPE(133),
+  UNKNOWN_TYPE(134),
+  UNKNOWN_TYPE(135),
+  UNKNOWN_TYPE(136),
+  UNKNOWN_TYPE(137),
+  UNKNOWN_TYPE(138),
+  UNKNOWN_TYPE(139),
+  UNKNOWN_TYPE(140),
+  UNKNOWN_TYPE(141),
+  UNKNOWN_TYPE(142),
+  UNKNOWN_TYPE(143),
+  UNKNOWN_TYPE(144),
+  UNKNOWN_TYPE(145),
+  UNKNOWN_TYPE(146),
+  UNKNOWN_TYPE(147),
+  UNKNOWN_TYPE(148),
+  UNKNOWN_TYPE(149),
+  UNKNOWN_TYPE(150),
+  UNKNOWN_TYPE(151),
+  UNKNOWN_TYPE(152),
+  UNKNOWN_TYPE(153),
+  UNKNOWN_TYPE(154),
+  UNKNOWN_TYPE(155),
+  UNKNOWN_TYPE(156),
+  UNKNOWN_TYPE(157),
+  UNKNOWN_TYPE(158),
+  UNKNOWN_TYPE(159),
+  UNKNOWN_TYPE(160),
+  UNKNOWN_TYPE(161),
+  UNKNOWN_TYPE(162),
+  UNKNOWN_TYPE(163),
+  UNKNOWN_TYPE(164),
+  UNKNOWN_TYPE(165),
+  UNKNOWN_TYPE(166),
+  UNKNOWN_TYPE(167),
+  UNKNOWN_TYPE(168),
+  UNKNOWN_TYPE(169),
+  UNKNOWN_TYPE(170),
+  UNKNOWN_TYPE(171),
+  UNKNOWN_TYPE(172),
+  UNKNOWN_TYPE(173),
+  UNKNOWN_TYPE(174),
+  UNKNOWN_TYPE(175),
+  UNKNOWN_TYPE(176),
+  UNKNOWN_TYPE(177),
+  UNKNOWN_TYPE(178),
+  UNKNOWN_TYPE(179),
+  UNKNOWN_TYPE(180),
+  UNKNOWN_TYPE(181),
+  UNKNOWN_TYPE(182),
+  UNKNOWN_TYPE(183),
+  UNKNOWN_TYPE(184),
+  UNKNOWN_TYPE(185),
+  UNKNOWN_TYPE(186),
+  UNKNOWN_TYPE(187),
+  UNKNOWN_TYPE(188),
+  UNKNOWN_TYPE(189),
+  UNKNOWN_TYPE(190),
+  UNKNOWN_TYPE(191),
+  UNKNOWN_TYPE(192),
+  UNKNOWN_TYPE(193),
+  UNKNOWN_TYPE(194),
+  UNKNOWN_TYPE(195),
+  UNKNOWN_TYPE(196),
+  UNKNOWN_TYPE(197),
+  UNKNOWN_TYPE(198),
+  UNKNOWN_TYPE(199),
+  UNKNOWN_TYPE(200),
+  UNKNOWN_TYPE(201),
+  UNKNOWN_TYPE(202),
+  UNKNOWN_TYPE(203),
+  UNKNOWN_TYPE(204),
+  UNKNOWN_TYPE(205),
+  UNKNOWN_TYPE(206),
+  UNKNOWN_TYPE(207),
+  UNKNOWN_TYPE(208),
+  UNKNOWN_TYPE(209),
+  UNKNOWN_TYPE(210),
+  UNKNOWN_TYPE(211),
+  UNKNOWN_TYPE(212),
+  UNKNOWN_TYPE(213),
+  UNKNOWN_TYPE(214),
+  UNKNOWN_TYPE(215),
+  UNKNOWN_TYPE(216),
+  UNKNOWN_TYPE(217),
+  UNKNOWN_TYPE(218),
+  UNKNOWN_TYPE(219),
+  UNKNOWN_TYPE(220),
+  UNKNOWN_TYPE(221),
+  UNKNOWN_TYPE(222),
+  UNKNOWN_TYPE(223),
+  UNKNOWN_TYPE(224),
+  UNKNOWN_TYPE(225),
+  UNKNOWN_TYPE(226),
+  UNKNOWN_TYPE(227),
+  UNKNOWN_TYPE(228),
+  UNKNOWN_TYPE(229),
+  UNKNOWN_TYPE(230),
+  UNKNOWN_TYPE(231),
+  UNKNOWN_TYPE(232),
+  UNKNOWN_TYPE(233),
+  UNKNOWN_TYPE(234),
+  UNKNOWN_TYPE(235),
+  UNKNOWN_TYPE(236),
+  UNKNOWN_TYPE(237),
+  UNKNOWN_TYPE(238),
+  UNKNOWN_TYPE(239),
+  UNKNOWN_TYPE(240),
+  UNKNOWN_TYPE(241),
+  UNKNOWN_TYPE(242),
+  UNKNOWN_TYPE(243),
+  UNKNOWN_TYPE(244),
+  UNKNOWN_TYPE(245),
+  UNKNOWN_TYPE(246),
+  UNKNOWN_TYPE(247),
+  UNKNOWN_TYPE(248),
+  UNKNOWN_TYPE(249),
+  UNKNOWN_TYPE(250),
+  UNKNOWN_TYPE(251),
+  UNKNOWN_TYPE(252),
+  UNKNOWN_TYPE(253),
+  UNKNOWN_TYPE(254),
+  UNKNOWN_TYPE(255),
+
+  TYPE("URI", ZONE_TYPE_URI, ZONE_CLASS_ANY, FIELDS(uri_rdata_fields),
+              check_uri_rr, parse_uri_rdata),
+  TYPE("CAA", ZONE_TYPE_CAA, ZONE_CLASS_ANY, FIELDS(caa_rdata_fields),
+              check_caa_rr, parse_caa_rdata),
+  TYPE("AVC", ZONE_TYPE_AVC, ZONE_CLASS_ANY, FIELDS(avc_rdata_fields),
+              check_txt_rr, parse_txt_rdata),
+
+  UNKNOWN_TYPE(259),
+  UNKNOWN_TYPE(260),
+
+  TYPE("RESINFO", ZONE_TYPE_RESINFO, ZONE_CLASS_ANY, FIELDS(resinfo_rdata_fields),
+              check_txt_rr, parse_txt_rdata),
+  TYPE("WALLET", ZONE_TYPE_WALLET, ZONE_CLASS_ANY, FIELDS(wallet_rdata_fields),
+              check_txt_rr, parse_txt_rdata),
+  TYPE("CLA", ZONE_TYPE_CLA, ZONE_CLASS_ANY, FIELDS(cla_rdata_fields),
+              check_txt_rr, parse_txt_rdata),
+
+  UNKNOWN_TYPE(264),
+
+  /* Map 32768 in hash.c to 265 */
+  TYPE("TA", ZONE_TYPE_TA, ZONE_CLASS_ANY, FIELDS(ta_rdata_fields), // obsolete
+              check_ds_rr, parse_ds_rdata),
+  /* Map 32769 in hash.c to 266 */
+  TYPE("DLV", ZONE_TYPE_DLV, ZONE_CLASS_ANY, FIELDS(dlv_rdata_fields), // obsolete
+              check_ds_rr, parse_ds_rdata)
+};
+
+#undef UNKNOWN_CLASS
+#undef CLASS
+#undef UNKNOWN_TYPE
+#undef TYPE
+
+diagnostic_pop()
+
+#endif // TYPES_H
Index: simdzone/src/generic/wks.h
===================================================================
RCS file: simdzone/src/generic/wks.h
diff -N simdzone/src/generic/wks.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/generic/wks.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,254 @@
+/*
+ * wks.c -- Well-Known Services (WKS) RDATA parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef WKS_H
+#define WKS_H
+
+// RFC1035 section 3.4.2:
+// The purpose of WKS RRs is to provide availability information for servers
+// for TCP and UDP.
+//
+// NSD and BIND use getprotobyname, which reads /etc/protocols (optimizations
+// may be in place for TCP and UDP). Note that BIND passes the protocol to
+// getservbyname for TCP and UDP only, NULL otherwise, which means any
+// protocol matches. Unfortunately, getprotobyname is NOT thread-safe.
+// getprotobyname_r exist on most BSDs and Linux, but not Windows.
+// The list of known protocols and services also differs between operating
+// systems and no list covers all IANA (links below) registered protocols
+// and services, which may cause compatibility issues. Furthermore, even
+// getprotobyname_r and getservbyname_r are marked locale, meaning the locale
+// object is read without any form of synchronization, which may be an issue
+// for a library.
+//
+// https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
+// https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
+//
+// https://www.gnu.org/software/libc/manual/html_node/Protocols-Database.html
+// https://www.gnu.org/software/libc/manual/html_node/Services-Database.html
+// https://www.gnu.org/software/libc/manual/html_node/POSIX-Safety-Concepts.html
+// https://www.gnu.org/software/libc/manual/html_node/Other-Safety-Remarks.html
+//
+// WKS RRs are rarely used and a document to deprecate the RRTYPE (among
+// others) has been drafted (WKS removed from the second draft).
+//
+// https://datatracker.ietf.org/doc/html/draft-sury-deprecate-obsolete-resource-records-00
+// https://mailarchive.ietf.org/arch/msg/dnsop/YCVvXuM8HbJLF2SoXyDqyOGao34/
+//
+//
+// WKS RRs have been said to be deprecated in an informational document (NOT
+// a standard), although it wrongly claims WKS RRs are in fact deprecated.
+//
+// RFC1912 section 2.6.1 (informational):
+// WKS records are deprecated in [RFC 1123].  They serve no known useful
+// function, except internally among LISP machines.  Don't use them.
+//
+// https://datatracker.ietf.org/doc/html/rfc1912
+//
+// RFC1123 section 2.2 (standard):
+// An application SHOULD NOT rely on the ability to locate a WKS record
+// containing an accurate listing of all services at a particular host
+// address, since the WKS RR type is not often used by Internet sites.
+// To confirm that a service is present, simply attempt to use it.
+//
+// https://datatracker.ietf.org/doc/html/rfc1123
+//
+// RFC1127 section 2 (informational):
+// WKS Records Detracted   [AS 2.2, 5.2.12, 6.1.3.6]
+// Recommend against using WKS records from DNS.
+//
+// https://datatracker.ietf.org/doc/html/rfc1127
+//
+//
+// Rather than supporting any protocol registered by IANA, support a small
+// subset of mnemonics (TCP and UDP) as well as numeric values and add
+// support (or remove it entirely) for additional protocols on demand.
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+# define TCP (0x0000000000706374llu)
+# define UDP (0x0000000000706475llu)
+#elif BYTE_ORDER == BIG_ENDIAN
+# define TCP (0x7463700000000000llu)
+# define UDP (0x7564700000000000llu)
+#else
+# error "byte order unknown"
+#endif
+
+static really_inline int32_t scan_protocol(
+  const char *name, size_t length, uint8_t *protocol)
+{
+  static const int8_t zero_masks[48] = {
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+     0,  0,  0,  0,  0,  0,  0,  0,
+     0,  0,  0,  0,  0,  0,  0,  0
+  };
+
+  uint64_t key;
+  uint64_t mask;
+  const int8_t *zero_mask = &zero_masks[32 - (length & 0x1f)];
+  memcpy(&mask, zero_mask, 8);
+
+  memcpy(&key, name, sizeof(key)); // safe, input is padded
+  key |= (key & 0x4040404040404040) >> 1; // convert to lower case
+  key &= mask;
+
+  if (key == TCP)
+    return (void)(*protocol = 6), 1;
+  else if (key == UDP)
+    return (void)(*protocol = 17), 1;
+  else
+    return scan_int8(name, length, protocol);
+}
+
+typedef struct service service_t;
+struct service {
+  struct {
+    const char name[16];
+    size_t length;
+  } key;
+  uint16_t port;
+};
+
+#define UNKNOWN_SERVICE() { { "", 0 }, 0 }
+#define SERVICE(name, port) { { name, sizeof(name) - 1 }, port }
+
+static const service_t services[64] = {
+  UNKNOWN_SERVICE(),
+  SERVICE("snmptrap", 162),
+  SERVICE("pop3s", 995),
+  SERVICE("pop3", 110),
+  SERVICE("ldaps", 636),
+  SERVICE("domain", 53),
+  SERVICE("nntps", 563),
+  SERVICE("nntp", 119),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  SERVICE("ftps-data", 989),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  SERVICE("imaps", 993),
+  SERVICE("imap", 143),
+  SERVICE("time", 37),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  SERVICE("kerberos", 88),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  SERVICE("ftp", 21),
+  SERVICE("ntp", 123),
+  SERVICE("whoispp", 63),
+  SERVICE("ssh", 22),
+  UNKNOWN_SERVICE(),
+  SERVICE("nicname", 43),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  SERVICE("ptp-general", 320),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  SERVICE("domain-s", 853),
+  SERVICE("ftp-data", 20),
+  SERVICE("ftps", 990),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  SERVICE("snmp", 161),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  UNKNOWN_SERVICE(),
+  SERVICE("bgmp", 264),
+  SERVICE("echo", 7),
+  UNKNOWN_SERVICE(),
+  SERVICE("nnsp", 433),
+  SERVICE("submission", 587),
+  // submissions cannot be distinguished from submission by hash value because
+  // the shared prefix is too long. include length to generate a unique key
+  SERVICE("submissions", 465),
+  UNKNOWN_SERVICE(),
+  SERVICE("ptp-event", 319),
+  UNKNOWN_SERVICE(),
+  SERVICE("npp", 92),
+  UNKNOWN_SERVICE(),
+  SERVICE("https", 443),
+  SERVICE("http", 80),
+  UNKNOWN_SERVICE(),
+  SERVICE("telnet", 23),
+  SERVICE("tcpmux", 1),
+  UNKNOWN_SERVICE(),
+  SERVICE("lmtp", 24),
+  SERVICE("smtp", 25)
+};
+
+#undef SERVICE
+#undef UNKNOWN_SERVICE
+
+// magic (139898079) generated using wks-hash.c
+static really_inline uint8_t service_hash(uint64_t input, size_t length)
+{
+  // le64toh is required for big endian, no-op on little endian
+  input = le64toh(input);
+  uint32_t input32 = (uint32_t)((input >> 32) ^ input);
+  return (((input32 * 139898079llu) >> 32) + length) & 0x3f;
+}
+
+nonnull((1,4))
+static really_inline int32_t scan_service(
+  const char *data, size_t length, int32_t protocol, uint16_t *port)
+{
+  uint8_t digit = (uint8_t)*data - '0';
+  static const int8_t zero_masks[48] = {
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+     0,  0,  0,  0,  0,  0,  0,  0,
+     0,  0,  0,  0,  0,  0,  0,  0
+  };
+
+  (void)protocol; // all supported services map to tcp and udp
+
+  if (digit > 9) {
+    uint64_t input0, input1;
+    static const uint64_t upper_mask = 0xdfdfdfdfdfdfdfdfllu;
+    static const uint64_t letter_mask = 0x4040404040404040llu;
+    memcpy(&input0, data, 8);
+    memcpy(&input1, data+8, 8);
+    // convert to upper case, unconditionally transforms digits (0x30-0x39)
+    // and dash (0x2d), but does not introduce clashes
+    uint64_t key = input0 & upper_mask;
+    // zero out non-relevant bytes
+    uint64_t zero_mask0, zero_mask1;
+    const int8_t *zero_mask = &zero_masks[32 - (length & 0xf)];
+    memcpy(&zero_mask0, zero_mask, 8);
+    memcpy(&zero_mask1, zero_mask+8, 8);
+    uint8_t index = service_hash(key & zero_mask0, length);
+    assert(index < 64);
+
+    input0 |= (input0 & letter_mask) >> 1;
+    input0 &= zero_mask0;
+    input1 |= (input1 & letter_mask) >> 1;
+    input1 &= zero_mask1;
+
+    uint64_t name0, name1;
+    memcpy(&name0, services[index].key.name, 8);
+    memcpy(&name1, services[index].key.name+8, 8);
+
+    *port = services[index].port;
+    return (input0 == name0) &
+	   (input1 == name1) &
+	   (services[index].key.length == length);
+  }
+
+  return scan_int16(data, length, port);
+}
+
+#endif // WKS_H
Index: simdzone/src/haswell/base32.h
===================================================================
RCS file: simdzone/src/haswell/base32.h
diff -N simdzone/src/haswell/base32.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/haswell/base32.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,97 @@
+/*
+ * base32.h -- Fast Base32 decoder
+ *
+ * Copyright (c) 2023, Daniel Lemire and @aqrit.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef BASE32_H
+#define BASE32_H
+
+#include <stdint.h>
+
+//////////////////////////
+/// Source: Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding Using AVX2 Instructions,
+///         ACM Transactions on the Web 12 (3), 2018
+///         https://arxiv.org/abs/1704.00605
+//////////////////////////
+
+static size_t base32hex_avx(uint8_t *dst, const uint8_t *src) {
+  static int8_t zero_masks256[64] = {
+      0,  0,  0,  0,  0,  0,  0,  0,  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, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+  bool valid = true;
+  const __m256i delta_check = _mm256_setr_epi8(
+      -16, -32, -48, 70, -65, 41, -97, 9, 0, 0, 0, 0, 0, 0, 0, 0, -16, -32, -48,
+      70, -65, 41, -97, 9, 0, 0, 0, 0, 0, 0, 0, 0);
+  const __m256i delta_rebase = _mm256_setr_epi8(
+      0, 0, 0, -48, -55, -55, -87, -87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -48,
+      -55, -55, -87, -87, 0, 0, 0, 0, 0, 0, 0, 0);
+  const uint8_t *srcinit = src;
+  do {
+    __m256i v = _mm256_loadu_si256((__m256i *)src);
+
+    __m256i hash_key =
+        _mm256_and_si256(_mm256_srli_epi32(v, 4), _mm256_set1_epi8(0x0F));
+    __m256i check =
+        _mm256_add_epi8(_mm256_shuffle_epi8(delta_check, hash_key), v);
+    v = _mm256_add_epi8(v, _mm256_shuffle_epi8(delta_rebase, hash_key));
+    unsigned int m = (unsigned)_mm256_movemask_epi8(check);
+
+    if (m) {
+      int length = (int)trailing_zeroes(m);
+      if (length == 0) {
+        break;
+      }
+      src += length;
+      __m256i zero_mask =
+          _mm256_loadu_si256((__m256i *)(zero_masks256 + 32 - length));
+      v = _mm256_andnot_si256(zero_mask, v);
+      valid = false;
+    } else { // common case
+      src += 32;
+    }
+    v = _mm256_maddubs_epi16(v, _mm256_set1_epi32(0x01200120));
+    v = _mm256_madd_epi16(
+        v, _mm256_set_epi32(0x00010400, 0x00104000, 0x00010400, 0x00104000,
+                            0x00010400, 0x00104000, 0x00010400, 0x00104000));
+    // ...00000000`0000eeee`efffffgg`ggghhhhh`00000000`aaaaabbb`bbcccccd`dddd0000
+    v = _mm256_or_si256(v, _mm256_srli_epi64(v, 48));
+    v = _mm256_shuffle_epi8(
+        v, _mm256_set_epi8(0, 0, 0, 0, 0, 0, 12, 13, 8, 9, 10, 4, 5, 0, 1, 2, 0,
+                           0, 0, 0, 0, 0, 12, 13, 8, 9, 10, 4, 5, 0, 1, 2));
+    // 5. store bytes
+    _mm_storeu_si128((__m128i *)dst, _mm256_castsi256_si128(v));
+    dst += 10;
+    _mm_storeu_si128((__m128i *)dst, _mm256_extractf128_si256(v, 1));
+    dst += 10;
+
+  } while (valid);
+
+  return (size_t)(src - srcinit);
+}
+
+nonnull_all
+static really_inline int32_t parse_base32(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  size_t length = (token->length * 5) / 8;
+  if (length > 255 || (uintptr_t)rdata->limit - (uintptr_t)rdata->octets < (length + 1))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  size_t decoded = base32hex_avx(rdata->octets+1, (const uint8_t*)token->data);
+  if (decoded != token->length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  *rdata->octets = (uint8_t)length;
+  rdata->octets += 1 + length;
+  return 0;
+}
+
+#endif // BASE32_H
Index: simdzone/src/haswell/bench.c
===================================================================
RCS file: simdzone/src/haswell/bench.c
diff -N simdzone/src/haswell/bench.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/haswell/bench.c	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,34 @@
+/*
+ * bench.c -- AVX2 compilation target for benchmark function(s)
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include "zone.h"
+#include "attributes.h"
+#include "diagnostic.h"
+#include "haswell/simd.h"
+#include "haswell/bits.h"
+#include "generic/parser.h"
+#include "generic/scanner.h"
+
+diagnostic_push()
+clang_diagnostic_ignored(missing-prototypes)
+
+int32_t zone_bench_haswell_lex(zone_parser_t *parser, size_t *tokens)
+{
+  token_t token;
+
+  (*tokens) = 0;
+  take(parser, &token);
+  while (token.code > 0) {
+    (*tokens)++;
+    take(parser, &token);
+  }
+
+  return token.code ? -1 : 0;
+}
+
+diagnostic_pop()
Index: simdzone/src/haswell/bits.h
===================================================================
RCS file: simdzone/src/haswell/bits.h
diff -N simdzone/src/haswell/bits.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/haswell/bits.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,61 @@
+/*
+ * bits.h -- Haswell specific implementation of bit manipulation instructions
+ *
+ * Copyright (c) 2018-2023 The simdjson authors
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#ifndef BITS_H
+#define BITS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <immintrin.h>
+
+static inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) {
+#if has_builtin(__builtin_uaddll_overflow)
+  return __builtin_uaddll_overflow(value1, value2, (unsigned long long *)result);
+#else
+  *result = value1 + value2;
+  return *result < value1;
+#endif
+}
+
+static inline uint64_t count_ones(uint64_t bits) {
+  return (uint64_t)_mm_popcnt_u64(bits);
+}
+
+no_sanitize_undefined
+static inline uint64_t trailing_zeroes(uint64_t bits) {
+  return (uint64_t)_tzcnt_u64(bits);
+}
+
+// result might be undefined when bits is zero
+static inline uint64_t clear_lowest_bit(uint64_t bits) {
+  return bits & (bits - 1);
+}
+
+static inline uint64_t leading_zeroes(uint64_t bits) {
+  return (uint64_t)_lzcnt_u64(bits);
+}
+
+static inline uint64_t prefix_xor(const uint64_t bitmask) {
+  __m128i all_ones = _mm_set1_epi8('\xFF');
+  __m128i mask = _mm_set_epi64x(0ULL, (long long)bitmask);
+#if defined __SUNPRO_C
+  // Oracle Developer Studio has issues generating vpclmulqdq
+  // Oracle Solaris and Intel assembler use the opposite order for source and
+  //   destination operands. See x86 Assemble Language Reference Manual.
+  __asm volatile ("vpclmulqdq $0,%[all_ones],%[mask],%[mask]"
+    : [mask] "+x" (mask)
+    : [all_ones] "x" (all_ones));
+#else
+  // There should be no such thing with a processor supporting avx2
+  // but not clmul.
+  mask = _mm_clmulepi64_si128(mask, all_ones, 0);
+#endif
+  return (uint64_t)_mm_cvtsi128_si64(mask);
+}
+
+#endif // BITS_H
Index: simdzone/src/haswell/parser.c
===================================================================
RCS file: simdzone/src/haswell/parser.c
diff -N simdzone/src/haswell/parser.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/haswell/parser.c	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,52 @@
+/*
+ * parser.c -- AVX2 specific compilation target for (DNS) zone file parser
+ *
+ * Copyright (c) 2022, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause.
+ *
+ */
+#include "zone.h"
+#include "attributes.h"
+#include "diagnostic.h"
+#include "haswell/simd.h"
+#include "generic/endian.h"
+#include "haswell/bits.h"
+#include "generic/parser.h"
+#include "generic/scanner.h"
+#include "generic/number.h"
+#include "generic/ttl.h"
+#include "westmere/time.h"
+#include "westmere/ip4.h"
+#include "generic/ip6.h"
+#include "generic/text.h"
+#include "generic/name.h"
+#include "generic/base16.h"
+#include "haswell/base32.h"
+#include "generic/base64.h"
+#include "generic/nsec.h"
+#include "generic/nxt.h"
+#include "generic/caa.h"
+#include "generic/ilnp64.h"
+#include "generic/eui.h"
+#include "generic/nsap.h"
+#include "generic/wks.h"
+#include "generic/loc.h"
+#include "generic/gpos.h"
+#include "generic/apl.h"
+#include "generic/svcb.h"
+#include "generic/cert.h"
+#include "generic/algorithm.h"
+#include "generic/types.h"
+#include "westmere/type.h"
+#include "generic/format.h"
+
+diagnostic_push()
+clang_diagnostic_ignored(missing-prototypes)
+
+int32_t zone_haswell_parse(parser_t *parser)
+{
+  return parse(parser);
+}
+
+diagnostic_pop()
Index: simdzone/src/haswell/simd.h
===================================================================
RCS file: simdzone/src/haswell/simd.h
diff -N simdzone/src/haswell/simd.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/haswell/simd.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,123 @@
+/*
+ * haswell.h -- SIMD abstractions targeting AVX2
+ *
+ * Copyright (c) 2022, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#ifndef SIMD_H
+#define SIMD_H
+
+#include <stdint.h>
+#include <immintrin.h>
+
+#define SIMD_8X_SIZE (32)
+
+typedef uint8_t simd_table_t[SIMD_8X_SIZE];
+
+#define SIMD_TABLE(v00, v01, v02, v03, v04, v05, v06, v07, \
+                   v08, v09, v0a, v0b, v0c, v0d, v0e, v0f) \
+  {                                                        \
+    v00, v01, v02, v03, v04, v05, v06, v07,                \
+    v08, v09, v0a, v0b, v0c, v0d, v0e, v0f,                \
+    v00, v01, v02, v03, v04, v05, v06, v07,                \
+    v08, v09, v0a, v0b, v0c, v0d, v0e, v0f                 \
+  }
+
+typedef struct { __m256i chunks[1]; } simd_8x_t;
+
+typedef struct { __m128i chunks[1]; } simd_8x16_t;
+
+typedef simd_8x_t simd_8x32_t;
+
+typedef struct { __m256i chunks[2]; } simd_8x64_t;
+
+
+nonnull_all
+static really_inline void simd_loadu_8x(simd_8x_t *simd, const void *address)
+{
+  simd->chunks[0] = _mm256_loadu_si256((const __m256i *)(address));
+}
+
+nonnull_all
+static really_inline void simd_storeu_8x(void *address, simd_8x_t *simd)
+{
+  _mm256_storeu_si256((__m256i *)address, simd->chunks[0]);
+}
+
+nonnull_all
+static really_inline uint64_t simd_find_8x(const simd_8x_t *simd, char key)
+{
+  const __m256i k = _mm256_set1_epi8(key);
+  const __m256i r = _mm256_cmpeq_epi8(simd->chunks[0], k);
+  return (uint32_t)_mm256_movemask_epi8(r);
+}
+
+nonnull_all
+static really_inline uint64_t simd_find_any_8x(
+  const simd_8x_t *simd, const simd_table_t table)
+{
+  const __m256i t = _mm256_loadu_si256((const __m256i *)table);
+  const __m256i r = _mm256_cmpeq_epi8(
+    _mm256_shuffle_epi8(t, simd->chunks[0]), simd->chunks[0]);
+  return (uint32_t)_mm256_movemask_epi8(r);
+}
+
+nonnull_all
+static really_inline void simd_loadu_8x16(simd_8x16_t *simd, const uint8_t *address)
+{
+  simd->chunks[0] = _mm_loadu_si128((const __m128i *)address);
+}
+
+nonnull_all
+static really_inline uint64_t simd_find_8x16(const simd_8x16_t *simd, char key)
+{
+  const __m128i k = _mm_set1_epi8(key);
+  const __m128i r = _mm_cmpeq_epi8(simd->chunks[0], k);
+  const uint64_t m = (uint16_t)_mm_movemask_epi8(r);
+  return m;
+}
+
+#define simd_loadu_8x32(simd, address) simd_loadu_8x(simd, address)
+#define simd_storeu_8x32(address, simd) simd_storeu_8x(address, simd)
+#define simd_find_8x32(simd, key) simd_find_8x(simd, key)
+
+nonnull_all
+static really_inline void simd_loadu_8x64(simd_8x64_t *simd, const uint8_t *address)
+{
+  simd->chunks[0] = _mm256_loadu_si256((const __m256i *)(address));
+  simd->chunks[1] = _mm256_loadu_si256((const __m256i *)(address+32));
+}
+
+nonnull_all
+static really_inline uint64_t simd_find_8x64(const simd_8x64_t *simd, char key)
+{
+  const __m256i k = _mm256_set1_epi8(key);
+
+  const __m256i r0 = _mm256_cmpeq_epi8(simd->chunks[0], k);
+  const __m256i r1 = _mm256_cmpeq_epi8(simd->chunks[1], k);
+
+  const uint64_t m0 = (uint32_t)_mm256_movemask_epi8(r0);
+  const uint64_t m1 = (uint32_t)_mm256_movemask_epi8(r1);
+
+  return m0 | (m1 << 32);
+}
+
+nonnull_all
+static really_inline uint64_t simd_find_any_8x64(
+  const simd_8x64_t *simd, const simd_table_t table)
+{
+  const __m256i t = _mm256_loadu_si256((const __m256i *)table);
+
+  const __m256i r0 = _mm256_cmpeq_epi8(
+    _mm256_shuffle_epi8(t, simd->chunks[0]), simd->chunks[0]);
+  const __m256i r1 = _mm256_cmpeq_epi8(
+    _mm256_shuffle_epi8(t, simd->chunks[1]), simd->chunks[1]);
+
+  const uint64_t m0 = (uint32_t)_mm256_movemask_epi8(r0);
+  const uint64_t m1 = (uint32_t)_mm256_movemask_epi8(r1);
+
+  return m0 | (m1 << 32);
+}
+
+#endif // SIMD_H
Index: simdzone/src/westmere/base32.h
===================================================================
RCS file: simdzone/src/westmere/base32.h
diff -N simdzone/src/westmere/base32.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/westmere/base32.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,88 @@
+/*
+ * base32.h -- Fast Base32 decoder
+ *
+ * Copyright (c) 2023, Daniel Lemire and @aqrit.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef BASE32_H
+#define BASE32_H
+
+#include <stdint.h>
+#include <immintrin.h>
+
+
+//////////////////////////
+/// Source: Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding Using AVX2 Instructions,
+///         ACM Transactions on the Web 12 (3), 2018
+///         https://arxiv.org/abs/1704.00605
+//////////////////////////
+static size_t base32hex_sse(uint8_t *dst, const uint8_t *src) {
+  static int8_t zero_masks[32] = {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                                  0,  0,  0,  0,  0,  -1, -1, -1, -1, -1, -1,
+                                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+  bool valid = true;
+  const __m128i delta_check =
+      _mm_setr_epi8(-16, -32, -48, 70, -65, 41, -97, 9, 0, 0, 0, 0, 0, 0, 0, 0);
+  const __m128i delta_rebase =
+      _mm_setr_epi8(0, 0, 0, -48, -55, -55, -87, -87, 0, 0, 0, 0, 0, 0, 0, 0);
+  const uint8_t *srcinit = src;
+  do {
+    __m128i v = _mm_loadu_si128((__m128i *)src);
+
+    __m128i hash_key = _mm_and_si128(_mm_srli_epi32(v, 4), _mm_set1_epi8(0x0F));
+    __m128i check = _mm_add_epi8(_mm_shuffle_epi8(delta_check, hash_key), v);
+    v = _mm_add_epi8(v, _mm_shuffle_epi8(delta_rebase, hash_key));
+    unsigned int m = (unsigned)_mm_movemask_epi8(check);
+
+    if (m) {
+      int length = (int)trailing_zeroes(m);
+      if (length == 0) {
+        break;
+      }
+      src += length;
+      __m128i zero_mask =
+          _mm_loadu_si128((__m128i *)(zero_masks + 16 - length));
+      v = _mm_andnot_si128(zero_mask, v);
+      valid = false;
+    } else { // common case
+      src += 16;
+    }
+    v = _mm_maddubs_epi16(v, _mm_set1_epi32(0x01200120));
+    v = _mm_madd_epi16(
+        v, _mm_set_epi32(0x00010400, 0x00104000, 0x00010400, 0x00104000));
+    // ...00000000`0000eeee`efffffgg`ggghhhhh`00000000`aaaaabbb`bbcccccd`dddd0000
+    v = _mm_or_si128(v, _mm_srli_epi64(v, 48));
+    v = _mm_shuffle_epi8(
+        v, _mm_set_epi8(0, 0, 0, 0, 0, 0, 12, 13, 8, 9, 10, 4, 5, 0, 1, 2));
+
+    /* decoded 10 bytes... but write 16 cause why not? */
+    _mm_storeu_si128((__m128i *)dst, v);
+    dst += 10;
+  } while (valid);
+
+  return (size_t)(src - srcinit);
+}
+
+nonnull_all
+static really_inline int32_t parse_base32(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  size_t length = (token->length * 5) / 8;
+  if (length > 255 || (uintptr_t)rdata->limit - (uintptr_t)rdata->octets < (length + 1))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  size_t decoded = base32hex_sse(rdata->octets+1, (const uint8_t*)token->data);
+  if (decoded != token->length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  *rdata->octets = (uint8_t)length;
+  rdata->octets += 1 + length;
+  return 0;
+}
+
+#endif // BASE32_H
Index: simdzone/src/westmere/bench.c
===================================================================
RCS file: simdzone/src/westmere/bench.c
diff -N simdzone/src/westmere/bench.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/westmere/bench.c	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,34 @@
+/*
+ * bench.c -- SSE4.2 compilation target for benchmark function(s)
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include "zone.h"
+#include "attributes.h"
+#include "diagnostic.h"
+#include "westmere/simd.h"
+#include "westmere/bits.h"
+#include "generic/parser.h"
+#include "generic/scanner.h"
+
+diagnostic_push()
+clang_diagnostic_ignored(missing-prototypes)
+
+int32_t zone_bench_westmere_lex(zone_parser_t *parser, size_t *tokens)
+{
+  token_t token;
+
+  (*tokens) = 0;
+  take(parser, &token);
+  while (token.code > 0) {
+    (*tokens)++;
+    take(parser, &token);
+  }
+
+  return token.code ? -1 : 0;
+}
+
+diagnostic_pop()
Index: simdzone/src/westmere/bits.h
===================================================================
RCS file: simdzone/src/westmere/bits.h
diff -N simdzone/src/westmere/bits.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/westmere/bits.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,65 @@
+/*
+ * bits.h -- Westmere specific implementation of bit manipulation instructions
+ *
+ * Copyright (c) 2018-2022 The simdjson authors
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#ifndef BITS_H
+#define BITS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <immintrin.h>
+
+static inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) {
+#if has_builtin(__builtin_uaddll_overflow)
+  return __builtin_uaddll_overflow(value1, value2, (unsigned long long *)result);
+#else
+  *result = value1 + value2;
+  return *result < value1;
+#endif
+}
+
+static inline uint64_t count_ones(uint64_t input_num) {
+  return (uint64_t)_mm_popcnt_u64(input_num);
+}
+
+no_sanitize_undefined
+static inline uint64_t trailing_zeroes(uint64_t mask) {
+#if has_builtin(__builtin_ctzll)
+  return (uint64_t)__builtin_ctzll(mask);
+#else
+  uint64_t result;
+  asm("bsfq %[mask], %[result]"
+      : [result] "=r" (result)
+      : [mask] "mr" (mask));
+  return result;
+#endif
+}
+
+// result might be undefined when input_num is zero
+static inline uint64_t clear_lowest_bit(uint64_t input_num) {
+  return input_num & (input_num-1);
+}
+
+static inline uint64_t leading_zeroes(uint64_t mask) {
+#if has_builtin(__builtin_clzll)
+  return (uint64_t)__builtin_clzll(mask);
+#else
+  uint64_t result;
+  asm("bsrq %[mask], %[result]" :
+      [result] "=r" (result) :
+      [mask] "mr" (mask));
+  return 63 ^ (int)result;
+#endif
+}
+
+static inline uint64_t prefix_xor(const uint64_t bitmask) {
+  __m128i all_ones = _mm_set1_epi8('\xFF');
+  __m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, (long long)bitmask), all_ones, 0);
+  return (uint64_t)_mm_cvtsi128_si64(result);
+}
+
+#endif // BITS_H
Index: simdzone/src/westmere/ip4.h
===================================================================
RCS file: simdzone/src/westmere/ip4.h
diff -N simdzone/src/westmere/ip4.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/westmere/ip4.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,206 @@
+/*
+ * ip4.h -- SSE 4.1 parser for IPv4 addresses
+ *          https://lemire.me/blog/2023/06/08/parsing-ip-addresses-crazily-fast/
+ *
+ * Copyright (c) 2023. Daniel Lemire
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef IP4_H
+#define IP4_H
+
+static const uint8_t patterns_id[256] = {
+  38,  65,  255, 56,  73,  255, 255, 255, 255, 255, 255, 3,   255, 255, 6,
+  255, 255, 9,   255, 27,  255, 12,  30,  255, 255, 255, 255, 15,  255, 33,
+  255, 255, 255, 255, 18,  36,  255, 255, 255, 54,  21,  255, 39,  255, 255,
+  57,  255, 255, 255, 255, 255, 255, 255, 255, 24,  42,  255, 255, 255, 60,
+  255, 255, 255, 255, 255, 255, 255, 255, 45,  255, 255, 63,  255, 255, 255,
+  255, 255, 255, 255, 255, 255, 48,  53,  255, 255, 66,  71,  255, 255, 16,
+  255, 34,  255, 255, 255, 255, 255, 255, 255, 52,  255, 255, 22,  70,  40,
+  255, 255, 58,  51,  255, 255, 69,  255, 255, 255, 255, 255, 255, 255, 255,
+  255, 5,   255, 255, 255, 255, 255, 255, 11,  29,  46,  255, 255, 64,  255,
+  255, 72,  0,   77,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 76,  255, 255, 255, 255, 255, 255, 255, 75,  255,
+  80,  255, 255, 255, 26,  255, 44,  255, 7,   62,  255, 255, 25,  255, 43,
+  13,  31,  61,  255, 255, 255, 255, 255, 255, 255, 255, 255, 2,   19,  37,
+  255, 255, 50,  55,  79,  68,  255, 255, 255, 255, 49,  255, 255, 67,  255,
+  255, 255, 255, 17,  255, 35,  78,  255, 4,   255, 255, 255, 255, 255, 255,
+  10,  23,  28,  41,  255, 255, 59,  255, 255, 255, 8,   255, 255, 255, 255,
+  255, 1,   14,  32,  255, 255, 255, 255, 255, 255, 255, 255, 74,  255, 47,
+  20,
+};
+
+static const uint8_t patterns[81][16] = {
+  {0, 128, 2, 128, 4, 128, 6, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+  {0, 128, 2, 128, 4, 128, 7, 6, 128, 128, 128, 128, 128, 128, 128, 6},
+  {0, 128, 2, 128, 4, 128, 8, 7, 128, 128, 128, 128, 128, 128, 6, 6},
+  {0, 128, 2, 128, 5, 4, 7, 128, 128, 128, 128, 128, 128, 4, 128, 128},
+  {0, 128, 2, 128, 5, 4, 8, 7, 128, 128, 128, 128, 128, 4, 128, 7},
+  {0, 128, 2, 128, 5, 4, 9, 8, 128, 128, 128, 128, 128, 4, 7, 7},
+  {0, 128, 2, 128, 6, 5, 8, 128, 128, 128, 128, 128, 4, 4, 128, 128},
+  {0, 128, 2, 128, 6, 5, 9, 8, 128, 128, 128, 128, 4, 4, 128, 8},
+  {0, 128, 2, 128, 6, 5, 10, 9, 128, 128, 128, 128, 4, 4, 8, 8},
+  {0, 128, 3, 2, 5, 128, 7, 128, 128, 128, 128, 2, 128, 128, 128, 128},
+  {0, 128, 3, 2, 5, 128, 8, 7, 128, 128, 128, 2, 128, 128, 128, 7},
+  {0, 128, 3, 2, 5, 128, 9, 8, 128, 128, 128, 2, 128, 128, 7, 7},
+  {0, 128, 3, 2, 6, 5, 8, 128, 128, 128, 128, 2, 128, 5, 128, 128},
+  {0, 128, 3, 2, 6, 5, 9, 8, 128, 128, 128, 2, 128, 5, 128, 8},
+  {0, 128, 3, 2, 6, 5, 10, 9, 128, 128, 128, 2, 128, 5, 8, 8},
+  {0, 128, 3, 2, 7, 6, 9, 128, 128, 128, 128, 2, 5, 5, 128, 128},
+  {0, 128, 3, 2, 7, 6, 10, 9, 128, 128, 128, 2, 5, 5, 128, 9},
+  {0, 128, 3, 2, 7, 6, 11, 10, 128, 128, 128, 2, 5, 5, 9, 9},
+  {0, 128, 4, 3, 6, 128, 8, 128, 128, 128, 2, 2, 128, 128, 128, 128},
+  {0, 128, 4, 3, 6, 128, 9, 8, 128, 128, 2, 2, 128, 128, 128, 8},
+  {0, 128, 4, 3, 6, 128, 10, 9, 128, 128, 2, 2, 128, 128, 8, 8},
+  {0, 128, 4, 3, 7, 6, 9, 128, 128, 128, 2, 2, 128, 6, 128, 128},
+  {0, 128, 4, 3, 7, 6, 10, 9, 128, 128, 2, 2, 128, 6, 128, 9},
+  {0, 128, 4, 3, 7, 6, 11, 10, 128, 128, 2, 2, 128, 6, 9, 9},
+  {0, 128, 4, 3, 8, 7, 10, 128, 128, 128, 2, 2, 6, 6, 128, 128},
+  {0, 128, 4, 3, 8, 7, 11, 10, 128, 128, 2, 2, 6, 6, 128, 10},
+  {0, 128, 4, 3, 8, 7, 12, 11, 128, 128, 2, 2, 6, 6, 10, 10},
+  {1, 0, 3, 128, 5, 128, 7, 128, 128, 0, 128, 128, 128, 128, 128, 128},
+  {1, 0, 3, 128, 5, 128, 8, 7, 128, 0, 128, 128, 128, 128, 128, 7},
+  {1, 0, 3, 128, 5, 128, 9, 8, 128, 0, 128, 128, 128, 128, 7, 7},
+  {1, 0, 3, 128, 6, 5, 8, 128, 128, 0, 128, 128, 128, 5, 128, 128},
+  {1, 0, 3, 128, 6, 5, 9, 8, 128, 0, 128, 128, 128, 5, 128, 8},
+  {1, 0, 3, 128, 6, 5, 10, 9, 128, 0, 128, 128, 128, 5, 8, 8},
+  {1, 0, 3, 128, 7, 6, 9, 128, 128, 0, 128, 128, 5, 5, 128, 128},
+  {1, 0, 3, 128, 7, 6, 10, 9, 128, 0, 128, 128, 5, 5, 128, 9},
+  {1, 0, 3, 128, 7, 6, 11, 10, 128, 0, 128, 128, 5, 5, 9, 9},
+  {1, 0, 4, 3, 6, 128, 8, 128, 128, 0, 128, 3, 128, 128, 128, 128},
+  {1, 0, 4, 3, 6, 128, 9, 8, 128, 0, 128, 3, 128, 128, 128, 8},
+  {1, 0, 4, 3, 6, 128, 10, 9, 128, 0, 128, 3, 128, 128, 8, 8},
+  {1, 0, 4, 3, 7, 6, 9, 128, 128, 0, 128, 3, 128, 6, 128, 128},
+  {1, 0, 4, 3, 7, 6, 10, 9, 128, 0, 128, 3, 128, 6, 128, 9},
+  {1, 0, 4, 3, 7, 6, 11, 10, 128, 0, 128, 3, 128, 6, 9, 9},
+  {1, 0, 4, 3, 8, 7, 10, 128, 128, 0, 128, 3, 6, 6, 128, 128},
+  {1, 0, 4, 3, 8, 7, 11, 10, 128, 0, 128, 3, 6, 6, 128, 10},
+  {1, 0, 4, 3, 8, 7, 12, 11, 128, 0, 128, 3, 6, 6, 10, 10},
+  {1, 0, 5, 4, 7, 128, 9, 128, 128, 0, 3, 3, 128, 128, 128, 128},
+  {1, 0, 5, 4, 7, 128, 10, 9, 128, 0, 3, 3, 128, 128, 128, 9},
+  {1, 0, 5, 4, 7, 128, 11, 10, 128, 0, 3, 3, 128, 128, 9, 9},
+  {1, 0, 5, 4, 8, 7, 10, 128, 128, 0, 3, 3, 128, 7, 128, 128},
+  {1, 0, 5, 4, 8, 7, 11, 10, 128, 0, 3, 3, 128, 7, 128, 10},
+  {1, 0, 5, 4, 8, 7, 12, 11, 128, 0, 3, 3, 128, 7, 10, 10},
+  {1, 0, 5, 4, 9, 8, 11, 128, 128, 0, 3, 3, 7, 7, 128, 128},
+  {1, 0, 5, 4, 9, 8, 12, 11, 128, 0, 3, 3, 7, 7, 128, 11},
+  {1, 0, 5, 4, 9, 8, 13, 12, 128, 0, 3, 3, 7, 7, 11, 11},
+  {2, 1, 4, 128, 6, 128, 8, 128, 0, 0, 128, 128, 128, 128, 128, 128},
+  {2, 1, 4, 128, 6, 128, 9, 8, 0, 0, 128, 128, 128, 128, 128, 8},
+  {2, 1, 4, 128, 6, 128, 10, 9, 0, 0, 128, 128, 128, 128, 8, 8},
+  {2, 1, 4, 128, 7, 6, 9, 128, 0, 0, 128, 128, 128, 6, 128, 128},
+  {2, 1, 4, 128, 7, 6, 10, 9, 0, 0, 128, 128, 128, 6, 128, 9},
+  {2, 1, 4, 128, 7, 6, 11, 10, 0, 0, 128, 128, 128, 6, 9, 9},
+  {2, 1, 4, 128, 8, 7, 10, 128, 0, 0, 128, 128, 6, 6, 128, 128},
+  {2, 1, 4, 128, 8, 7, 11, 10, 0, 0, 128, 128, 6, 6, 128, 10},
+  {2, 1, 4, 128, 8, 7, 12, 11, 0, 0, 128, 128, 6, 6, 10, 10},
+  {2, 1, 5, 4, 7, 128, 9, 128, 0, 0, 128, 4, 128, 128, 128, 128},
+  {2, 1, 5, 4, 7, 128, 10, 9, 0, 0, 128, 4, 128, 128, 128, 9},
+  {2, 1, 5, 4, 7, 128, 11, 10, 0, 0, 128, 4, 128, 128, 9, 9},
+  {2, 1, 5, 4, 8, 7, 10, 128, 0, 0, 128, 4, 128, 7, 128, 128},
+  {2, 1, 5, 4, 8, 7, 11, 10, 0, 0, 128, 4, 128, 7, 128, 10},
+  {2, 1, 5, 4, 8, 7, 12, 11, 0, 0, 128, 4, 128, 7, 10, 10},
+  {2, 1, 5, 4, 9, 8, 11, 128, 0, 0, 128, 4, 7, 7, 128, 128},
+  {2, 1, 5, 4, 9, 8, 12, 11, 0, 0, 128, 4, 7, 7, 128, 11},
+  {2, 1, 5, 4, 9, 8, 13, 12, 0, 0, 128, 4, 7, 7, 11, 11},
+  {2, 1, 6, 5, 8, 128, 10, 128, 0, 0, 4, 4, 128, 128, 128, 128},
+  {2, 1, 6, 5, 8, 128, 11, 10, 0, 0, 4, 4, 128, 128, 128, 10},
+  {2, 1, 6, 5, 8, 128, 12, 11, 0, 0, 4, 4, 128, 128, 10, 10},
+  {2, 1, 6, 5, 9, 8, 11, 128, 0, 0, 4, 4, 128, 8, 128, 128},
+  {2, 1, 6, 5, 9, 8, 12, 11, 0, 0, 4, 4, 128, 8, 128, 11},
+  {2, 1, 6, 5, 9, 8, 13, 12, 0, 0, 4, 4, 128, 8, 11, 11},
+  {2, 1, 6, 5, 10, 9, 12, 128, 0, 0, 4, 4, 8, 8, 128, 128},
+  {2, 1, 6, 5, 10, 9, 13, 12, 0, 0, 4, 4, 8, 8, 128, 12},
+  {2, 1, 6, 5, 10, 9, 14, 13, 0, 0, 4, 4, 8, 8, 12, 12},
+};
+
+// convert IPv4 from text to binary form.
+//
+// ipv4_string points to a character string containing an IPv4 network address in dotted-decimal format
+// "ddd.ddd.ddd.ddd" of length ipv4_string_length (the string does not have to be null terminated),
+// where ddd is a decimal number of up to three digits in the range 0 to 255. 
+// The address is converted to a 32-bit integer (destination) (in  network byte order).
+//
+// Important: the function will systematically read 16 bytes at the provided address (ipv4_string). We infer
+// the network address size in bytes by looking for a sequence of dots and decimal digits.
+//
+// returns 1 on success (network address was successfully converted).
+//
+// This function assumes that the processor supports SSE 4.1 instructions or better. That's true of most
+// processors in operation today (June 2023).
+static inline int sse_inet_aton(const char* ipv4_string, uint8_t* destination, size_t* restrict ipv4_string_length) {
+
+  __m128i v = _mm_loadu_si128((const __m128i *)ipv4_string);
+
+  __m128i is_dot = _mm_cmpeq_epi8(v, _mm_set1_epi8(0x2E));
+  uint32_t dot_mask = (uint32_t)_mm_movemask_epi8(is_dot);
+
+  // set non-digits to 0x80..0x89, set digits to 0x00..0x09
+  const __m128i saturation_distance = _mm_set1_epi8(0x76); // 0x7F - 9
+  v = _mm_xor_si128(v, _mm_set1_epi8(0x30)); // ascii '0'
+  v = _mm_adds_epu8(v, saturation_distance);
+  uint32_t non_digit_mask = (uint32_t)_mm_movemask_epi8(v);
+  v = _mm_subs_epi8(v, saturation_distance);
+
+  uint32_t bad_mask = dot_mask ^ non_digit_mask;
+  uint32_t clip_mask = bad_mask ^ (bad_mask - 1);
+  uint32_t partition_mask = non_digit_mask & clip_mask;
+
+  const uint32_t length = (uint32_t)count_ones(clip_mask) - 1;
+
+  uint32_t hash_key = (partition_mask * 0x00CF7800) >> 24;
+  uint8_t hash_id = patterns_id[hash_key];
+  if (hash_id >= 81)
+      return 0;
+  const uint8_t* const pattern_ptr = &patterns[hash_id][0];
+
+  __m128i shuf = _mm_loadu_si128((const __m128i *)pattern_ptr);
+  v = _mm_shuffle_epi8(v, shuf);
+
+  const __m128i mul_weights =
+      _mm_set_epi8(0,100, 0,100, 0,100, 0,100, 10,1, 10,1, 10,1, 10,1);
+  __m128i acc = _mm_maddubs_epi16(mul_weights, v);
+  __m128i swapped = _mm_shuffle_epi32(acc, _MM_SHUFFLE(1,0,3,2));
+  acc = _mm_adds_epu16(acc, swapped);
+
+  // check `v` for leading zeros in each partition, ignore lanes if partition has only one digit
+  // if hibyte of `acc` then bad_char or overflow
+  __m128i check_lz = _mm_xor_si128(_mm_cmpeq_epi8(_mm_setzero_si128(), v), shuf);
+  __m128i check_of = _mm_adds_epu16(_mm_set1_epi16(0x7F00), acc);
+  __m128i checks = _mm_or_si128(check_lz, check_of);
+  uint32_t check_mask = (uint32_t)_mm_movemask_epi8(checks);
+  check_mask &= 0x0000AA00; // the only lanes wanted
+
+  // pack and we are done!
+  uint32_t address = (uint32_t)_mm_cvtsi128_si32(_mm_packus_epi16(acc, acc));
+  *ipv4_string_length = length;
+  memcpy(destination, &address, 4);
+  return (int)(length + check_mask - pattern_ptr[6]);
+}
+
+nonnull_all
+static really_inline int32_t scan_ip4(const char *text, uint8_t *wire)
+{
+  size_t len;
+  if (sse_inet_aton(text, wire, &len) != 1)
+    return 0;
+  return (int32_t)len;
+}
+
+nonnull_all
+static really_inline int32_t parse_ip4(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  // Note that this assumes that reading up to token->data + 16 is safe (i.e., we do not cross a page).
+  if ((size_t)scan_ip4(token->data, rdata->octets) != token->length)
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+  rdata->octets += 4;
+  return 0;
+}
+
+#endif // IP4_H
Index: simdzone/src/westmere/parser.c
===================================================================
RCS file: simdzone/src/westmere/parser.c
diff -N simdzone/src/westmere/parser.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/westmere/parser.c	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,52 @@
+/*
+ * parser.c -- SSE4.2 specific compilation target for (DNS) zone file parser
+ *
+ * Copyright (c) 2022, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause.
+ *
+ */
+#include "zone.h"
+#include "attributes.h"
+#include "diagnostic.h"
+#include "westmere/simd.h"
+#include "generic/endian.h"
+#include "westmere/bits.h"
+#include "generic/parser.h"
+#include "generic/scanner.h"
+#include "generic/number.h"
+#include "generic/ttl.h"
+#include "westmere/time.h"
+#include "westmere/ip4.h"
+#include "generic/ip6.h"
+#include "generic/text.h"
+#include "generic/name.h"
+#include "generic/base16.h"
+#include "westmere/base32.h"
+#include "generic/base64.h"
+#include "generic/nsec.h"
+#include "generic/nxt.h"
+#include "generic/caa.h"
+#include "generic/ilnp64.h"
+#include "generic/eui.h"
+#include "generic/nsap.h"
+#include "generic/wks.h"
+#include "generic/loc.h"
+#include "generic/gpos.h"
+#include "generic/apl.h"
+#include "generic/svcb.h"
+#include "generic/cert.h"
+#include "generic/algorithm.h"
+#include "generic/types.h"
+#include "westmere/type.h"
+#include "generic/format.h"
+
+diagnostic_push()
+clang_diagnostic_ignored(missing-prototypes)
+
+int32_t zone_westmere_parse(parser_t *parser)
+{
+  return parse(parser);
+}
+
+diagnostic_pop()
Index: simdzone/src/westmere/simd.h
===================================================================
RCS file: simdzone/src/westmere/simd.h
diff -N simdzone/src/westmere/simd.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/westmere/simd.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,142 @@
+/*
+ * simd.h -- SIMD abstractions targeting SSE4.2
+ *
+ * Copyright (c) 2022, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ *
+ */
+#ifndef SIMD_H
+#define SIMD_H
+
+#include <stdint.h>
+#include <immintrin.h>
+
+#define SIMD_8X_SIZE (16)
+
+typedef uint8_t simd_table_t[SIMD_8X_SIZE];
+
+#define SIMD_TABLE(v00, v01, v02, v03, v04, v05, v06, v07, \
+                   v08, v09, v0a, v0b, v0c, v0d, v0e, v0f) \
+  {                                                        \
+    v00, v01, v02, v03, v04, v05, v06, v07,                \
+    v08, v09, v0a, v0b, v0c, v0d, v0e, v0f                 \
+  }
+
+typedef struct { __m128i chunks[1]; } simd_8x_t;
+
+typedef simd_8x_t simd_8x16_t;
+
+typedef struct { __m128i chunks[2]; } simd_8x32_t;
+
+typedef struct { __m128i chunks[4]; } simd_8x64_t;
+
+nonnull_all
+static really_inline void simd_loadu_8x(simd_8x_t *simd, const uint8_t *address)
+{
+  simd->chunks[0] = _mm_loadu_si128((const __m128i *)address);
+}
+
+nonnull_all
+static really_inline void simd_storeu_8x(uint8_t *address, const simd_8x_t *simd)
+{
+  _mm_storeu_si128((__m128i *)address, simd->chunks[0]);
+}
+
+nonnull_all
+static really_inline uint64_t simd_find_8x(const simd_8x_t *simd, char key)
+{
+  const __m128i k = _mm_set1_epi8(key);
+  const __m128i r = _mm_cmpeq_epi8(simd->chunks[0], k);
+  return (uint16_t)_mm_movemask_epi8(r);
+}
+
+nonnull_all
+static really_inline uint64_t simd_find_any_8x(
+  const simd_8x_t *simd, const simd_table_t table)
+{
+  const __m128i t = _mm_loadu_si128((const __m128i *)table);
+  const __m128i r = _mm_cmpeq_epi8(
+    _mm_shuffle_epi8(t, simd->chunks[0]), simd->chunks[0]);
+  return (uint16_t)_mm_movemask_epi8(r);
+}
+
+#define simd_loadu_8x16(simd, address) simd_loadu_8x(simd, address)
+#define simd_find_8x16(simd, key) simd_find_8x(simd, key)
+
+nonnull_all
+static really_inline void simd_loadu_8x32(simd_8x32_t *simd, const char *address)
+{
+  simd->chunks[0] = _mm_loadu_si128((const __m128i *)(address));
+  simd->chunks[1] = _mm_loadu_si128((const __m128i *)(address+16));
+}
+
+nonnull_all
+static really_inline void simd_storeu_8x32(uint8_t *address, const simd_8x32_t *simd)
+{
+  _mm_storeu_si128((__m128i *)(address), simd->chunks[0]);
+  _mm_storeu_si128((__m128i *)(address+16), simd->chunks[1]);
+}
+
+nonnull_all
+static really_inline uint64_t simd_find_8x32(const simd_8x32_t *simd, char key)
+{
+  const __m128i k = _mm_set1_epi8(key);
+  const __m128i r0 = _mm_cmpeq_epi8(simd->chunks[0], k);
+  const __m128i r1 = _mm_cmpeq_epi8(simd->chunks[1], k);
+  const uint32_t m0 = (uint16_t)_mm_movemask_epi8(r0);
+  const uint32_t m1 = (uint16_t)_mm_movemask_epi8(r1);
+  return m0 | (m1 << 16);
+}
+
+nonnull_all
+static really_inline void simd_loadu_8x64(simd_8x64_t *simd, const uint8_t *address)
+{
+  simd->chunks[0] = _mm_loadu_si128((const __m128i *)(address));
+  simd->chunks[1] = _mm_loadu_si128((const __m128i *)(address+16));
+  simd->chunks[2] = _mm_loadu_si128((const __m128i *)(address+32));
+  simd->chunks[3] = _mm_loadu_si128((const __m128i *)(address+48));
+}
+
+nonnull_all
+static really_inline uint64_t simd_find_8x64(const simd_8x64_t *simd, char key)
+{
+  const __m128i k = _mm_set1_epi8(key);
+
+  const __m128i r0 = _mm_cmpeq_epi8(simd->chunks[0], k);
+  const __m128i r1 = _mm_cmpeq_epi8(simd->chunks[1], k);
+  const __m128i r2 = _mm_cmpeq_epi8(simd->chunks[2], k);
+  const __m128i r3 = _mm_cmpeq_epi8(simd->chunks[3], k);
+
+  const uint64_t m0 = (uint16_t)_mm_movemask_epi8(r0);
+  const uint64_t m1 = (uint16_t)_mm_movemask_epi8(r1);
+  const uint64_t m2 = (uint16_t)_mm_movemask_epi8(r2);
+  const uint64_t m3 = (uint16_t)_mm_movemask_epi8(r3);
+
+  return m0 | (m1 << 16) | (m2 << 32) | (m3 << 48);
+}
+
+nonnull_all
+static really_inline uint64_t simd_find_any_8x64(
+  const simd_8x64_t *simd, const simd_table_t table)
+{
+  const __m128i t = _mm_loadu_si128((const __m128i *)table);
+
+  const __m128i r0 = _mm_cmpeq_epi8(
+    _mm_shuffle_epi8(t, simd->chunks[0]), simd->chunks[0]);
+  const __m128i r1 = _mm_cmpeq_epi8(
+    _mm_shuffle_epi8(t, simd->chunks[1]), simd->chunks[1]);
+  const __m128i r2 = _mm_cmpeq_epi8(
+    _mm_shuffle_epi8(t, simd->chunks[2]), simd->chunks[2]);
+  const __m128i r3 = _mm_cmpeq_epi8(
+    _mm_shuffle_epi8(t, simd->chunks[3]), simd->chunks[3]);
+
+  const uint64_t m0 = (uint16_t)_mm_movemask_epi8(r0);
+  const uint64_t m1 = (uint16_t)_mm_movemask_epi8(r1);
+  const uint64_t m2 = (uint16_t)_mm_movemask_epi8(r2);
+  const uint64_t m3 = (uint16_t)_mm_movemask_epi8(r3);
+
+  return m0 | (m1 << 16) | (m2 << 32) | (m3 << 48);
+}
+
+#endif // SIMD_H
Index: simdzone/src/westmere/time.h
===================================================================
RCS file: simdzone/src/westmere/time.h
diff -N simdzone/src/westmere/time.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/westmere/time.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,144 @@
+/*
+ * ip4.h -- SSE 4.1 parser for time stamps
+ *          https://lemire.me/blog/2023/07/01/parsing-time-stamps-faster-with-simd-instructions/
+ *
+ * Copyright (c) 2023. Daniel Lemire
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef SSE_TIME_H
+#define SSE_TIME_H
+
+static const int mdays_minus_one[] = {30, 27, 30, 29, 30, 29, 30, 30, 29, 30, 29, 30};
+
+static const int mdays_cumulative[] = {0,   31,  59,  90,  120, 151, 181,
+                                       212, 243, 273, 304, 334, 365};
+
+/*
+  The 32-bit timestamp spans from year 1970 to 2106.
+  Therefore, the only special case for leap years is 2100.
+  We use that to produce fast functions.
+*/
+static inline uint32_t is_leap_year(uint32_t year) {  
+  return (year % 4 == 0) & (year != 2100);
+}
+
+static inline uint32_t leap_days(uint32_t year) { 
+  --year;
+  return (year/4 - 1970/4) - (year >= 2100);
+}
+
+static bool sse_parse_time(const char *date_string, uint32_t *time_in_second) {
+  // We load the block of digits. We subtract 0x30 (the code point value of the
+  // character '0'), and all bytes values should be between 0 and 9,
+  // inclusively. We know that some character must be smaller that 9, for
+  // example, we cannot have more than 59 seconds and never 60 seconds, in the
+  // time stamp string. So one character must be between 0 and 5. Similarly, we
+  // start the hours at 00 and end at 23, so one character must be between 0
+  // and 2. We do a saturating subtraction of the maximum: the result of such a
+  // subtraction should be zero if the value is no larger. We then use a special
+  // instruction to multiply one byte by 10, and sum it up with the next byte,
+  // getting a 16-bit value. We then repeat the same approach as before,
+  // checking that the result is not too large.
+  //
+  // We compute the month the good old ways, as an integer in [0,11], we
+  // check for overflows later.
+  uint64_t mo = (uint64_t)((date_string[4]-0x30)*10 + (date_string[5]-0x30) - 1);
+  __m128i v = _mm_loadu_si128((const __m128i *)date_string);
+  // loaded YYYYMMDDHHmmSS.....
+  v = _mm_xor_si128(v, _mm_set1_epi8(0x30));
+  // W can use _mm_sub_epi8 or _mm_xor_si128 for the subtraction above.
+  // subtracting by 0x30 (or '0'), turns all values into a byte value between 0
+  // and 9 if the initial input was made of digits.
+  __m128i limit =
+      _mm_setr_epi8(9, 9, 9, 9, 1, 9, 3, 9, 2, 9, 5, 9, 5, 9, -1, -1);
+  // credit @aqrit
+  // overflows are still possible, if hours are in the range 24 to 29
+  // of if days are in the range 32 to 39
+  // or if months are in the range 12 to 19.
+  __m128i abide_by_limits = _mm_subs_epu8(v, limit); // must be all zero
+
+#if defined __SUNPRO_C
+  __m128i byteflip = _mm_setr_epi64((__m64){0x0607040502030001ULL},
+                                    (__m64){0x0e0f0c0d0a0b0809ULL});
+#else
+  __m128i byteflip = _mm_setr_epi64((__m64)0x0607040502030001ULL,
+                                    (__m64)0x0e0f0c0d0a0b0809ULL);
+#endif
+
+  __m128i little_endian = _mm_shuffle_epi8(v, byteflip);
+  __m128i limit16 = _mm_setr_epi16(0x0909, 0x0909, 0x0102, 0x0301, 0x0203,
+                                   0x0509, 0x0509, -1);
+  __m128i abide_by_limits16 =
+      _mm_subs_epu16(little_endian, limit16); // must be all zero
+
+  __m128i combined_limits =
+      _mm_or_si128(abide_by_limits16, abide_by_limits); // must be all zero
+
+  if (!_mm_test_all_zeros(combined_limits, combined_limits)) {
+    return false;
+  }
+  // 0x000000SS0mmm0HHH`00DD00MM00YY00YY
+  //////////////////////////////////////////////////////
+  // pmaddubsw has a high latency (e.g., 5 cycles) and is
+  // likely a performance bottleneck.
+  /////////////////////////////////////////////////////
+  const __m128i weights = _mm_setr_epi8(
+      //     Y   Y   Y   Y   m   m   d   d   H   H   M   M   S   S   -   -
+      10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 0, 0);
+  v = _mm_maddubs_epi16(v, weights);
+
+  uint64_t hi = (uint64_t)_mm_extract_epi64(v, 1);
+  uint64_t seconds = (hi * 0x0384000F00004000) >> 46;
+  uint64_t lo = (uint64_t)_mm_extract_epi64(v, 0);
+  uint64_t yr = (lo * 0x64000100000000) >> 48;
+
+  // We compute the day (starting at zero). We implicitly 
+  // check for overflows later.
+  uint64_t dy = (uint64_t)_mm_extract_epi8(v, 6) - 1;
+
+  bool is_leap_yr = (bool)is_leap_year((uint32_t)yr);
+  if(yr < 1970 || mo > 11) { return false; } // unlikely branch
+  if (dy > (uint64_t)mdays_minus_one[mo]) { // unlikely branch
+    if (mo == 1 && is_leap_yr) {
+      if (dy != 29 - 1) {
+        return false;
+      }
+    } else {
+      return false;
+    }
+  }
+  uint64_t days = 365 * (yr - 1970) + (uint64_t)leap_days((uint32_t)yr);
+
+  days += (uint64_t)mdays_cumulative[mo];
+  days += is_leap_yr & (mo > 1);
+
+  days += dy;
+  uint64_t time_in_second64 = seconds + days * 60 * 60 * 24;
+  *time_in_second = (uint32_t)time_in_second64;
+  return true;
+}
+
+nonnull_all
+static really_inline int32_t parse_time(
+  parser_t *parser,
+  const type_info_t *type,
+  const rdata_info_t *field,
+  rdata_t *rdata,
+  const token_t *token)
+{
+  uint32_t time;
+
+  if (unlikely(token->length != 14))
+    return parse_int32(parser, type, field, rdata, token);
+  if (!sse_parse_time(token->data, &time))
+    SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
+
+  time = htobe32(time);
+  memcpy(rdata->octets, &time, sizeof(time));
+  rdata->octets += sizeof(time);
+  return 0;
+}
+
+#endif // TIME_H
Index: simdzone/src/westmere/type.h
===================================================================
RCS file: simdzone/src/westmere/type.h
diff -N simdzone/src/westmere/type.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simdzone/src/westmere/type.h	3 Sep 2025 13:53:32 -0000
@@ -0,0 +1,184 @@
+/*
+ * type.h -- SSE4.1 RRTYPE parser
+ *
+ * Copyright (c) 2023, NLnet Labs. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef TYPE_H
+#define TYPE_H
+
+#define V(code) { &(types[0].name),      0 }
+#define T(code) { &(types[code].name),   1 }
+#define C(code) { &(classes[code].name), 2 }
+
+// map hash to type or class descriptor (generated using hash.c)
+static const struct {
+  const mnemonic_t *mnemonic;
+  int32_t code;
+} types_and_classes[256] = {
+    V(0),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),  T(20),
+    T(3),   V(0),   V(0),   V(0),   V(0), T(261),   V(0),   V(0),
+   T(60),   V(0),   V(0), T(105),   V(0),   V(0),   V(0), T(258),
+    V(0),   V(0),   V(0),   V(0),  T(30),   V(0),  T(28),   V(0),
+    V(0),  T(16),   V(0),   V(0),  T(56),  T(14),  T(22),   V(0),
+    V(0),  T(13),   V(0),  T(47),  T(21),   V(0),  T(65),  T(27),
+    V(0),   V(0),   V(0),   V(0),   V(0),   T(1),  T(62),   V(0),
+    V(0),   C(1),   V(0),  T(44),   V(0),   V(0),  T(33),   V(0),
+    V(0),   V(0),   V(0),   V(0),  T(63),   V(0), T(266),   V(0),
+    C(3),  T(99),  T(37),   V(0),   V(0),   V(0),   C(2),  T(43),
+    V(0),  T(50),   C(4),  T(51),   V(0),   V(0),   V(0),   T(2),
+   T(49),  T(42),  T(19),  T(23),   V(0),   T(6),   V(0),   V(0),
+    V(0),   V(0),  T(29),   V(0),   T(7),   V(0),   V(0),   V(0),
+    V(0),  T(57),   V(0),   V(0),   V(0),   V(0),   V(0),  T(36),
+   T(15),   V(0),   V(0),  T(26),  T(11),   V(0),   V(0),   V(0),
+    V(0),   V(0),   V(0),   V(0), T(104),   V(0),   T(8),   V(0),
+    V(0),   V(0),  T(38),   V(0),   T(9),   V(0),  T(64),   V(0),
+    V(0),   V(0),   V(0),   V(0),   V(0),   V(0),  T(39),  T(52),
+   T(24),   V(0),   T(5), T(106),   V(0),   V(0),   V(0),   V(0),
+  T(265),   V(0),   V(0),   V(0),   V(0),  T(25),   V(0),  T(18),
+   T(48),   V(0),  T(53),   V(0),   V(0),   V(0),  T(59),   V(0),
+    V(0),   V(0),   V(0),   V(0),   T(4),   V(0),  T(10),   V(0),
+    V(0),   V(0),   V(0),   V(0),   V(0),  T(55),   V(0),   V(0),
+    V(0),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),
+    V(0),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),
+    V(0),   V(0),  T(61),  T(12),   V(0),   V(0),   V(0),   V(0),
+    V(0), T(108),   V(0),   V(0), T(257),   V(0),   V(0),   V(0),
+   T(35),   V(0), T(263),   V(0),   V(0),   V(0),   V(0), T(107),
+    V(0),   V(0),   V(0),   V(0),  T(17),   V(0),  T(45),   V(0),
+    V(0),   V(0),   V(0),   V(0),   V(0),   V(0),  T(46),   V(0),
+    V(0), T(109),   V(0),   V(0),   V(0),   V(0),   V(0),   V(0),
+    V(0),   V(0),   V(0),   V(0), T(262),   V(0), T(256),   V(0)
+};
+
+#undef V
+#undef T
+#undef C
+
+nonnull_all
+static really_inline int32_t scan_generic_type(
+  const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic)
+{
+  if (scan_int16(data + 4, length - 4, code) == 0)
+    return -1;
+  if (*code <= 258)
+    *mnemonic = &types[*code].name;
+  else if (*code == 32769)
+    *mnemonic = &types[259].name;
+  else
+    *mnemonic = &types[0].name;
+  return 1;
+}
+
+nonnull_all
+static really_inline int32_t scan_generic_class(
+  const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic)
+{
+  if (scan_int16(data + 5, length - 5, code) == 0)
+    return -1;
+  if (*code <= 4)
+    *mnemonic = &classes[*code].name;
+  else
+    *mnemonic = &classes[0].name;
+  return 2;
+}
+
+#define TYPE (0x45505954llu)
+#define TYPE_MASK (0xffffffffllu)
+#define CLASS (0x5353414c43llu)
+#define CLASS_MASK (0xffffffffffllu)
+
+static const int8_t zero_masks[48] = {
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
+   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0
+};
+
+static really_inline uint8_t hash(uint64_t prefix)
+{
+  uint32_t value = (uint32_t)((prefix >> 32) ^ prefix);
+  // magic value is generated using hash.c, rerun when adding types
+  return (uint8_t)((value * 3523548378ull) >> 32);
+}
+
+nonnull_all
+static really_inline int32_t scan_type_or_class(
+  const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic)
+{
+  __m128i input = _mm_loadu_si128((const __m128i *)data);
+
+  const __m128i letter_mask =
+    _mm_srli_epi32(_mm_and_si128(input, _mm_set1_epi8(0x40)), 1);
+
+  // convert to upper case
+  input = _mm_andnot_si128(letter_mask, input);
+
+  // sanitize input
+  const __m128i zero_mask =
+    _mm_loadu_si128((const __m128i *)&zero_masks[32 - (length & 0x1f)]);
+  input = _mm_and_si128(input, zero_mask);
+
+  // input is now sanitized and upper case
+
+  const uint64_t prefix = (uint64_t)_mm_cvtsi128_si64(input);
+  const uint8_t index = hash(prefix);
+  *code = (uint16_t)types_and_classes[index].mnemonic->value;
+  *mnemonic = types_and_classes[index].mnemonic;
+
+  const __m128i compar = _mm_loadu_si128((const __m128i *)(*mnemonic)->key.data);
+  const __m128i xorthem = _mm_xor_si128(compar, input);
+
+  if (likely(_mm_test_all_zeros(xorthem, xorthem)))
+    return types_and_classes[index].code;
+  else if ((prefix & TYPE_MASK) == TYPE)
+    return scan_generic_type(data, length, code, mnemonic);
+  else if ((prefix & CLASS_MASK) == CLASS)
+    return scan_generic_class(data, length, code, mnemonic);
+  return 0;
+}
+
+nonnull_all
+static really_inline int32_t scan_type(
+  const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic)
+{
+  __m128i input = _mm_loadu_si128((const __m128i *)data);
+
+  const __m128i letter_mask =
+    _mm_srli_epi32(_mm_and_si128(input, _mm_set1_epi8(0x40)), 1);
+
+  // convert to upper case
+  input = _mm_andnot_si128(letter_mask, input);
+
+  // sanitize input
+  const __m128i zero_mask =
+    _mm_loadu_si128((const __m128i *)&zero_masks[32 - (length & 0x1f)]);
+  input = _mm_and_si128(input, zero_mask);
+
+  // input is now sanitized and upper case
+
+  const uint64_t prefix = (uint64_t)_mm_cvtsi128_si64(input);
+  const uint8_t index = hash(prefix);
+  *code = (uint16_t)types_and_classes[index].mnemonic->value;
+  *mnemonic = types_and_classes[index].mnemonic;
+
+  const __m128i compar = _mm_loadu_si128((const __m128i *)(*mnemonic)->key.data);
+  const __m128i xorthem = _mm_xor_si128(compar, input);
+
+  // FIXME: make sure length matches too!
+  if (likely(_mm_test_all_zeros(xorthem, xorthem)))
+    return types_and_classes[index].code == 1;
+  else if ((prefix & TYPE_MASK) == TYPE)
+    return scan_generic_type(data, length, code, mnemonic);
+  return 0;
+}
+
+#undef TYPE
+#undef TYPE_MASK
+#undef CLASS
+#undef CLASS_MASK
+
+#endif // TYPE_H