untrusted comment: verify with openbsd-78-base.pub RWS3/nvFmk4SWVPp0t2oEuLf7+gBvlTug06PYEeV0NG3DHjgpursCxgJ4aqigh1MSVt0p4Ac1wchhnDFTNhOLzzqTl0ESlVYtAo= OpenBSD 7.8 errata 058, September 30, 2026: Update nsd(8) to version 4.15.2. CVE-2026-12244 CVE-2026-12245 CVE-2026-12246 CVE-2026-12490 CVE-2026-18664 CVE-2026-18916 CVE-2026-19401 CVE-2026-19538 Apply by doing: signify -Vep /etc/signify/openbsd-78-base.pub -x 058_nsd.patch.sig \ -m - | (cd /usr/src && patch -p0) And then rebuild and install nsd: cd /usr/src/usr.sbin/nsd make -f Makefile.bsd-wrapper obj make -f Makefile.bsd-wrapper clean make -f Makefile.bsd-wrapper make -f Makefile.bsd-wrapper install Index: usr.sbin/nsd/Makefile.in =================================================================== RCS file: /cvs/src/usr.sbin/nsd/Makefile.in,v diff -u -p -r1.40 Makefile.in --- usr.sbin/nsd/Makefile.in 6 Sep 2025 17:41:37 -0000 1.40 +++ usr.sbin/nsd/Makefile.in 21 Sep 2026 16:29:05 -0000 @@ -101,10 +101,10 @@ XDP_OBJ=xdp-server.o xdp-util.o NSD_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) $(XDP_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 nsd-checkzone.o verify.o +NSD_CHECKZONE_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) $(XDP_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 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_xfrd_tcp.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 +CUTEST_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) $(XDP_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_xfrd_tcp.o cutest_bitset.o cutest_popen3.o cutest_iter.o cutest_event.o cutest.o qtest.o +NSD_MEM_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) $(XDP_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 @@ -163,6 +163,14 @@ nsd-checkzone.8: $(srcdir)/nsd-checkzone nsd-control.8: $(srcdir)/nsd-control.8.in config.h $(EDIT) $(srcdir)/nsd-control.8.in > $@ +contrib/nsd.openrc: $(srcdir)/contrib/nsd.openrc.in + mkdir -p contrib + $(EDIT) $(srcdir)/contrib/nsd.openrc.in > $@ + +contrib/nsd-tmpfiles.conf: $(srcdir)/contrib/nsd-tmpfiles.conf.in + mkdir -p contrib + $(EDIT) $(srcdir)/contrib/nsd-tmpfiles.conf.in > $@ + install: orig-install: all @@ -522,7 +530,7 @@ difffile.o: $(srcdir)/difffile.c config. dname.o: $(srcdir)/dname.c config.h $(srcdir)/dns.h $(srcdir)/dname.h $(srcdir)/buffer.h \ $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/radtree.h $(srcdir)/rbtree.h \ $(srcdir)/nsd.h $(srcdir)/edns.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 \ +dns.o: $(srcdir)/dns.c config.h $(srcdir)/dns.h $(srcdir)/rdata.h $(srcdir)/namedb.h $(srcdir)/dname.h \ $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.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)/bitset.h $(srcdir)/nsd.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h \ @@ -540,7 +548,7 @@ ixfr.o: $(srcdir)/ixfr.c config.h $(srcd simdzone/include/zone/export.h ixfrcreate.o: $(srcdir)/ixfrcreate.c config.h $(srcdir)/ixfrcreate.h $(srcdir)/dns.h \ $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/radtree.h \ - $(srcdir)/rbtree.h $(srcdir)/ixfr.h $(srcdir)/query.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/options.h + $(srcdir)/rbtree.h $(srcdir)/ixfr.h $(srcdir)/query.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/options.h $(srcdir)/rdata.h lookup3.o: $(srcdir)/lookup3.c config.h $(srcdir)/lookup3.h metrics.o: $(srcdir)/metrics.c config.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h \ $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/xfrd.h $(srcdir)/rbtree.h $(srcdir)/namedb.h $(srcdir)/dname.h \ @@ -551,6 +559,7 @@ namedb.o: $(srcdir)/namedb.c config.h $( netio.o: $(srcdir)/netio.c config.h $(srcdir)/netio.h $(srcdir)/region-allocator.h \ $(srcdir)/util.h $(srcdir)/bitset.h nsd.o: $(srcdir)/nsd.c config.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h \ + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/options.h $(srcdir)/rbtree.h $(srcdir)/tsig.h $(srcdir)/dname.h \ $(srcdir)/remote.h $(srcdir)/xfrd-disk.h $(srcdir)/ipc.h $(srcdir)/netio.h $(srcdir)/metrics.h $(srcdir)/dnstap/dnstap_collector.h \ $(srcdir)/util/proxy_protocol.h config.h $(srcdir)/xdp-server.h $(srcdir)/xdp-util.h nsd-checkconf.o: $(srcdir)/nsd-checkconf.c config.h $(srcdir)/tsig.h $(srcdir)/buffer.h \ @@ -580,18 +589,19 @@ packet.o: $(srcdir)/packet.c config.h $( popen3.o: $(srcdir)/popen3.c config.h $(srcdir)/popen3.h query.o: $(srcdir)/query.c config.h $(srcdir)/answer.h $(srcdir)/dns.h $(srcdir)/namedb.h $(srcdir)/dname.h \ $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/packet.h \ - $(srcdir)/query.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/tsig.h $(srcdir)/axfr.h $(srcdir)/options.h $(srcdir)/nsec3.h + $(srcdir)/query.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/tsig.h $(srcdir)/axfr.h $(srcdir)/options.h $(srcdir)/nsec3.h $(srcdir)/rdata.h radtree.o: $(srcdir)/radtree.c config.h $(srcdir)/radtree.h $(srcdir)/util.h $(srcdir)/bitset.h \ $(srcdir)/region-allocator.h rbtree.o: $(srcdir)/rbtree.c config.h $(srcdir)/rbtree.h $(srcdir)/region-allocator.h rdata.o: $(srcdir)/rdata.c config.h $(srcdir)/rdata.h $(srcdir)/dns.h $(srcdir)/namedb.h $(srcdir)/dname.h \ - $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/zonec.h + $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/zonec.h \ + $(srcdir)/query.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h region-allocator.o: $(srcdir)/region-allocator.c config.h \ $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h remote.o: $(srcdir)/remote.c config.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/xfrd.h \ $(srcdir)/rbtree.h $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/dns.h $(srcdir)/radtree.h \ $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/xfrd-catalog-zones.h $(srcdir)/xfrd-notify.h $(srcdir)/xfrd-tcp.h $(srcdir)/nsd.h \ - $(srcdir)/edns.h $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/ipc.h $(srcdir)/netio.h $(srcdir)/remote.h $(srcdir)/metrics.h + $(srcdir)/edns.h $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/ipc.h $(srcdir)/netio.h $(srcdir)/remote.h $(srcdir)/rdata.h $(srcdir)/metrics.h rrl.o: $(srcdir)/rrl.c config.h $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h \ $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h \ $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/lookup3.h $(srcdir)/options.h @@ -623,7 +633,7 @@ xfrd.o: $(srcdir)/xfrd.c config.h $(srcd $(srcdir)/udb.h $(srcdir)/ipc.h $(srcdir)/remote.h $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/dnstap/dnstap_collector.h $(srcdir)/metrics.h xfrd-catalog-zones.o: $(srcdir)/xfrd-catalog-zones.c config.h \ $(srcdir)/difffile.h $(srcdir)/rbtree.h $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h \ - $(srcdir)/bitset.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/options.h $(srcdir)/udb.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h \ + $(srcdir)/bitset.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/options.h $(srcdir)/udb.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/rdata.h \ $(srcdir)/xfrd-catalog-zones.h $(srcdir)/xfrd.h $(srcdir)/tsig.h $(srcdir)/xfrd-notify.h xfrd-disk.o: $(srcdir)/xfrd-disk.c config.h $(srcdir)/xfrd-disk.h $(srcdir)/xfrd.h \ $(srcdir)/rbtree.h $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/bitset.h \ @@ -641,77 +651,79 @@ zonec.o: $(srcdir)/zonec.c config.h $(sr $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h \ $(srcdir)/rdata.h $(srcdir)/options.h $(srcdir)/nsec3.h $(srcdir)/simdzone/include/zone.h \ $(srcdir)/simdzone/include/zone/attributes.h simdzone/include/zone/export.h -b64_ntop.o: $(srcdir)/compat/b64_ntop.c config.h -b64_pton.o: $(srcdir)/compat/b64_pton.c config.h +b64_ntop.o: $(srcdir)/compat/b64_ntop.c config.h $(srcdir)/compat/cpuset.h +b64_pton.o: $(srcdir)/compat/b64_pton.c config.h $(srcdir)/compat/cpuset.h basename.o: $(srcdir)/compat/basename.c -explicit_bzero.o: $(srcdir)/compat/explicit_bzero.c config.h -fake-rfc2553.o: $(srcdir)/compat/fake-rfc2553.c $(srcdir)/compat/fake-rfc2553.h config.h -inet_aton.o: $(srcdir)/compat/inet_aton.c config.h -inet_ntop.o: $(srcdir)/compat/inet_ntop.c config.h -inet_pton.o: $(srcdir)/compat/inet_pton.c config.h +cpuset.o: $(srcdir)/compat/cpuset.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/compat/cpuset.h +explicit_bzero.o: $(srcdir)/compat/explicit_bzero.c config.h $(srcdir)/compat/cpuset.h +fake-rfc2553.o: $(srcdir)/compat/fake-rfc2553.c $(srcdir)/compat/fake-rfc2553.h config.h \ + $(srcdir)/compat/cpuset.h +inet_aton.o: $(srcdir)/compat/inet_aton.c config.h $(srcdir)/compat/cpuset.h +inet_ntop.o: $(srcdir)/compat/inet_ntop.c config.h $(srcdir)/compat/cpuset.h +inet_pton.o: $(srcdir)/compat/inet_pton.c config.h $(srcdir)/compat/cpuset.h malloc.o: $(srcdir)/compat/malloc.c -memcmp.o: $(srcdir)/compat/memcmp.c config.h -memmove.o: $(srcdir)/compat/memmove.c config.h -pselect.o: $(srcdir)/compat/pselect.c config.h -reallocarray.o: $(srcdir)/compat/reallocarray.c config.h -setproctitle.o: $(srcdir)/compat/setproctitle.c config.h -snprintf.o: $(srcdir)/compat/snprintf.c config.h -strlcat.o: $(srcdir)/compat/strlcat.c config.h -strlcpy.o: $(srcdir)/compat/strlcpy.c config.h +memcmp.o: $(srcdir)/compat/memcmp.c config.h $(srcdir)/compat/cpuset.h +memmove.o: $(srcdir)/compat/memmove.c config.h $(srcdir)/compat/cpuset.h +pselect.o: $(srcdir)/compat/pselect.c config.h $(srcdir)/compat/cpuset.h +reallocarray.o: $(srcdir)/compat/reallocarray.c config.h $(srcdir)/compat/cpuset.h +setproctitle.o: $(srcdir)/compat/setproctitle.c config.h $(srcdir)/compat/cpuset.h +snprintf.o: $(srcdir)/compat/snprintf.c config.h $(srcdir)/compat/cpuset.h +strlcat.o: $(srcdir)/compat/strlcat.c config.h $(srcdir)/compat/cpuset.h +strlcpy.o: $(srcdir)/compat/strlcpy.c config.h $(srcdir)/compat/cpuset.h strptime.o: $(srcdir)/compat/strptime.c cutest_bitset.o: $(srcdir)/tpkg/cutest/cutest_bitset.c $(srcdir)/bitset.h \ $(srcdir)/tpkg/cutest/cutest.h -cutest.o: $(srcdir)/tpkg/cutest/cutest.c config.h \ +cutest.o: $(srcdir)/tpkg/cutest/cutest.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h -cutest_dname.o: $(srcdir)/tpkg/cutest/cutest_dname.c config.h \ +cutest_dname.o: $(srcdir)/tpkg/cutest/cutest_dname.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/dname.h $(srcdir)/buffer.h \ $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/dns.h -cutest_dns.o: $(srcdir)/tpkg/cutest/cutest_dns.c config.h \ +cutest_dns.o: $(srcdir)/tpkg/cutest/cutest_dns.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/dns.h -cutest_event.o: $(srcdir)/tpkg/cutest/cutest_event.c config.h $(srcdir)/nsd.h \ +cutest_event.o: $(srcdir)/tpkg/cutest/cutest_event.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/nsd.h \ $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h \ $(srcdir)/tpkg/cutest/cutest.h cutest_iterated_hash.o: $(srcdir)/tpkg/cutest/cutest_iterated_hash.c config.h \ - $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h \ + $(srcdir)/compat/cpuset.h $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h \ $(srcdir)/iterated_hash.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h -cutest_iter.o: $(srcdir)/tpkg/cutest/cutest_iter.c config.h $(srcdir)/nsd.h \ +cutest_iter.o: $(srcdir)/tpkg/cutest/cutest_iter.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/nsd.h \ $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/options.h \ $(srcdir)/rbtree.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/radtree.h $(srcdir)/tpkg/cutest/cutest.h -cutest_namedb.o: $(srcdir)/tpkg/cutest/cutest_namedb.c config.h \ +cutest_namedb.o: $(srcdir)/tpkg/cutest/cutest_namedb.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/options.h $(srcdir)/region-allocator.h \ $(srcdir)/rbtree.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/dns.h $(srcdir)/radtree.h \ $(srcdir)/nsec3.h $(srcdir)/udb.h $(srcdir)/difffile.h $(srcdir)/namedb.h $(srcdir)/options.h $(srcdir)/udb.h $(srcdir)/zonec.h $(srcdir)/nsd.h $(srcdir)/edns.h \ $(srcdir)/simdzone/include/zone.h $(srcdir)/simdzone/include/zone/attributes.h \ - simdzone/include/zone/export.h -cutest_options.o: $(srcdir)/tpkg/cutest/cutest_options.c config.h \ + simdzone/include/zone/export.h $(srcdir)/rdata.h +cutest_options.o: $(srcdir)/tpkg/cutest/cutest_options.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/options.h $(srcdir)/region-allocator.h \ $(srcdir)/rbtree.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/nsd.h $(srcdir)/edns.h -cutest_popen3.o: $(srcdir)/tpkg/cutest/cutest_popen3.c config.h \ +cutest_popen3.o: $(srcdir)/tpkg/cutest/cutest_popen3.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/popen3.h $(srcdir)/tpkg/cutest/cutest.h -cutest_radtree.o: $(srcdir)/tpkg/cutest/cutest_radtree.c config.h \ +cutest_radtree.o: $(srcdir)/tpkg/cutest/cutest_radtree.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/radtree.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h -cutest_rbtree.o: $(srcdir)/tpkg/cutest/cutest_rbtree.c config.h \ +cutest_rbtree.o: $(srcdir)/tpkg/cutest/cutest_rbtree.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/rbtree.h $(srcdir)/region-allocator.h -cutest_region.o: $(srcdir)/tpkg/cutest/cutest_region.c config.h \ +cutest_region.o: $(srcdir)/tpkg/cutest/cutest_region.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/rbtree.h \ $(srcdir)/region-allocator.h -cutest_rrl.o: $(srcdir)/tpkg/cutest/cutest_rrl.c config.h \ +cutest_rrl.o: $(srcdir)/tpkg/cutest/cutest_rrl.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h \ $(srcdir)/packet.h $(srcdir)/tsig.h -cutest_run.o: $(srcdir)/tpkg/cutest/cutest_run.c config.h \ +cutest_run.o: $(srcdir)/tpkg/cutest/cutest_run.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/tpkg/cutest/qtest.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h \ - $(srcdir)/util.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h $(srcdir)/bitset.h -cutest_udb.o: $(srcdir)/tpkg/cutest/cutest_udb.c config.h \ + $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h +cutest_udb.o: $(srcdir)/tpkg/cutest/cutest_udb.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/udb.h -cutest_util.o: $(srcdir)/tpkg/cutest/cutest_util.c config.h \ +cutest_util.o: $(srcdir)/tpkg/cutest/cutest_util.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h -cutest_xfrd_tcp.o: $(srcdir)/tpkg/cutest/cutest_xfrd_tcp.c config.h \ +cutest_xfrd_tcp.o: $(srcdir)/tpkg/cutest/cutest_xfrd_tcp.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/xfrd-tcp.h $(srcdir)/xfrd.h $(srcdir)/rbtree.h $(srcdir)/region-allocator.h \ $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/options.h \ $(srcdir)/tsig.h popen3_echo.o: $(srcdir)/tpkg/cutest/popen3_echo.c -qtest.o: $(srcdir)/tpkg/cutest/qtest.c config.h $(srcdir)/tpkg/cutest/qtest.h \ +qtest.o: $(srcdir)/tpkg/cutest/qtest.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/tpkg/cutest/qtest.h \ $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h \ $(srcdir)/buffer.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/namedb.h \ $(srcdir)/util.h $(srcdir)/nsec3.h $(srcdir)/options.h $(srcdir)/packet.h $(srcdir)/dname.h $(srcdir)/rdata.h Index: usr.sbin/nsd/README.md =================================================================== RCS file: /cvs/src/usr.sbin/nsd/README.md,v diff -u -p -r1.3 README.md --- usr.sbin/nsd/README.md 3 Sep 2025 18:46:48 -0000 1.3 +++ usr.sbin/nsd/README.md 21 Sep 2026 16:29:05 -0000 @@ -4,15 +4,14 @@ [![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) +[![Mastodon Follow](https://img.shields.io/mastodon/follow/114692612288811644?domain=social.nlnetlabs.nl&style=social)](https://social.nlnetlabs.nl/@nlnetlabs) The NLnet Labs Name Server Daemon (NSD) is an authoritative DNS name server. It has been developed for operations in environments where speed, reliability, stability and security are of high importance. If you have any feedback, we would love to hear from you. Don’t hesitate to [create an issue on Github](https://github.com/NLnetLabs/nsd/issues/new) -or post a message on the -[NSD mailing list](https://lists.nlnetlabs.nl/mailman/listinfo/nsd-users). +or post a message on our [community forum](https://community.nlnetlabs.nl/). You can learn more about NSD by reading our [documentation](https://nsd.docs.nlnetlabs.nl/). Index: usr.sbin/nsd/aclocal.m4 =================================================================== RCS file: /cvs/src/usr.sbin/nsd/aclocal.m4,v diff -u -p -r1.6 aclocal.m4 --- usr.sbin/nsd/aclocal.m4 6 Sep 2025 17:41:37 -0000 1.6 +++ usr.sbin/nsd/aclocal.m4 21 Sep 2026 16:29:06 -0000 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.18.1 -*- Autoconf -*- +# generated automatically by aclocal 1.16.5 -*- Autoconf -*- -# Copyright (C) 1996-2025 Free Software Foundation, Inc. +# Copyright (C) 1996-2021 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -13,7 +13,7 @@ m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) # pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*- -# serial 13 (pkgconf) +# serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson @@ -29,7 +29,9 @@ dnl MERCHANTABILITY or FITNESS FOR A PAR dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License -dnl along with this program; if not, see . +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a @@ -58,8 +60,8 @@ m4_if(m4_version_compare(PKG_MACROS_VERS [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ -dnl PKG_PROG_PKG_CONFIG([MIN-VERSION], [ACTION-IF-NOT-FOUND]) -dnl --------------------------------------------------------- +dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) +dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to @@ -67,12 +69,6 @@ dnl first found in the path. Checks that dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. -dnl -dnl If pkg-config is not found or older than specified, it will result -dnl in an empty PKG_CONFIG variable. To avoid widespread issues with -dnl scripts not checking it, ACTION-IF-NOT-FOUND defaults to aborting. -dnl You can specify [PKG_CONFIG=false] as an action instead, which would -dnl result in pkg-config tests failing, but no bogus error messages. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) @@ -93,9 +89,6 @@ if test -n "$PKG_CONFIG"; then AC_MSG_RESULT([no]) PKG_CONFIG="" fi -fi -if test -z "$PKG_CONFIG"; then - m4_default([$2], [AC_MSG_ERROR([pkg-config not found])]) fi[]dnl ])dnl PKG_PROG_PKG_CONFIG Index: usr.sbin/nsd/acx_nlnetlabs.m4 =================================================================== RCS file: /cvs/src/usr.sbin/nsd/acx_nlnetlabs.m4,v diff -u -p -r1.10 acx_nlnetlabs.m4 --- usr.sbin/nsd/acx_nlnetlabs.m4 12 Apr 2024 15:53:34 -0000 1.10 +++ usr.sbin/nsd/acx_nlnetlabs.m4 21 Sep 2026 16:29:06 -0000 @@ -2,7 +2,12 @@ # Copyright 2009, Wouter Wijngaards, NLnet Labs. # BSD licensed. # -# Version 48 +# Version 51 +# 2025-11-06 Fix ACX_CHECK_NONSTRING_ATTRIBUTE to reject clang, that prints +# a warning for 'unknown attribute' when nonstring is used. +# 2025-09-29 add ac_cv_func_malloc_0_nonnull as a cache value for the malloc(0) +# check by ACX_FUNC_MALLOC. +# 2025-09-29 add ACX_CHECK_NONSTRING_ATTRIBUTE, AHX_CONFIG_NONSTRING_ATTRIBUTE. # 2024-01-16 fix to add -l:libssp.a to -lcrypto link check. # and check for getaddrinfo with only header. # 2024-01-15 fix to add crypt32 to -lcrypto link check when checking for gdi32. @@ -71,6 +76,7 @@ # ACX_DEPFLAG - find cc dependency flags. # ACX_DETERMINE_EXT_FLAGS_UNBOUND - find out which flags enable BSD and POSIX. # ACX_CHECK_FORMAT_ATTRIBUTE - find cc printf format syntax. +# ACX_CHECK_NONSTRING_ATTRIBUTE - find cc nonstring attribute syntax. # ACX_CHECK_UNUSED_ATTRIBUTE - find cc variable unused syntax. # ACX_CHECK_FLTO - see if cc supports -flto and use it if so. # ACX_LIBTOOL_C_ONLY - create libtool for C only, improved. @@ -92,6 +98,7 @@ # ACX_FUNC_IOCTLSOCKET - find ioctlsocket, portably. # ACX_FUNC_MALLOC - check malloc, define replacement . # AHX_CONFIG_FORMAT_ATTRIBUTE - config.h text for format. +# AHX_CONFIG_NONSTRING_ATTRIBUTE - config.h text for nonstring. # AHX_CONFIG_UNUSED_ATTRIBUTE - config.h text for unused. # AHX_CONFIG_FSEEKO - define fseeko, ftello fallback. # AHX_CONFIG_RAND_MAX - define RAND_MAX if needed. @@ -490,7 +497,7 @@ AC_DEFUN([AHX_CONFIG_FORMAT_ATTRIBUTE], ]) dnl Check how to mark function arguments as unused. -dnl result in HAVE_ATTR_UNUSED. +dnl result in HAVE_ATTR_UNUSED. dnl Make sure you include AHX_CONFIG_UNUSED_ATTRIBUTE also. AC_DEFUN([ACX_CHECK_UNUSED_ATTRIBUTE], [AC_REQUIRE([AC_PROG_CC]) @@ -525,6 +532,49 @@ if test $ac_cv_c_unused_attribute = yes; fi ])dnl +dnl Check how to mark function arguments as nonstring. +dnl result in HAVE_ATTR_NONSTRING. +dnl Make sure you include AHX_CONFIG_NONSTRING_ATTRIBUTE also. +AC_DEFUN([ACX_CHECK_NONSTRING_ATTRIBUTE], +[AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([ACX_CHECK_ERROR_FLAGS]) +BAKCFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $ERRFLAG" +AC_MSG_CHECKING(whether the C compiler (${CC-cc}) accepts the "nonstring" attribute) +AC_CACHE_VAL(ac_cv_c_nonstring_attribute, +[ac_cv_c_nonstring_attribute=no +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +struct test { + char __attribute__((nonstring)) s[1]; +}; +]], [[ + struct test t = { "1" }; + (void) t; +]])],[ac_cv_c_nonstring_attribute="yes"],[ac_cv_c_nonstring_attribute="no"]) +CFLAGS="$BAKCFLAGS" +]) + +dnl Setup ATTR_NONSTRING config.h parts. +dnl make sure you call ACX_CHECK_NONSTRING_ATTRIBUTE also. +AC_DEFUN([AHX_CONFIG_NONSTRING_ATTRIBUTE], +[ +#if defined(DOXYGEN) +# define ATTR_NONSTRING(x) x +#elif defined(__cplusplus) +# define ATTR_NONSTRING(x) __attribute__((nonstring)) x +#elif defined(HAVE_ATTR_NONSTRING) +# define ATTR_NONSTRING(x) __attribute__((nonstring)) x +#else /* !HAVE_ATTR_NONSTRING */ +# define ATTR_NONSTRING(x) x +#endif /* !HAVE_ATTR_NONSTRING */ +]) + +AC_MSG_RESULT($ac_cv_c_nonstring_attribute) +if test $ac_cv_c_nonstring_attribute = yes; then + AC_DEFINE(HAVE_ATTR_NONSTRING, 1, [Whether the C compiler accepts the "nonstring" attribute]) +fi +])dnl + dnl Pre-fun for ACX_LIBTOOL_C_ONLY AC_DEFUN([ACX_LIBTOOL_C_PRE], [ # skip these tests, we do not need them. @@ -1190,8 +1240,9 @@ dnl detect malloc and provide malloc com dnl $1: unique name for compat code AC_DEFUN([ACX_FUNC_MALLOC], [ - AC_MSG_CHECKING([for GNU libc compatible malloc]) - AC_RUN_IFELSE([AC_LANG_PROGRAM( + AC_CACHE_CHECK([for GNU libc compatible malloc],[ac_cv_func_malloc_0_nonnull], + [ + AC_RUN_IFELSE([AC_LANG_PROGRAM( [[#if defined STDC_HEADERS || defined HAVE_STDLIB_H #include #else @@ -1199,14 +1250,16 @@ char *malloc (); #endif ]], [ if(malloc(0) != 0) return 1;]) ], - [AC_MSG_RESULT([no]) - AC_LIBOBJ(malloc) - AC_DEFINE_UNQUOTED([malloc], [rpl_malloc_$1], [Define if replacement function should be used.])] , - [AC_MSG_RESULT([yes]) - AC_DEFINE([HAVE_MALLOC], 1, [If have GNU libc compatible malloc])], - [AC_MSG_RESULT([no (crosscompile)]) - AC_LIBOBJ(malloc) - AC_DEFINE_UNQUOTED([malloc], [rpl_malloc_$1], [Define if replacement function should be used.])] ) + [ac_cv_func_malloc_0_nonnull=no], + [ac_cv_func_malloc_0_nonnull=yes], + [ac_cv_func_malloc_0_nonnull="no (crosscompile)"]) + ]) + AS_IF([test "$ac_cv_func_malloc_0_nonnull" = yes], + [AC_DEFINE([HAVE_MALLOC], 1, [If have GNU libc compatible malloc])], + [ + AC_LIBOBJ(malloc) + AC_DEFINE_UNQUOTED([malloc], [rpl_malloc_$1], [Define if replacement function should be used.]) + ]) ]) dnl Define fallback for fseeko and ftello if needed. Index: usr.sbin/nsd/axfr.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/axfr.c,v diff -u -p -r1.24 axfr.c --- usr.sbin/nsd/axfr.c 3 Sep 2025 18:46:48 -0000 1.24 +++ usr.sbin/nsd/axfr.c 21 Sep 2026 16:29:06 -0000 @@ -79,11 +79,16 @@ query_axfr(struct nsd *nsd, struct query query_add_compression_domain(query, qdomain, QHEADERSZ); - assert(query->axfr_zone->soa_rrset->rr_count == 1); + if(query->axfr_zone->soa_rrset->rr_count != 1) { + VERBOSITY(2, (LOG_INFO, "axfr: zone %s soa rr count %u (expected 1), servfail for AXFR-out", domain_to_string(query->axfr_zone->apex), + (unsigned)query->axfr_zone->soa_rrset->rr_count)); + RCODE_SET(query->packet, RCODE_SERVFAIL); + return QUERY_PROCESSED; + } added = packet_encode_rr(query, query->axfr_zone->apex, - &query->axfr_zone->soa_rrset->rrs[0], - query->axfr_zone->soa_rrset->rrs[0].ttl); + query->axfr_zone->soa_rrset->rrs[0], + query->axfr_zone->soa_rrset->rrs[0]->ttl); if (!added) { /* XXX: This should never happen... generate error code? */ abort(); @@ -123,8 +128,8 @@ query_axfr(struct nsd *nsd, struct query added = packet_encode_rr( query, query->axfr_current_domain, - &query->axfr_current_rrset->rrs[query->axfr_current_rr], - query->axfr_current_rrset->rrs[query->axfr_current_rr].ttl); + query->axfr_current_rrset->rrs[query->axfr_current_rr], + query->axfr_current_rrset->rrs[query->axfr_current_rr]->ttl); if(total_added == 0) { query->maxlen = oldmaxlen; if(query_overflow(query)) { @@ -135,8 +140,20 @@ query_axfr(struct nsd *nsd, struct query } } } - if (!added) + if (!added) { + if(total_added == 0) { + /* RR wire encoding exceeds TCP_MAX_MESSAGE_LEN; cannot transmit. */ + char apexstr[MAXDOMAINLEN * 5]; + domain_to_string_buf(query->axfr_zone->apex, apexstr); + VERBOSITY(2, (LOG_ERR, "axfr: RR at %s in zone %s too large for any DNS message " + "(wire encoding exceeds %d bytes), aborting transfer", + domain_to_string(query->axfr_current_rrset->rrs[query->axfr_current_rr]->owner), + apexstr, TCP_MAX_MESSAGE_LEN)); + RCODE_SET(query->packet, RCODE_SERVFAIL); + query->axfr_is_done = 1; + } goto return_answer; + } ++total_added; ++query->axfr_current_rr; } @@ -151,11 +168,10 @@ query_axfr(struct nsd *nsd, struct query } /* Add terminating SOA RR. */ - assert(query->axfr_zone->soa_rrset->rr_count == 1); added = packet_encode_rr(query, query->axfr_zone->apex, - &query->axfr_zone->soa_rrset->rrs[0], - query->axfr_zone->soa_rrset->rrs[0].ttl); + query->axfr_zone->soa_rrset->rrs[0], + query->axfr_zone->soa_rrset->rrs[0]->ttl); if (added) { ++total_added; query->tsig_sign_it = 1; /* sign last packet */ @@ -256,10 +272,10 @@ 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", + 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"):"")); + (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"), @@ -269,11 +285,12 @@ static int axfr_ixfr_can_admit_query(str char a[128]; addr2str(&q->client_addr, a, sizeof(a)); #ifdef HAVE_SSL - VERBOSITY(1, (LOG_INFO, "%s for %s from %s %s %s", + VERBOSITY(1, (LOG_INFO, "%s for %s from %s%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")); + (q->tls||q->tls_auth)?(q->tls?" tls":" tls-auth"):"", + (q->tls||q->tls_auth)?" ":"", + (q->tls||q->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"), Index: usr.sbin/nsd/buffer.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/buffer.h,v diff -u -p -r1.5 buffer.h --- usr.sbin/nsd/buffer.h 3 Sep 2025 18:46:48 -0000 1.5 +++ usr.sbin/nsd/buffer.h 21 Sep 2026 16:29:06 -0000 @@ -260,6 +260,14 @@ buffer_write(buffer_type *buffer, const buffer->_position += count; } +static inline void +buffer_fill(buffer_type *buffer, const int c, size_t count) +{ + assert(buffer_available_at(buffer, buffer->_position, count)); + memset(buffer->_data + buffer->_position, c, count); + buffer->_position += count; +} + static inline int try_buffer_write_at(buffer_type *buffer, size_t at, const void *data, size_t count) { Index: usr.sbin/nsd/config.guess =================================================================== RCS file: /cvs/src/usr.sbin/nsd/config.guess,v diff -u -p -r1.5 config.guess --- usr.sbin/nsd/config.guess 6 Sep 2025 17:41:37 -0000 1.5 +++ usr.sbin/nsd/config.guess 21 Sep 2026 16:29:06 -0000 @@ -1,10 +1,10 @@ #! /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-08-22' +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. @@ -155,9 +155,6 @@ Linux|GNU|GNU/*) set_cc_for_build cat <<-EOF > "$dummy.c" - #if defined(__ANDROID__) - LIBC=android - #else #include #if defined(__UCLIBC__) LIBC=uclibc @@ -172,7 +169,6 @@ Linux|GNU|GNU/*) LIBC=musl #endif #endif - #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" @@ -463,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 ;; @@ -908,7 +904,7 @@ EOF fi ;; *:FreeBSD:*:*) - UNAME_PROCESSOR=`uname -p` + UNAME_PROCESSOR=`/usr/bin/uname -p` case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; @@ -970,37 +966,11 @@ 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 ;; aarch64: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 __ARM_EABI__ - #ifdef __ARM_PCS_VFP - ABI=eabihf - #else - ABI=eabi - #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 - eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; - esac - fi - GUESS=$CPU-unknown-linux-$LIBCABI + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be @@ -1066,16 +1036,7 @@ EOF k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; - kvx:Linux:*:*) - GUESS=$UNAME_MACHINE-unknown-linux-$LIBC - ;; - kvx:cos:*:*) - GUESS=$UNAME_MACHINE-unknown-cos - ;; - kvx:mbr:*:*) - GUESS=$UNAME_MACHINE-unknown-mbr - ;; - loongarch32:Linux:*:* | loongarch64:Linux:*:*) + loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) @@ -1190,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 @@ -1230,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 ;; @@ -1371,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 GUESS=i586-unisys-sysv4 ;; @@ -1417,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: usr.sbin/nsd/config.h.in =================================================================== RCS file: /cvs/src/usr.sbin/nsd/config.h.in,v diff -u -p -r1.46 config.h.in --- usr.sbin/nsd/config.h.in 6 Sep 2025 17:41:37 -0000 1.46 +++ usr.sbin/nsd/config.h.in 21 Sep 2026 16:29:06 -0000 @@ -46,22 +46,22 @@ /* Define to the default facility for syslog. */ #undef FACILITY -/* Define to 1 if you have the 'accept4' function. */ +/* Define to 1 if you have the `accept4' function. */ #undef HAVE_ACCEPT4 -/* Define to 1 if you have the 'alarm' function. */ +/* Define to 1 if you have the `alarm' function. */ #undef HAVE_ALARM -/* Define to 1 if you have the 'arc4random' function. */ +/* Define to 1 if you have the `arc4random' function. */ #undef HAVE_ARC4RANDOM -/* Define to 1 if you have the 'arc4random_uniform' function. */ +/* Define to 1 if you have the `arc4random_uniform' function. */ #undef HAVE_ARC4RANDOM_UNIFORM /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H -/* Define to 1 if you have the 'ASN1_STRING_get0_data' function. */ +/* 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 */ @@ -76,145 +76,148 @@ /* Whether the C compiler accepts the "weak" attribute */ #undef HAVE_ATTR_WEAK -/* Define to 1 if you have the 'b64_ntop' function. */ +/* Define to 1 if you have the `b64_ntop' function. */ #undef HAVE_B64_NTOP -/* Define to 1 if you have the 'b64_pton' function. */ +/* Define to 1 if you have the `b64_pton' function. */ #undef HAVE_B64_PTON -/* Define to 1 if you have the 'basename' function. */ +/* Define to 1 if you have the `basename' function. */ #undef HAVE_BASENAME -/* Define to 1 if your system has a working 'chown' function. */ +/* Define to 1 if your system has a working `chown' function. */ #undef HAVE_CHOWN -/* Define to 1 if you have the 'chroot' function. */ +/* Define to 1 if you have the `chroot' function. */ #undef HAVE_CHROOT -/* Define to 1 if you have the 'clock_gettime' function. */ +/* Define to 1 if you have the `clock_gettime' function. */ #undef HAVE_CLOCK_GETTIME -/* Define to 1 if the system has the type 'cpuid_t'. */ +/* Define to 1 if the system has the type `cpuid_t'. */ #undef HAVE_CPUID_T -/* Define to 1 if the system has the type 'cpuset_t'. */ +/* Define to 1 if the system has the type `cpuset_t'. */ #undef HAVE_CPUSET_T -/* Define to 1 if the system has the type 'cpu_set_t'. */ +/* Define to 1 if the system has the type `cpu_set_t'. */ #undef HAVE_CPU_SET_T -/* Define to 1 if you have the 'CRYPTO_memcmp' function. */ +/* Define to 1 if you have the `CRYPTO_memcmp' function. */ #undef HAVE_CRYPTO_MEMCMP /* if time.h provides ctime_r prototype */ #undef HAVE_CTIME_R_PROTO -/* Define to 1 if you have the declaration of 'reallocarray', and to 0 if you +/* Define to 1 if you have the declaration of `reallocarray', and to 0 if you don't. */ #undef HAVE_DECL_REALLOCARRAY -/* Define to 1 if you have the declaration of 'SSL_CTX_set_ecdh_auto', and to +/* Define to 1 if you have the declaration of `SSL_CTX_set_ecdh_auto', and to 0 if you don't. */ #undef HAVE_DECL_SSL_CTX_SET_ECDH_AUTO -/* Define to 1 if you have the declaration of 'SSL_CTX_set_tmp_ecdh', and to 0 +/* Define to 1 if you have the declaration of `SSL_CTX_set_tmp_ecdh', and to 0 if you don't. */ #undef HAVE_DECL_SSL_CTX_SET_TMP_ECDH -/* Define to 1 if you have the 'dup2' function. */ +/* Define to 1 if you have the `dup2' function. */ #undef HAVE_DUP2 -/* Define to 1 if you have the 'EC_KEY_new_by_curve_name' function. */ +/* Define to 1 if you have the `EC_KEY_new_by_curve_name' function. */ #undef HAVE_EC_KEY_NEW_BY_CURVE_NAME /* Define to 1 if you have the header file. */ #undef HAVE_ENDIAN_H -/* Define to 1 if you have the 'endpwent' function. */ +/* Define to 1 if you have the `endpwent' function. */ #undef HAVE_ENDPWENT -/* Define to 1 if you have the 'ERR_load_crypto_strings' function. */ +/* Define to 1 if you have the `ERR_load_crypto_strings' function. */ #undef HAVE_ERR_LOAD_CRYPTO_STRINGS -/* Define to 1 if you have the 'ERR_load_SSL_strings' function. */ +/* Define to 1 if you have the `ERR_load_SSL_strings' function. */ #undef HAVE_ERR_LOAD_SSL_STRINGS -/* Define to 1 if you have the 'event_base_free' function. */ +/* Define to 1 if you have the `event_base_free' function. */ #undef HAVE_EVENT_BASE_FREE -/* Define to 1 if you have the 'event_base_get_method' function. */ +/* Define to 1 if you have the `event_base_get_method' function. */ #undef HAVE_EVENT_BASE_GET_METHOD -/* Define to 1 if you have the 'event_base_new' function. */ +/* Define to 1 if you have the `event_base_new' function. */ #undef HAVE_EVENT_BASE_NEW -/* Define to 1 if you have the 'event_base_once' function. */ +/* Define to 1 if you have the `event_base_once' function. */ #undef HAVE_EVENT_BASE_ONCE /* Define to 1 if you have the header file. */ #undef HAVE_EVENT_H -/* Define to 1 if you have the 'evhttp_free' function. */ +/* Define to 1 if you have the `evhttp_free' function. */ #undef HAVE_EVHTTP_FREE -/* Define to 1 if you have the 'EVP_cleanup' function. */ +/* Define to 1 if you have the `EVP_cleanup' function. */ #undef HAVE_EVP_CLEANUP -/* Define to 1 if you have the 'EVP_MAC_CTX_get_mac_size' function. */ +/* Define to 1 if you have the `EVP_MAC_CTX_get_mac_size' function. */ #undef HAVE_EVP_MAC_CTX_GET_MAC_SIZE -/* Define to 1 if you have the 'EVP_MAC_CTX_new' function. */ +/* Define to 1 if you have the `EVP_MAC_CTX_new' function. */ #undef HAVE_EVP_MAC_CTX_NEW -/* Define to 1 if you have the 'EVP_MAC_CTX_set_params' function. */ +/* Define to 1 if you have the `EVP_MAC_CTX_set_params' function. */ #undef HAVE_EVP_MAC_CTX_SET_PARAMS -/* Define to 1 if you have the 'EVP_PKEY_get0_type_name' function. */ +/* Define to 1 if you have the `EVP_PKEY_get0_type_name' function. */ #undef HAVE_EVP_PKEY_GET0_TYPE_NAME -/* Define to 1 if you have the 'ev_default_loop' function. */ +/* Define to 1 if you have the `ev_default_loop' function. */ #undef HAVE_EV_DEFAULT_LOOP -/* Define to 1 if you have the 'ev_loop' function. */ +/* Define to 1 if you have the `ev_loop' function. */ #undef HAVE_EV_LOOP -/* Define to 1 if you have the 'explicit_bzero' function. */ +/* Define to 1 if you have the `explicit_bzero' function. */ #undef HAVE_EXPLICIT_BZERO /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H -/* Define to 1 if you have the 'fork' function. */ +/* Define to 1 if you have the `fork' function. */ #undef HAVE_FORK -/* Define to 1 if you have the 'freeaddrinfo' function. */ +/* Define to 1 if you have the `freeaddrinfo' function. */ #undef HAVE_FREEADDRINFO -/* Define to 1 if fseeko (and ftello) are declared in stdio.h. */ +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ #undef HAVE_FSEEKO -/* Define to 1 if you have the 'gai_strerror' function. */ +/* Define to 1 if you have the `fstrm_tcp_writer_options_init' function. */ +#undef HAVE_FSTRM_TCP_WRITER_OPTIONS_INIT + +/* Define to 1 if you have the `gai_strerror' function. */ #undef HAVE_GAI_STRERROR -/* Define to 1 if you have the 'getaddrinfo' function. */ +/* Define to 1 if you have the `getaddrinfo' function. */ #undef HAVE_GETADDRINFO -/* Define to 1 if you have the 'gethostname' function. */ +/* Define to 1 if you have the `gethostname' function. */ #undef HAVE_GETHOSTNAME -/* Define to 1 if you have the 'getifaddrs' function. */ +/* Define to 1 if you have the `getifaddrs' function. */ #undef HAVE_GETIFADDRS -/* Define to 1 if you have the 'getnameinfo' function. */ +/* Define to 1 if you have the `getnameinfo' function. */ #undef HAVE_GETNAMEINFO -/* Define to 1 if you have the 'getpwnam' function. */ +/* Define to 1 if you have the `getpwnam' function. */ #undef HAVE_GETPWNAM -/* Define to 1 if you have the 'getrandom' function. */ +/* Define to 1 if you have the `getrandom' function. */ #undef HAVE_GETRANDOM -/* Define to 1 if you have the 'glob' function. */ +/* Define to 1 if you have the `glob' function. */ #undef HAVE_GLOB /* Define to 1 if you have the header file. */ @@ -223,65 +226,65 @@ /* Define to 1 if you have the header file. */ #undef HAVE_GRP_H -/* Define to 1 if you have the 'HMAC_CTX_new' function. */ +/* Define to 1 if you have the `HMAC_CTX_new' function. */ #undef HAVE_HMAC_CTX_NEW -/* Define to 1 if you have the 'HMAC_CTX_reset' function. */ +/* Define to 1 if you have the `HMAC_CTX_reset' function. */ #undef HAVE_HMAC_CTX_RESET /* Define to 1 if you have the header file. */ #undef HAVE_IFADDRS_H -/* Define to 1 if you have the 'inet_aton' function. */ +/* Define to 1 if you have the `inet_aton' function. */ #undef HAVE_INET_ATON -/* Define to 1 if you have the 'inet_ntop' function. */ +/* Define to 1 if you have the `inet_ntop' function. */ #undef HAVE_INET_NTOP -/* Define to 1 if you have the 'inet_pton' function. */ +/* Define to 1 if you have the `inet_pton' function. */ #undef HAVE_INET_PTON -/* Define to 1 if you have the 'initgroups' function. */ +/* Define to 1 if you have the `initgroups' function. */ #undef HAVE_INITGROUPS /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H -/* Define to 1 if you have the 'crypto' library (-lcrypto). */ +/* Define to 1 if you have the `crypto' library (-lcrypto). */ #undef HAVE_LIBCRYPTO /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H -/* Define to 1 if you have the 'localtime_r' function. */ +/* Define to 1 if you have the `localtime_r' function. */ #undef HAVE_LOCALTIME_R /* Define to 1 if you have the header file. */ #undef HAVE_LOGIN_CAP_H -/* Define to 1 if your system has a GNU libc compatible 'malloc' function, and +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and to 0 otherwise. */ #undef HAVE_MALLOC -/* Define to 1 if you have the 'memcpy' function. */ +/* Define to 1 if you have the `memcpy' function. */ #undef HAVE_MEMCPY -/* Define to 1 if you have the 'memmove' function. */ +/* Define to 1 if you have the `memmove' function. */ #undef HAVE_MEMMOVE -/* Define to 1 if you have the 'memset' function. */ +/* Define to 1 if you have the `memset' function. */ #undef HAVE_MEMSET /* Define to 1 if you have the header file. */ #undef HAVE_MINIX_CONFIG_H -/* Define to 1 if you have the 'mmap' function. */ +/* Define to 1 if you have the `mmap' function. */ #undef HAVE_MMAP /* If sys/socket.h has a struct mmsghdr. */ #undef HAVE_MMSGHDR -/* Define to 1 if you have the 'munmap' function. */ +/* Define to 1 if you have the `munmap' function. */ #undef HAVE_MUNMAP /* Define to 1 if you have the header file. */ @@ -299,10 +302,10 @@ /* Define to 1 if you have the header file. */ #undef HAVE_OPENSSL_ERR_H -/* Define to 1 if you have the 'OPENSSL_init_crypto' function. */ +/* Define to 1 if you have the `OPENSSL_init_crypto' function. */ #undef HAVE_OPENSSL_INIT_CRYPTO -/* Define to 1 if you have the 'OPENSSL_init_ssl' function. */ +/* Define to 1 if you have the `OPENSSL_init_ssl' function. */ #undef HAVE_OPENSSL_INIT_SSL /* Define to 1 if you have the header file. */ @@ -317,16 +320,16 @@ /* Define to 1 if you have the header file. */ #undef HAVE_OPENSSL_X509V3_H -/* Define to 1 if you have the 'ppoll' function. */ +/* Define to 1 if you have the `ppoll' function. */ #undef HAVE_PPOLL -/* Define to 1 if you have the 'pselect' function. */ +/* Define to 1 if you have the `pselect' function. */ #undef HAVE_PSELECT /* if sys/select.h provides pselect prototype */ #undef HAVE_PSELECT_PROTO -/* Define to 1 if you have the 'pwrite' function. */ +/* Define to 1 if you have the `pwrite' function. */ #undef HAVE_PWRITE /* If we have reallocarray(3) */ @@ -344,49 +347,49 @@ /* Define if sendmmsg is implemented */ #undef HAVE_SENDMMSG -/* Define to 1 if you have the 'setproctitle' function. */ +/* Define to 1 if you have the `setproctitle' function. */ #undef HAVE_SETPROCTITLE -/* Define to 1 if you have the 'setregid' function. */ +/* Define to 1 if you have the `setregid' function. */ #undef HAVE_SETREGID -/* Define to 1 if you have the 'setresgid' function. */ +/* Define to 1 if you have the `setresgid' function. */ #undef HAVE_SETRESGID -/* Define to 1 if you have the 'setresuid' function. */ +/* Define to 1 if you have the `setresuid' function. */ #undef HAVE_SETRESUID -/* Define to 1 if you have the 'setreuid' function. */ +/* Define to 1 if you have the `setreuid' function. */ #undef HAVE_SETREUID -/* Define to 1 if you have the 'setusercontext' function. */ +/* Define to 1 if you have the `setusercontext' function. */ #undef HAVE_SETUSERCONTEXT -/* Define to 1 if you have the 'SHA1_Init' function. */ +/* Define to 1 if you have the `SHA1_Init' function. */ #undef HAVE_SHA1_INIT -/* Define to 1 if you have the 'sigaction' function. */ +/* Define to 1 if you have the `sigaction' function. */ #undef HAVE_SIGACTION /* Define to 1 if you have the header file. */ #undef HAVE_SIGNAL_H -/* Define to 1 if you have the 'sigprocmask' function. */ +/* Define to 1 if you have the `sigprocmask' function. */ #undef HAVE_SIGPROCMASK -/* Define to 1 if you have the 'snprintf' function. */ +/* Define to 1 if you have the `snprintf' function. */ #undef HAVE_SNPRINTF -/* Define to 1 if you have the 'socket' function. */ +/* Define to 1 if you have the `socket' function. */ #undef HAVE_SOCKET /* Define if you have the SSL libraries installed. */ #undef HAVE_SSL -/* Define to 1 if you have the 'SSL_CTX_set_security_level' function. */ +/* Define to 1 if you have the `SSL_CTX_set_security_level' function. */ #undef HAVE_SSL_CTX_SET_SECURITY_LEVEL -/* Define to 1 if you have the 'SSL_get1_peer_certificate' function. */ +/* Define to 1 if you have the `SSL_get1_peer_certificate' function. */ #undef HAVE_SSL_GET1_PEER_CERTIFICATE /* Define to 1 if you have the header file. */ @@ -404,19 +407,19 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H -/* Define to 1 if you have the 'strcasecmp' function. */ +/* Define to 1 if you have the `strcasecmp' function. */ #undef HAVE_STRCASECMP -/* Define to 1 if you have the 'strchr' function. */ +/* Define to 1 if you have the `strchr' function. */ #undef HAVE_STRCHR -/* Define to 1 if you have the 'strdup' function. */ +/* Define to 1 if you have the `strdup' function. */ #undef HAVE_STRDUP -/* Define to 1 if you have the 'strerror' function. */ +/* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR -/* Define to 1 if you have the 'strftime' function. */ +/* Define to 1 if you have the `strftime' function. */ #undef HAVE_STRFTIME /* Define to 1 if you have the header file. */ @@ -425,34 +428,34 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H -/* Define to 1 if you have the 'strlcat' function. */ +/* Define to 1 if you have the `strlcat' function. */ #undef HAVE_STRLCAT -/* Define to 1 if you have the 'strlcpy' function. */ +/* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY -/* Define to 1 if you have the 'strncasecmp' function. */ +/* Define to 1 if you have the `strncasecmp' function. */ #undef HAVE_STRNCASECMP -/* Define to 1 if you have the 'strptime' function. */ +/* Define to 1 if you have the `strptime' function. */ #undef HAVE_STRPTIME -/* Define to 1 if you have the 'strtol' function. */ +/* Define to 1 if you have the `strtol' function. */ #undef HAVE_STRTOL -/* Define to 1 if 'sun_len' is a member of 'struct sockaddr_un'. */ +/* Define to 1 if `sun_len' is a member of `struct sockaddr_un'. */ #undef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN -/* Define to 1 if 'st_mtimensec' is a member of 'struct stat'. */ +/* Define to 1 if `st_mtimensec' is a member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_MTIMENSEC -/* Define to 1 if 'st_mtim.tv_nsec' is a member of 'struct stat'. */ +/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC /* If time.h has a struct timespec (for pselect). */ #undef HAVE_STRUCT_TIMESPEC -/* Define to 1 if you have the 'sysconf' function. */ +/* Define to 1 if you have the `sysconf' function. */ #undef HAVE_SYSCONF /* Define to 1 if you have the header file. */ @@ -503,7 +506,7 @@ /* Define if TLS 1.3 is supported by OpenSSL */ #undef HAVE_TLS_1_3 -/* Define to 1 if you have the 'tzset' function. */ +/* Define to 1 if you have the `tzset' function. */ #undef HAVE_TZSET /* Define to 1 if you have the header file. */ @@ -512,7 +515,7 @@ /* Define this if you have double va_list definitions. */ #undef HAVE_VA_LIST_DOUBLE_DEF -/* Define to 1 if you have the 'vfork' function. */ +/* Define to 1 if you have the `vfork' function. */ #undef HAVE_VFORK /* Define to 1 if you have the header file. */ @@ -521,13 +524,13 @@ /* Define to 1 if you have the header file. */ #undef HAVE_WCHAR_H -/* Define to 1 if 'fork' works. */ +/* Define to 1 if `fork' works. */ #undef HAVE_WORKING_FORK -/* Define to 1 if 'vfork' works. */ +/* Define to 1 if `vfork' works. */ #undef HAVE_WORKING_VFORK -/* Define to 1 if you have the 'writev' function. */ +/* Define to 1 if you have the `writev' function. */ #undef HAVE_WRITEV /* Define to the default nsd identity. */ @@ -542,6 +545,9 @@ /* Define to the maximum message length to pass to syslog. */ #undef MAXSYSLOGMSGLEN +/* Define to the default maximum number of CNAMEs to follow. */ +#undef MAX_CNAME_CHAIN + /* Define this to cleanup memory at exit (eg. for valgrind, etc.) */ #undef MEMCLEAN @@ -554,6 +560,10 @@ /* Define if mkdir has one argument. */ #undef MKDIR_HAS_ONE_ARG +/* Define this to enable experimental support for more than one catalog + consumer zone. */ +#undef MULTIPLE_CATALOG_CONSUMER_ZONES + /* Undefine this to enable internal runtime checks. */ #undef NDEBUG @@ -616,13 +626,13 @@ /* NSD shared files dir */ #undef SHAREDFILESDIR -/* The size of 'off_t', as computed by sizeof. */ +/* The size of `off_t', as computed by sizeof. */ #undef SIZEOF_OFF_T -/* The size of 'void*', as computed by sizeof. */ +/* The size of `void*', as computed by sizeof. */ #undef SIZEOF_VOIDP -/* Define to 1 if all of the C89 standard headers exist (not just the ones +/* 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 @@ -677,7 +687,7 @@ /* Define this to configure to use the radix tree. */ #undef USE_RADIX_TREE -/* Enable extensions on AIX, Interix, z/OS. */ +/* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif @@ -738,15 +748,11 @@ #ifndef __STDC_WANT_IEC_60559_DFP_EXT__ # undef __STDC_WANT_IEC_60559_DFP_EXT__ #endif -/* Enable extensions specified by C23 Annex F. */ -#ifndef __STDC_WANT_IEC_60559_EXT__ -# undef __STDC_WANT_IEC_60559_EXT__ -#endif /* Enable extensions specified by ISO/IEC TS 18661-4:2015. */ #ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__ # undef __STDC_WANT_IEC_60559_FUNCS_EXT__ #endif -/* Enable extensions specified by C23 Annex H and ISO/IEC TS 18661-3:2015. */ +/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */ #ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ # undef __STDC_WANT_IEC_60559_TYPES_EXT__ #endif @@ -790,8 +796,8 @@ /* Pathname to where the NSD transfer dir is created. */ #undef XFRDIR -/* Define to 1 if 'lex' declares 'yytext' as a 'char *' by default, not a - 'char[]'. */ +/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a + `char[]'. */ #undef YYTEXT_POINTER /* Pathname to the NSD zone list file. */ @@ -803,31 +809,25 @@ /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS -/* Define to 1 if necessary to make fseeko visible. */ +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ #undef _LARGEFILE_SOURCE -/* Define to 1 on platforms where this makes off_t a 64-bit type. */ +/* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES /* Enable for compile on Minix */ #undef _NETBSD_SOURCE -/* Number of bits in time_t, on hosts where this is settable. */ -#undef _TIME_BITS - -/* Define to 1 on platforms where this makes time_t a 64-bit type. */ -#undef __MINGW_USE_VC2005_COMPAT - -/* Define to empty if 'const' does not conform to ANSI C. */ +/* Define to empty if `const' does not conform to ANSI C. */ #undef const -/* Define as 'int' if doesn't define. */ +/* Define to `int' if doesn't define. */ #undef gid_t /* in_addr_t */ #undef in_addr_t -/* Define to '__inline__' or '__inline' if that's what the C compiler +/* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline @@ -848,7 +848,7 @@ /* Define to rpl_malloc if the replacement function should be used. */ #undef malloc -/* Define to 'long int' if does not define. */ +/* Define to `long int' if does not define. */ #undef off_t /* Define as a signed integer type capable of holding a process identifier. */ @@ -857,7 +857,7 @@ /* Define "sig_atomic_t" to "int" if "sig_atomic_t" is missing */ #undef sig_atomic_t -/* Define as 'unsigned int' if doesn't define. */ +/* Define to `unsigned int' if does not define. */ #undef size_t /* Define "socklen_t" to "int" if "socklen_t" is missing */ @@ -872,7 +872,7 @@ /* Define "suseconds_t" to "time_t" if "suseconds_t" is missing */ #undef suseconds_t -/* Define as 'int' if doesn't define. */ +/* Define to `int' if doesn't define. */ #undef uid_t /* Define "uint16_t" to "unsigned short" if "uint16_t" is missing */ @@ -890,7 +890,7 @@ /* Define "uintptr_t" to "void*" if "uintptr_t" is missing */ #undef uintptr_t -/* Define as 'fork' if 'vfork' does not work. */ +/* Define as `fork' if `vfork' does not work. */ #undef vfork Index: usr.sbin/nsd/config.sub =================================================================== RCS file: /cvs/src/usr.sbin/nsd/config.sub,v diff -u -p -r1.5 config.sub --- usr.sbin/nsd/config.sub 6 Sep 2025 17:41:37 -0000 1.5 +++ usr.sbin/nsd/config.sub 21 Sep 2026 16:29:06 -0000 @@ -1,10 +1,10 @@ #! /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-09-19' +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 &2 + echo Invalid configuration \`"$1"\': more than four components >&2 exit 1 ;; *-*-*-*) @@ -145,8 +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-* \ - | windows-* ) + | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 basic_os=$maybe_os ;; @@ -944,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) @@ -1076,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-*) @@ -1181,7 +1180,7 @@ case $cpu-$vendor in case $cpu in 1750a | 580 \ | a29k \ - | aarch64 | aarch64_be | aarch64c | arm64ec \ + | aarch64 | aarch64_be \ | abacus \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \ @@ -1200,23 +1199,45 @@ case $cpu-$vendor in | d10v | d30v | dlx | dsp16xx \ | e2k | elxsi | epiphany \ | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \ - | javascript \ | h8300 | h8500 \ | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i*86 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ - | kvx \ | 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 \ | m88110 | m88k | maxq | mb | mcore | mep | metag \ | microblaze | microblazeel \ - | mips* \ + | 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 \ @@ -1264,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 @@ -1285,12 +1306,11 @@ esac # Decode manufacturer-specific aliases for certain operating systems. -if test x"$basic_os" != x +if test x$basic_os != x then # First recognize some ad-hoc cases, or perhaps split kernel-os, or else just # set os. -obj= case $basic_os in gnu/linux*) kernel=linux @@ -1321,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 @@ -1490,16 +1506,10 @@ case $os in os=eabi ;; *) - os= - obj=elf + os=elf ;; esac ;; - aout* | coff* | elf* | pe*) - # These are machine code file formats, not OSes - obj=$os - os= - ;; *) # No normalization, but not necessarily accepted, that comes below. ;; @@ -1518,15 +1528,12 @@ else # system, and we'll never get to this point. kernel= -obj= case $cpu-$vendor in score-*) - os= - obj=elf + os=elf ;; spu-*) - os= - obj=elf + os=elf ;; *-acorn) os=riscix1.2 @@ -1536,35 +1543,28 @@ case $cpu-$vendor in os=gnu ;; arm*-semi) - os= - obj=aout + os=aout ;; c4x-* | tic4x-*) - os= - obj=coff + os=coff ;; c8051-*) - os= - obj=elf + os=elf ;; clipper-intergraph) os=clix ;; hexagon-*) - os= - obj=elf + os=elf ;; tic54x-*) - os= - obj=coff + os=coff ;; tic55x-*) - os= - obj=coff + os=coff ;; tic6x-*) - os= - obj=coff + os=coff ;; # This must come before the *-dec entry. pdp10-*) @@ -1586,24 +1586,19 @@ case $cpu-$vendor in os=sunos3 ;; m68*-cisco) - os= - obj=aout + os=aout ;; mep-*) - os= - obj=elf + os=elf ;; mips*-cisco) - os= - obj=elf + os=elf ;; mips*-*) - os= - obj=elf + os=elf ;; or32-*) - os= - obj=coff + os=coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=sysv3 @@ -1612,8 +1607,7 @@ case $cpu-$vendor in os=sunos4.1.1 ;; pru-*) - os= - obj=elf + os=elf ;; *-be) os=beos @@ -1694,12 +1688,10 @@ case $cpu-$vendor in os=uxpv ;; *-rom68k) - os= - obj=coff + os=coff ;; *-*bug) - os= - obj=coff + os=coff ;; *-apple) os=macos @@ -1717,8 +1709,7 @@ esac fi -# Now, validate our (potentially fixed-up) individual pieces (OS, OBJ). - +# 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*) @@ -1729,9 +1720,6 @@ case $os in # VxWorks passes extra cpu info in the 4th filed. simlinux | simwindows | spe) ;; - # See `case $cpu-$os` validation below - ghcjs) - ;; # Now accept the basic system types. # The portable systems comes first. # Each alternative MUST end in a * to match a version number. @@ -1740,7 +1728,7 @@ case $os in | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ | hiux* | abug | nacl* | netware* | windows* \ - | os9* | macos* | osx* | ios* | tvos* | watchos* \ + | os9* | macos* | osx* | ios* \ | mpw* | magic* | mmixware* | mon960* | lnews* \ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ @@ -1749,11 +1737,11 @@ case $os in | mirbsd* | netbsd* | dicos* | openedition* | ose* \ | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \ | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ - | bosx* | nextstep* | cxux* | oabi* \ - | ptx* | ecoff* | winnt* | domain* | vsta* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ | udi* | lites* | ieee* | go32* | aux* | hcos* \ | chorusrdb* | cegcc* | glidix* | serenity* \ - | cygwin* | msys* | moss* | proelf* | rtems* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ | midipix* | mingw32* | mingw64* | mint* \ | uxpv* | beos* | mpeix* | udk* | moxiebox* \ | interix* | uwin* | mks* | rhapsody* | darwin* \ @@ -1766,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* | cos* | mbr* ) + | fiwix* ) ;; # This one is extra strict with allowed versions sco3.2v2 | sco3.2v[4-9]* | sco5v6*) @@ -1774,99 +1762,41 @@ case $os in ;; none) ;; - kernel* | msvc* ) - # Restricted further below - ;; - '') - if test x"$obj" = x - then - echo "Invalid configuration '$1': Blank OS only allowed with explicit machine code file format" 1>&2 - fi - ;; - *) - echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 - exit 1 - ;; -esac - -case $obj in - aout* | coff* | elf* | pe*) - ;; - '') - # empty is fine - ;; *) - echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 - exit 1 - ;; -esac - -# Here we handle the constraint that a (synthetic) cpu and os are -# valid only in combination with each other and nowhere else. -case $cpu-$os in - # The "javascript-unknown-ghcjs" triple is used by GHC; we - # accept it here in order to tolerate that, but reject any - # variations. - javascript-ghcjs) - ;; - javascript-* | *-ghcjs) - echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 + 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-$obj in - linux-gnu*- | linux-dietlibc*- | linux-android*- | linux-newlib*- \ - | linux-musl*- | linux-relibc*- | linux-uclibc*- | linux-mlibc*- ) - ;; - uclinux-uclibc*- ) - ;; - managarm-mlibc*- | managarm-kernel*- ) +case $kernel-$os in + linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ + | linux-musl* | linux-relibc* | linux-uclibc* ) ;; - windows*-msvc*-) + uclinux-uclibc* ) ;; - -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 ;; - *-msvc*- ) - echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 - exit 1 - ;; - kfreebsd*-gnu*- | kopensolaris*-gnu*-) + kfreebsd*-gnu* | kopensolaris*-gnu*) ;; - vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) + vxworks-simlinux | vxworks-simwindows | vxworks-spe) ;; - nto-qnx*-) - ;; - os2-emx-) + nto-qnx*) ;; - *-eabi*- | *-gnueabi*-) + os2-emx) ;; - none--*) - # None (no kernel, i.e. freestanding / bare metal), - # can be paired with an machine code file format + *-eabi* | *-gnueabi*) ;; - -*-) + -*) # Blank kernel with real OS is always fine. ;; - --*) - # Blank kernel and OS with real machine code file format 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 @@ -1949,7 +1879,7 @@ case $vendor in ;; esac -echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" +echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: Index: usr.sbin/nsd/configlexer.lex =================================================================== RCS file: /cvs/src/usr.sbin/nsd/configlexer.lex,v diff -u -p -r1.27 configlexer.lex --- usr.sbin/nsd/configlexer.lex 6 Sep 2025 17:41:37 -0000 1.27 +++ usr.sbin/nsd/configlexer.lex 21 Sep 2026 16:29:06 -0000 @@ -213,6 +213,7 @@ tcp-query-count{COLON} { LEXOUT(("v(%s) tcp-timeout{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_TIMEOUT;} tcp-mss{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_MSS;} outgoing-tcp-mss{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_TCP_MSS;} +tcp-listen-queue{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_LISTEN_QUEUE;} ipv4-edns-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IPV4_EDNS_SIZE;} ipv6-edns-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IPV6_EDNS_SIZE;} pidfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PIDFILE;} @@ -314,6 +315,7 @@ tls-auth-port{COLON} { LEXOUT(("v(%s) " 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; } +allow-proxy{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_PROXY;} 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;} @@ -336,6 +338,7 @@ xdp-program-path{COLON} { LEXOUT(("v(%s xdp-program-load{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XDP_PROGRAM_LOAD; } xdp-bpffs-path{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XDP_BPFFS_PATH; } xdp-force-copy{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XDP_FORCE_COPY; } +udp-padding-port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_UDP_PADDING_PORT;} {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} servers={UNQUOTEDLETTER}* { Index: usr.sbin/nsd/configparser.y =================================================================== RCS file: /cvs/src/usr.sbin/nsd/configparser.y,v diff -u -p -r1.41 configparser.y --- usr.sbin/nsd/configparser.y 6 Sep 2025 17:41:37 -0000 1.41 +++ usr.sbin/nsd/configparser.y 21 Sep 2026 16:29:06 -0000 @@ -106,6 +106,7 @@ struct component { %token VAR_TCP_TIMEOUT %token VAR_TCP_MSS %token VAR_OUTGOING_TCP_MSS +%token VAR_TCP_LISTEN_QUEUE %token VAR_IPV4_EDNS_SIZE %token VAR_IPV6_EDNS_SIZE %token VAR_STATISTICS @@ -133,6 +134,7 @@ struct component { %token VAR_TLS_AUTH_XFR_ONLY %token VAR_TLS_CERT_BUNDLE %token VAR_PROXY_PROTOCOL_PORT +%token VAR_ALLOW_PROXY %token VAR_CPU_AFFINITY %token VAR_XFRD_CPU_AFFINITY %token VAR_SERVER_CPU_AFFINITY @@ -143,6 +145,7 @@ struct component { %token VAR_METRICS_INTERFACE %token VAR_METRICS_PORT %token VAR_METRICS_PATH +%token VAR_UDP_PADDING_PORT /* dnstap */ %token VAR_DNSTAP @@ -295,9 +298,25 @@ server_option: | VAR_IP_FREEBIND boolean { cfg_parser->opt->ip_freebind = $2; } | VAR_SEND_BUFFER_SIZE number - { cfg_parser->opt->send_buffer_size = (int)$2; } + { + if ($2 > 0) { + cfg_parser->opt->send_buffer_size = (int)$2; + } else if ($2 == 0) { + /* do nothing and use the default value */ + } else { + yyerror("expected a number equal to or greater than zero"); + } + } | VAR_RECEIVE_BUFFER_SIZE number - { cfg_parser->opt->receive_buffer_size = (int)$2; } + { + if ($2 > 0) { + cfg_parser->opt->receive_buffer_size = (int)$2; + } else if ($2 == 0) { + /* do nothing and use the default value */ + } else { + yyerror("expected a number equal to or greater than zero"); + } + } | VAR_DEBUG_MODE boolean { cfg_parser->opt->debug_mode = $2; } | VAR_USE_SYSTEMD boolean @@ -374,6 +393,9 @@ server_option: { cfg_parser->opt->tcp_mss = (int)$2; } | VAR_OUTGOING_TCP_MSS number { cfg_parser->opt->outgoing_tcp_mss = (int)$2; } + | VAR_TCP_LISTEN_QUEUE STRING + { /* With atoi is it allowed to be negative, for system chosen result. */ + cfg_parser->opt->tcp_listen_queue = atoi($2); } | VAR_IPV4_EDNS_SIZE number { cfg_parser->opt->ipv4_edns_size = (size_t)$2; } | VAR_IPV6_EDNS_SIZE number @@ -519,12 +541,18 @@ server_option: { cfg_parser->opt->tls_cert_bundle = region_strdup(cfg_parser->opt->region, $2); } | VAR_PROXY_PROTOCOL_PORT number { - struct proxy_protocol_port_list* elem = region_alloc_zero( + struct port_list* elem = region_alloc_zero( cfg_parser->opt->region, sizeof(*elem)); elem->port = $2; elem->next = cfg_parser->opt->proxy_protocol_port; cfg_parser->opt->proxy_protocol_port = elem; } + | VAR_ALLOW_PROXY STRING + { + acl_options_type* acl = parse_acl_info(cfg_parser->opt->region, $2, + "NOKEY"); + append_acl(&cfg_parser->opt->allow_proxy, acl); + } | VAR_ANSWER_COOKIE boolean { cfg_parser->opt->answer_cookie = $2; } | VAR_COOKIE_SECRET STRING @@ -660,6 +688,14 @@ server_option: #ifdef USE_METRICS cfg_parser->opt->metrics_path = region_strdup(cfg_parser->opt->region, $2); #endif /* USE_METRICS */ + } + | VAR_UDP_PADDING_PORT number + { + struct port_list* elem = region_alloc_zero( + cfg_parser->opt->region, sizeof(*elem)); + elem->port = $2; + elem->next = cfg_parser->opt->udp_padding_port; + cfg_parser->opt->udp_padding_port = elem; } ; Index: usr.sbin/nsd/configure =================================================================== RCS file: /cvs/src/usr.sbin/nsd/configure,v diff -u -p -r1.63 configure --- usr.sbin/nsd/configure 6 Sep 2025 17:41:37 -0000 1.63 +++ usr.sbin/nsd/configure 21 Sep 2026 16:29:06 -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.13.0. +# Generated by GNU Autoconf 2.71 for NSD 4.15.2. # # Report bugs to . # @@ -612,8 +612,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='NSD' PACKAGE_TARNAME='nsd' -PACKAGE_VERSION='4.13.0' -PACKAGE_STRING='NSD 4.13.0' +PACKAGE_VERSION='4.15.2' +PACKAGE_STRING='NSD 4.15.2' PACKAGE_BUGREPORT='https://github.com/NLnetLabs/nsd/issues or nsd-bugs@nlnetlabs.nl' PACKAGE_URL='' @@ -782,8 +782,10 @@ enable_largefile enable_recvmmsg with_facility with_tcp_timeout +with_max_cname_chain enable_root_server enable_ipv6 +enable_multiple_catalog_zones enable_bind8_stats enable_zone_stats enable_checking @@ -1381,7 +1383,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.13.0 to adapt to many kinds of systems. +\`configure' configures NSD 4.15.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1447,7 +1449,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of NSD 4.13.0:";; + short | recursive ) echo "Configuration of NSD 4.15.2:";; esac cat <<\_ACEOF @@ -1466,6 +1468,9 @@ Optional Features: problems for IPv6 --enable-root-server Configure NSD as a root server (obsolete) --disable-ipv6 Disables IPv6 support + --enable-multiple-catzones + Enable experimental support for more than one + catalog consumer zone --disable-bind8-stats Disable BIND8 like NSTATS & XSTATS and statistics in nsd-control --disable-zone-stats Disable per-zone statistics gathering (if enabled, @@ -1525,6 +1530,8 @@ Optional Packages: --with-facility=name Syslog default facility (LOG_DAEMON) --with-tcp-timeout=number Limit the default tcp timeout + --with-max-cname-chain=number + Limit the maximum number of CNAMEs to follow --with-ssl=pathname enable SSL (will check /usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/sfw /usr/local /usr /usr/local/opt/openssl) @@ -1635,7 +1642,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -NSD configure 4.13.0 +NSD configure 4.15.2 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2292,7 +2299,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.13.0, which was +It was created by NSD $as_me 4.15.2, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -10441,8 +10448,19 @@ printf "%s\n" "#define IDENTITY \"uniden printf "%s\n" "#define VERSION PACKAGE_STRING" >>confdefs.h +case "$host" in + *bsd*|*BSD*|*linux*|*Linux*) + +printf "%s\n" "#define TCP_BACKLOG -1" >>confdefs.h + + ;; + *) + printf "%s\n" "#define TCP_BACKLOG 256" >>confdefs.h + ;; +esac + printf "%s\n" "#define TCP_PORT \"53\"" >>confdefs.h @@ -10498,6 +10516,18 @@ fi printf "%s\n" "#define TCP_TIMEOUT $tcp_timeout" >>confdefs.h +max_cname_chain=18 + +# Check whether --with-max_cname_chain was given. +if test ${with_max_cname_chain+y} +then : + withval=$with_max_cname_chain; max_cname_chain=$withval +fi + + +printf "%s\n" "#define MAX_CNAME_CHAIN $max_cname_chain" >>confdefs.h + + # Check whether --enable-root-server was given. if test ${enable_root_server+y} then : @@ -10521,6 +10551,22 @@ printf "%s\n" "#define INET6 /**/" >>con ;; esac +# Check whether --enable-multiple-catalog-zones was given. +if test ${enable_multiple_catalog_zones+y} +then : + enableval=$enable_multiple_catalog_zones; +fi + +case "$enable_multiple_catalog_zones" in + yes) + +printf "%s\n" "#define MULTIPLE_CATALOG_CONSUMER_ZONES /**/" >>confdefs.h + + ;; + no|*) + ;; +esac + # Check whether --enable-bind8-stats was given. if test ${enable_bind8_stats+y} then : @@ -12149,6 +12195,19 @@ else $as_nop as_fn_error $? "The fstrm library was not found. It is needed for dnstap, use --disable-dnstap, or install fstrm-devel" "$LINENO" 5 fi + + for ac_func in fstrm_tcp_writer_options_init +do : + ac_fn_c_check_func "$LINENO" "fstrm_tcp_writer_options_init" "ac_cv_func_fstrm_tcp_writer_options_init" +if test "x$ac_cv_func_fstrm_tcp_writer_options_init" = xyes +then : + printf "%s\n" "#define HAVE_FSTRM_TCP_WRITER_OPTIONS_INIT 1" >>confdefs.h + +else $as_nop + as_fn_error $? "The fstrm library >= 0.4 was not found. It is needed for dnstap, use --disable-dnstap, or install fstrm-devel" "$LINENO" 5 +fi + +done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing protobuf_c_message_pack" >&5 printf %s "checking for library containing protobuf_c_message_pack... " >&6; } if test ${ac_cv_search_protobuf_c_message_pack+y} @@ -12384,9 +12443,6 @@ printf "%s\n" "no" >&6; } PKG_CONFIG="" fi fi -if test -z "$PKG_CONFIG"; then - as_fn_error $? "pkg-config not found" "$LINENO" 5 -fi if test "x$enable_systemd" != xno then : @@ -13144,7 +13200,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.13.0, which was +This file was extended by NSD $as_me 4.15.2, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -13208,7 +13264,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.13.0 +NSD config.status 4.15.2 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" Index: usr.sbin/nsd/configure.ac =================================================================== RCS file: /cvs/src/usr.sbin/nsd/configure.ac,v diff -u -p -r1.62 configure.ac --- usr.sbin/nsd/configure.ac 6 Sep 2025 17:41:37 -0000 1.62 +++ usr.sbin/nsd/configure.ac 21 Sep 2026 16:29:07 -0000 @@ -7,7 +7,7 @@ sinclude(dnstap/dnstap.m4) # autoconf-2.70 is needed for @runstatedir@ AC_PREREQ([2.70]) -AC_INIT([NSD],[4.13.0],[https://github.com/NLnetLabs/nsd/issues or nsd-bugs@nlnetlabs.nl]) +AC_INIT([NSD],[4.15.2],[https://github.com/NLnetLabs/nsd/issues or nsd-bugs@nlnetlabs.nl]) AC_CONFIG_HEADERS([config.h]) # @@ -178,10 +178,7 @@ AC_ARG_WITH([user], AC_SUBST(user) AC_DEFINE_UNQUOTED(USER, ["$user"], [the user name to drop privileges to]) -m4_version_prereq([2.70], [AC_PROG_CC], [m4_warn([obsolete], -[AC_PROG_CC_STDC is obsolete; use AC_PROG_CC -])dnl -AC_REQUIRE(AC_PROG_CC)]) +m4_version_prereq([2.70], [AC_PROG_CC], [AC_PROG_CC_STDC]) AC_PROG_SED AC_PROG_AWK AC_PROG_GREP @@ -950,7 +947,16 @@ dnl Some random defines's dnl AC_DEFINE_UNQUOTED([IDENTITY], ["unidentified server"], [Define to the default nsd identity.]) AC_DEFINE_UNQUOTED([VERSION], [PACKAGE_STRING], [Define to the NSD version to answer version.server query.]) -AC_DEFINE_UNQUOTED([TCP_BACKLOG], [256], [Define to the backlog to be used with listen.]) + +case "$host" in + *bsd*|*BSD*|*linux*|*Linux*) + AC_DEFINE_UNQUOTED([TCP_BACKLOG], [-1], [Define to the backlog to be used with listen.]) + ;; + *) + AC_DEFINE_UNQUOTED([TCP_BACKLOG], [256], [Define to the backlog to be used with listen.]) + ;; +esac + AC_DEFINE_UNQUOTED([TCP_PORT], ["53"], [Define to the default tcp port.]) AC_DEFINE_UNQUOTED([TCP_MAX_MESSAGE_LEN], [65535], [Define to the default maximum message length.]) AC_DEFINE_UNQUOTED([UDP_PORT], ["53"], [Define to the default udp port.]) @@ -981,6 +987,16 @@ AC_ARG_WITH([tcp_timeout], AC_DEFINE_UNQUOTED([TCP_TIMEOUT], $tcp_timeout, [Define to the default tcp timeout.]) dnl +dnl Determine the maximum CNAME chain length to return +dnl Defaults to 18 as a tribute to our dear colleague +dnl +max_cname_chain=18 +AC_ARG_WITH([max_cname_chain], + AS_HELP_STRING([--with-max-cname-chain=number],[Limit the maximum number of CNAMEs to follow]), + [max_cname_chain=$withval]) +AC_DEFINE_UNQUOTED([MAX_CNAME_CHAIN], $max_cname_chain, [Define to the default maximum number of CNAMEs to follow.]) + +dnl dnl Features dnl AC_ARG_ENABLE(root-server, AS_HELP_STRING([--enable-root-server],[Configure NSD as a root server (obsolete)])) @@ -994,6 +1010,15 @@ case "$enable_ipv6" in ;; esac +AC_ARG_ENABLE(multiple-catalog-zones, AS_HELP_STRING([--enable-multiple-catzones],[Enable experimental support for more than one catalog consumer zone])) +case "$enable_multiple_catalog_zones" in + yes) + AC_DEFINE_UNQUOTED([MULTIPLE_CATALOG_CONSUMER_ZONES], [], [Define this to enable experimental support for more than one catalog consumer zone.]) + ;; + no|*) + ;; +esac + AC_ARG_ENABLE(bind8-stats, AS_HELP_STRING([--disable-bind8-stats],[Disable BIND8 like NSTATS & XSTATS and statistics in nsd-control])) case "$enable_bind8_stats" in @@ -1505,4 +1530,4 @@ if test -n "$dnstap_config"; then fi fi m4_unquote( - _m4_defn([_m4_wrap_text])_m4_popdef([_m4_wrap_text])) \ No newline at end of file + _m4_defn([_m4_wrap_text])_m4_popdef([_m4_wrap_text])) Index: usr.sbin/nsd/dbaccess.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/dbaccess.c,v diff -u -p -r1.13 dbaccess.c --- usr.sbin/nsd/dbaccess.c 3 Sep 2025 18:46:48 -0000 1.13 +++ usr.sbin/nsd/dbaccess.c 21 Sep 2026 16:29:07 -0000 @@ -135,12 +135,22 @@ namedb_zone_delete(namedb_type* db, zone } } - /* soa_rrset is freed when the SOA was deleted */ + /* soa_nx_rrset is kept around, between zone delete and new zone + * contents. When the SOA was deleted, the usage for domain references + * was lowered. */ if(zone->soa_nx_rrset) { + region_recycle(db->region, zone->soa_nx_rrset->rrs[0], + sizeof(rr_type)+zone->soa_nx_rrset->rrs[0]->rdlength); +#ifndef PACKED_STRUCTS region_recycle(db->region, zone->soa_nx_rrset->rrs, - sizeof(rr_type)); + sizeof(rr_type*)); +#endif region_recycle(db->region, zone->soa_nx_rrset, - sizeof(rrset_type)); + sizeof(rrset_type) +#ifdef PACKED_STRUCTS + + sizeof(rr_type*) +#endif + ); } #ifdef NSEC3 hash_tree_delete(db->region, zone->nsec3tree); Index: usr.sbin/nsd/dbcreate.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/dbcreate.c,v diff -u -p -r1.9 dbcreate.c --- usr.sbin/nsd/dbcreate.c 3 Sep 2025 18:46:48 -0000 1.9 +++ usr.sbin/nsd/dbcreate.c 21 Sep 2026 16:29:07 -0000 @@ -26,44 +26,6 @@ /* pathname directory separator character */ #define PATHSEP '/' -/** add an rdata (uncompressed) to the destination */ -static size_t -add_rdata(rr_type* rr, unsigned i, uint8_t* buf, size_t buflen) -{ - switch(rdata_atom_wireformat_type(rr->type, i)) { - case RDATA_WF_COMPRESSED_DNAME: - case RDATA_WF_UNCOMPRESSED_DNAME: - { - const dname_type* dname = domain_dname( - rdata_atom_domain(rr->rdatas[i])); - if(dname->name_size > buflen) - return 0; - memmove(buf, dname_name(dname), dname->name_size); - return dname->name_size; - } - default: - break; - } - if(rdata_atom_size(rr->rdatas[i]) > buflen) - return 0; - memmove(buf, rdata_atom_data(rr->rdatas[i]), - rdata_atom_size(rr->rdatas[i])); - return rdata_atom_size(rr->rdatas[i]); -} - -/* marshal rdata into buffer, must be MAX_RDLENGTH in size */ -size_t -rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz) -{ - size_t len = 0; - unsigned i; - assert(rr); - for(i=0; irdata_count; i++) { - len += add_rdata(rr, i, rdata+len, sz-len); - } - return len; -} - int print_rrs(FILE* out, struct zone* zone) { @@ -77,7 +39,7 @@ print_rrs(FILE* out, struct zone* zone) if(zone->soa_rrset) { size_t i; for(i=0; i < zone->soa_rrset->rr_count; i++) { - if(!print_rr(out, state, &zone->soa_rrset->rrs[i], + if(!print_rr(out, state, zone->soa_rrset->rrs[i], rr_region, rr_buffer)){ log_msg(LOG_ERR, "There was an error " "printing SOARR to zone %s", @@ -97,7 +59,7 @@ print_rrs(FILE* out, struct zone* zone) if(rrset->zone != zone || rrset == zone->soa_rrset) continue; for(i=0; i < rrset->rr_count; i++) { - if(!print_rr(out, state, &rrset->rrs[i], + if(!print_rr(out, state, rrset->rrs[i], rr_region, rr_buffer)){ log_msg(LOG_ERR, "There was an error " "printing RR to zone %s", Index: usr.sbin/nsd/difffile.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/difffile.c,v diff -u -p -r1.24 difffile.c --- usr.sbin/nsd/difffile.c 6 Sep 2025 17:41:37 -0000 1.24 +++ usr.sbin/nsd/difffile.c 21 Sep 2026 16:29:07 -0000 @@ -235,19 +235,10 @@ diff_read_str(FILE* in, char* buf, size_ } static void -add_rdata_to_recyclebin(namedb_type* db, rr_type* rr) +add_rr_to_recyclebin(namedb_type* db, rr_type* rr) { - /* add rdatas to recycle bin. */ - size_t i; - for(i=0; irdata_count; i++) - { - if(!rdata_atom_is_domain(rr->type, i)) - region_recycle(db->region, rr->rdatas[i].data, - rdata_atom_size(rr->rdatas[i]) - + sizeof(uint16_t)); - } - region_recycle(db->region, rr->rdatas, - sizeof(rdata_atom_type)*rr->rdata_count); + /* add rr and rdata to recycle bin. */ + region_recycle(db->region, rr, sizeof(rr_type) + rr->rdlength); } /* this routine determines if below a domain there exist names with @@ -324,7 +315,7 @@ rrset_delete(namedb_type* db, domain_typ } if(domain == rrset->zone->apex && rrset_rrtype(rrset) == TYPE_RRSIG) { for (i = 0; i < rrset->rr_count; ++i) { - if(rr_rrsig_type_covered(&rrset->rrs[i])==TYPE_DNSKEY) { + if(rr_rrsig_type_covered(rrset->rrs[i])==TYPE_DNSKEY) { rrset->zone->is_secure = 0; break; } @@ -332,104 +323,164 @@ rrset_delete(namedb_type* db, domain_typ } /* recycle the memory space of the rrset */ for (i = 0; i < rrset->rr_count; ++i) - add_rdata_to_recyclebin(db, &rrset->rrs[i]); - region_recycle(db->region, rrset->rrs, - sizeof(rr_type) * rrset->rr_count); + add_rr_to_recyclebin(db, rrset->rrs[i]); +#ifndef PACKED_STRUCTS + region_recycle(db->region, rrset->rrs, sizeof(rr_type*) * rrset->rr_count); rrset->rr_count = 0; - region_recycle(db->region, rrset, sizeof(rrset_type)); +#endif + region_recycle(db->region, rrset, sizeof(rrset_type) +#ifdef PACKED_STRUCTS + + rrset->rr_count*sizeof(rr_type*) +#endif + ); } static int -rdatas_equal(rdata_atom_type *a, rdata_atom_type *b, int num, uint16_t type, +rdatas_equal(const rr_type *rr1, const rr_type *rr2, uint16_t type, int* rdnum, char** reason) { - int k, start, end; - start = 0; - end = num; + size_t offset = 0; + const nsd_type_descriptor_type *descriptor; + + if(rr1->rdlength != rr2->rdlength) { + *rdnum = 0; + *reason = "rr length different"; + return 0; + } + assert(rr1->rdlength == rr2->rdlength); + /** * SOA RDATA comparisons in XFR are more lenient, * only serial rdata is checked. **/ if (type == TYPE_SOA) { - start = 2; - end = 3; + offset = 2 * sizeof(void*); + if(rr1->rdlength != offset + 20) { + *rdnum = 0; + *reason = "invalid SOA length"; + return 0; + } + assert(rr1->rdlength == offset + 20); + if (memcmp(rr1->rdata + offset, rr2->rdata + offset, 4) == 0) + return 1; + *rdnum = 2; + *reason = "rdata data"; + return 0; } - for(k = start; k < end; k++) - { - if(rdata_atom_is_domain(type, k)) { - if(dname_compare(domain_dname(a[k].domain), - domain_dname(b[k].domain))!=0) { - *rdnum = k; + + if (memcmp(rr1->rdata, rr2->rdata, rr1->rdlength) == 0) + return 1; + + descriptor = nsd_type_descriptor(type); + for (size_t i=0; i < descriptor->rdata.length && offset < rr1->rdlength; i++) { + uint16_t field_len1, field_len2; + struct domain *domain1, *domain2; + if(offset == rr1->rdlength && + descriptor->rdata.fields[i].is_optional) + break; + if(!lookup_rdata_field_entry(descriptor, i, rr1, offset, + &field_len1, &domain1)) { + *rdnum = i; + *reason = "malformed field rr1"; + return 0; + } + if(!lookup_rdata_field_entry(descriptor, i, rr2, offset, + &field_len2, &domain2)) { + *rdnum = i; + *reason = "malformed field rr2"; + return 0; + } + if(domain1 && domain2) { + const struct dname *dname1, *dname2; + dname1 = domain_dname(domain1); + dname2 = domain_dname(domain2); + if (dname_compare(dname1, dname2) != 0) { + *rdnum = i; *reason = "dname data"; return 0; } - } else if(rdata_atom_is_literal_domain(type, k)) { - /* literal dname, but compare case insensitive */ - if(a[k].data[0] != b[k].data[0]) { - *rdnum = k; - *reason = "literal dname len"; - return 0; /* uncompressed len must be equal*/ - } - if(!dname_equal_nocase((uint8_t*)(a[k].data+1), - (uint8_t*)(b[k].data+1), a[k].data[0])) { - *rdnum = k; - *reason = "literal dname data"; + offset += field_len1; + continue; + } else if(descriptor->rdata.fields[i].length == + RDATA_LITERAL_DNAME) { + uint8_t length1, length2; + const uint8_t *name1 = rr1->rdata + offset; + const uint8_t *name2 = rr2->rdata + offset; + if(field_len1 != field_len2) { + *rdnum = i; + *reason = "literal dname field len"; return 0; } - } else { - /* check length */ - if(a[k].data[0] != b[k].data[0]) { - *rdnum = k; - *reason = "rdata len"; + length1 = buf_dname_length(name1, rr1->rdlength - offset); + length2 = buf_dname_length(name2, rr2->rdlength - offset); + + if (!length1 || !length2 || length1 != length2) { + *rdnum = i; + *reason = "literal dname len"; return 0; } - /* check data */ - if(memcmp(a[k].data+1, b[k].data+1, a[k].data[0])!=0) { - *rdnum = k; - *reason = "rdata data"; + if (!dname_equal_nocase((uint8_t*)name1, (uint8_t*)name2, length1)) { + *rdnum = i; + *reason = "literal dname data"; return 0; } + offset += field_len1; + continue; + } + if(field_len1 != field_len2) { + *rdnum = i; + *reason = "rdata len"; + return 0; + } + if(memcmp(rr1->rdata+offset, rr2->rdata+offset, field_len1) != 0) { + *rdnum = i; + *reason = "rdata data"; + return 0; } + offset += field_len1; } + return 1; } static void debug_find_rr_num(rrset_type* rrset, uint16_t type, uint16_t klass, - rdata_atom_type *rdatas, ssize_t rdata_num) + rr_type *rr) { int i, rd; char* reason = ""; for(i=0; i < rrset->rr_count; ++i) { - if (rrset->rrs[i].type != type) { + if (rrset->rrs[i]->type != type) { log_msg(LOG_WARNING, "diff: RR <%s, %s> does not match " "RR num %d type %s", - dname_to_string(domain_dname(rrset->rrs[i].owner),0), + dname_to_string(domain_dname(rrset->rrs[i]->owner),0), rrtype_to_string(type), i, - rrtype_to_string(rrset->rrs[i].type)); + rrtype_to_string(rrset->rrs[i]->type)); + continue; /* rdata equality needs same type. */ } - if (rrset->rrs[i].klass != klass) { + if (rrset->rrs[i]->klass != klass) { log_msg(LOG_WARNING, "diff: RR <%s, %s> class %d " "does not match RR num %d class %d", - dname_to_string(domain_dname(rrset->rrs[i].owner),0), + dname_to_string(domain_dname(rrset->rrs[i]->owner),0), rrtype_to_string(type), klass, i, - rrset->rrs[i].klass); + rrset->rrs[i]->klass); + continue; } - if (rrset->rrs[i].rdata_count != rdata_num) { + if (rrset->rrs[i]->rdlength != rr->rdlength) { log_msg(LOG_WARNING, "diff: RR <%s, %s> rdlen %u " "does not match RR num %d rdlen %d", - dname_to_string(domain_dname(rrset->rrs[i].owner),0), + dname_to_string(domain_dname(rrset->rrs[i]->owner),0), rrtype_to_string(type), - (unsigned) rdata_num, i, - (unsigned) rrset->rrs[i].rdata_count); + (unsigned) rr->rdlength, i, + (unsigned) rrset->rrs[i]->rdlength); } - if (!rdatas_equal(rdatas, rrset->rrs[i].rdatas, rdata_num, type, + if (!rdatas_equal(rr, rrset->rrs[i], type, &rd, &reason)) { log_msg(LOG_WARNING, "diff: RR <%s, %s> rdata element " "%d differs from RR num %d rdata (%s)", - dname_to_string(domain_dname(rrset->rrs[i].owner),0), + dname_to_string(domain_dname(rrset->rrs[i]->owner),0), rrtype_to_string(type), rd, i, reason); } @@ -438,24 +489,24 @@ debug_find_rr_num(rrset_type* rrset, uin static int find_rr_num(rrset_type* rrset, uint16_t type, uint16_t klass, - rdata_atom_type *rdatas, ssize_t rdata_num, int add) + rr_type *rr, int add) { int i, rd; char* reason; for(i=0; i < rrset->rr_count; ++i) { - if(rrset->rrs[i].type == type && - rrset->rrs[i].klass == klass && - rrset->rrs[i].rdata_count == rdata_num && - rdatas_equal(rdatas, rrset->rrs[i].rdatas, rdata_num, type, + if(rrset->rrs[i]->type == type && + rrset->rrs[i]->klass == klass && + rrset->rrs[i]->rdlength == rr->rdlength && + rdatas_equal(rrset->rrs[i], rr, type, &rd, &reason)) { return i; } } /* this is odd. Log why rr cannot be found. */ - if (!add) { - debug_find_rr_num(rrset, type, klass, rdatas, rdata_num); + if (!add && verbosity >= 3) { + debug_find_rr_num(rrset, type, klass, rr); } return -1; } @@ -471,6 +522,7 @@ nsec3_delete_rr_trigger(namedb_type* db, /* see if the domain was an NSEC3-domain in the chain, but no longer */ if(rr->type == TYPE_NSEC3 && rr->owner->nsec3 && rr->owner->nsec3->nsec3_node.key && + nsec3_has_owner_for_zone(rr->owner, zone) && nsec3_rr_uses_params(rr, zone) && nsec3_in_chain_count(rr->owner, zone) <= 1) { domain_type* prev = nsec3_chain_find_prev(zone, rr->owner); @@ -589,6 +641,7 @@ nsec3_add_rr_trigger(namedb_type* db, rr * in the udb has been adjusted) */ if(zone->nsec3_param && rr->type == TYPE_NSEC3 && (!rr->owner->nsec3 || !rr->owner->nsec3->nsec3_node.key) + && nsec3_has_owner_for_zone(rr->owner, zone) && nsec3_rr_uses_params(rr, zone)) { if(!zone->nsec3_last) { /* all nsec3s have previously been deleted, but @@ -640,38 +693,36 @@ nsec3_add_rrset_trigger(namedb_type* db, } #endif /* NSEC3 */ -/* fixup usage lower for domain names in the rdata */ -static void -rr_lower_usage(namedb_type* db, rr_type* rr) -{ - unsigned i; - for(i=0; irdata_count; i++) { - if(rdata_atom_is_domain(rr->type, i)) { - assert(rdata_atom_domain(rr->rdatas[i])->usage > 0); - rdata_atom_domain(rr->rdatas[i])->usage --; - if(rdata_atom_domain(rr->rdatas[i])->usage == 0) - domain_table_deldomain(db, - rdata_atom_domain(rr->rdatas[i])); - } - } -} - static void rrset_lower_usage(namedb_type* db, rrset_type* rrset) { unsigned i; for(i=0; irr_count; i++) - rr_lower_usage(db, &rrset->rrs[i]); + rr_lower_usage(db, rrset->rrs[i]); } int delete_RR(namedb_type* db, const dname_type* dname, - uint16_t type, uint16_t klass, + uint16_t type, uint16_t klass, uint32_t ttl, buffer_type* packet, size_t rdatalen, zone_type *zone, - region_type* temp_region, int* softfail) + region_type* temp_region, int* softfail, struct ixfr_store* ixfr_store) { domain_type *domain; rrset_type *rrset; +#ifdef PACKED_STRUCTS + rrset_type* rrset_prev; +#endif + const nsd_type_descriptor_type *descriptor = nsd_type_descriptor(type); + if (!dname_is_subdomain(dname, domain_dname(zone->apex))) { + char zname[MAXDOMAINLEN * 5]; + domain_to_string_buf(zone->apex, zname); + VERBOSITY(2, (LOG_WARNING, + "out-of-zone record deletion in transfer for zone %s: " + "owner %s is outside zone; skipping", + zname, dname_to_string(dname, NULL))); + buffer_skip(packet, rdatalen); + return 1; + } domain = domain_table_find(db->domains, dname); if(!domain) { log_msg(LOG_WARNING, "diff: domain %s does not exist", @@ -680,47 +731,71 @@ delete_RR(namedb_type* db, const dname_t *softfail = 1; return 1; /* not fatal error */ } +#ifndef PACKED_STRUCTS rrset = domain_find_rrset(domain, zone, type); +#else + rrset = domain_find_rrset_and_prev(domain, zone, type, &rrset_prev); +#endif if(!rrset) { - log_msg(LOG_WARNING, "diff: rrset %s does not exist", - dname_to_string(dname,0)); + VERBOSITY(2, (LOG_WARNING, + "diff: RRset to delete from <%s, %s> does not exist", + dname_to_string(dname,0), rrtype_to_string(type))); buffer_skip(packet, rdatalen); *softfail = 1; return 1; /* not fatal error */ } else { /* find the RR in the rrset */ domain_table_type *temptable; - rdata_atom_type *rdatas; - ssize_t rdata_num; - int rrnum; + int32_t rrnum, code; + struct rr *rr; + int was_rrsig_dnskey = 0; temptable = domain_table_create(temp_region); /* This will ensure that the dnames in rdata are * normalized, conform RFC 4035, section 6.2 */ - rdata_num = rdata_wireformat_to_rdata_atoms( - temp_region, temptable, type, rdatalen, packet, &rdatas); - if(rdata_num == -1) { - log_msg(LOG_ERR, "diff: bad rdata for %s", - dname_to_string(dname,0)); + code = descriptor->read_rdata(temptable, rdatalen, packet, &rr); + if(code < 0) { + log_msg(LOG_ERR, "diff: could not read rdata for " + "%s %s %s", dname_to_string(dname,0), + rrtype_to_string(type), + read_rdata_fail_str(code)); return 0; } - rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num, 0); + rr->owner = domain; + rr->type = type; + rr->klass = klass; + rr->ttl = ttl; + + /* Now that the RR has been read with its RRtype specific read + * routine, store the read data and rdata, for an ixfr store. + * Store the delete RR information, for the softfail cases, the + * IXFR is stopped and an AXFR is attempted, so it would not + * need to be stored as the transfer is rejected. */ + if(ixfr_store) + ixfr_store_delrr(ixfr_store, rr); + + rrnum = find_rr_num(rrset, type, klass, rr, 0); if(rrnum == -1 && type == TYPE_SOA && domain == zone->apex && rrset->rr_count != 0) rrnum = 0; /* replace existing SOA if no match */ if(rrnum == -1) { - log_msg(LOG_WARNING, "diff: RR <%s, %s> does not exist", - dname_to_string(dname,0), rrtype_to_string(type)); + VERBOSITY(2, (LOG_WARNING, + "diff: RR to delete from RRset <%s, %s> does not exist", + dname_to_string(dname,0), rrtype_to_string(type))); *softfail = 1; return 1; /* not fatal error */ } #ifdef NSEC3 /* process triggers for RR deletions */ - nsec3_delete_rr_trigger(db, &rrset->rrs[rrnum], zone); + nsec3_delete_rr_trigger(db, rrset->rrs[rrnum], zone); #endif + if(domain == zone->apex && type == TYPE_RRSIG && + rr_rrsig_type_covered(rrset->rrs[rrnum])==TYPE_DNSKEY) + was_rrsig_dnskey = 1; + /* lower usage (possibly deleting other domains, and thus * invalidating the current RR's domain pointers) */ - rr_lower_usage(db, &rrset->rrs[rrnum]); + rr_lower_usage(db, rrset->rrs[rrnum]); if(rrset->rr_count == 1) { /* delete entire rrset */ rrset_delete(db, domain, rrset); @@ -734,38 +809,65 @@ delete_RR(namedb_type* db, const dname_t domain_table_deldomain(db, domain); } else { /* swap out the bad RR and decrease the count */ - rr_type* rrs_orig = rrset->rrs; - add_rdata_to_recyclebin(db, &rrset->rrs[rrnum]); +#ifndef PACKED_STRUCTS + rr_type** rrs_orig = rrset->rrs; +#else + rrset_type* rrset_orig = rrset; +#endif + add_rr_to_recyclebin(db, rrset->rrs[rrnum]); if(rrnum < rrset->rr_count-1) rrset->rrs[rrnum] = rrset->rrs[rrset->rr_count-1]; - memset(&rrset->rrs[rrset->rr_count-1], 0, sizeof(rr_type)); + rrset->rrs[rrset->rr_count-1] = NULL; /* realloc the rrs array one smaller */ +#ifndef PACKED_STRUCTS rrset->rrs = region_alloc_array_init(db->region, rrs_orig, - (rrset->rr_count-1), sizeof(rr_type)); + (rrset->rr_count-1), sizeof(rr_type*)); if(!rrset->rrs) { log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); exit(1); } region_recycle(db->region, rrs_orig, - sizeof(rr_type) * rrset->rr_count); -#ifdef NSEC3 - if(type == TYPE_NSEC3PARAM && zone->nsec3_param) { - /* fixup nsec3_param pointer to same RR */ - assert(zone->nsec3_param >= rrs_orig && - zone->nsec3_param <= - rrs_orig+rrset->rr_count); - /* last moved to rrnum, others at same index*/ - if(zone->nsec3_param == &rrs_orig[ - rrset->rr_count-1]) - zone->nsec3_param = &rrset->rrs[rrnum]; - else - zone->nsec3_param = (void*) - ((char*)zone->nsec3_param - -(char*)rrs_orig + - (char*)rrset->rrs); + sizeof(rr_type*) * rrset->rr_count); +#else + rrset = region_alloc_init(db->region, rrset_orig, + sizeof(rrset_type) + + (rrset_orig->rr_count-1)*sizeof(rr_type*)); + if(!rrset) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); } -#endif /* NSEC3 */ + if(rrset_prev) + rrset_prev->next = rrset; + else domain->rrsets = rrset; + region_recycle(db->region, rrset_orig, + sizeof(rrset_type) + + rrset_orig->rr_count*sizeof(rr_type*)); +#endif /* PACKED_STRUCTS */ rrset->rr_count --; + if(domain == zone->apex) { + /* Because the rrset struct is reallocated, + * a pointer to it may need to be set again. */ + if(type == TYPE_SOA) { + zone->soa_rrset = rrset; + } else if(type == TYPE_NS) { + zone->ns_rrset = rrset; + } else if(type == TYPE_RRSIG + && was_rrsig_dnskey) { + /* See if the zone is still signed */ + unsigned i; + int has_dnskey_rrsig = 0; + for (i = 0; i < rrset->rr_count; i++) { + if(rr_rrsig_type_covered( + rrset->rrs[i])== + TYPE_DNSKEY){ + has_dnskey_rrsig = 1; + break; + } + } + if(!has_dnskey_rrsig) + zone->is_secure = 0; + } + } #ifdef NSEC3 /* for type nsec3, the domain may have become a * 'normal' domain with its remaining data now */ @@ -778,116 +880,224 @@ delete_RR(namedb_type* db, const dname_t return 1; } -int -add_RR(namedb_type* db, const dname_type* dname, - uint16_t type, uint16_t klass, uint32_t ttl, - buffer_type* packet, size_t rdatalen, zone_type *zone, - int* softfail) +static void +commit_RRset(namedb_type* db, zone_type* zone, struct collect_rrs* collect_rrs) { - domain_type* domain; - rrset_type* rrset; - rdata_atom_type *rdatas; - rr_type *rrs_old; - ssize_t rdata_num; - int rrnum; + struct rrset *rrset; + + if(!collect_rrs->domain || collect_rrs->rr_count == 0) + return; + if (!collect_rrs->rrset) { #ifdef NSEC3 - int rrset_added = 0; + domain_type* p; #endif - domain = domain_table_find(db->domains, dname); - if(!domain) { - /* create the domain */ - domain = domain_table_insert(db->domains, dname); - } - rrset = domain_find_rrset(domain, zone, type); - if(!rrset) { - /* create the rrset */ - rrset = region_alloc(db->region, sizeof(rrset_type)); + rrset = region_alloc(db->region, sizeof(*rrset) +#ifdef PACKED_STRUCTS + + sizeof(rr_type*) * collect_rrs->rr_count /* Add space for RRs. */ +#endif + ); if(!rrset) { log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); exit(1); } rrset->zone = zone; - rrset->rrs = 0; - rrset->rr_count = 0; - domain_add_rrset(domain, rrset); + rrset->rr_count = collect_rrs->rr_count; +#ifndef PACKED_STRUCTS + rrset->rrs = region_alloc(db->region, + sizeof(rr_type*) * collect_rrs->rr_count); +#endif + memcpy(rrset->rrs, collect_rrs->rrs, collect_rrs->rr_count * sizeof(rr_type*)); + /* Add it */ + domain_add_rrset(collect_rrs->domain, rrset); #ifdef NSEC3 - rrset_added = 1; + p = collect_rrs->domain->parent; + nsec3_add_rrset_trigger(db, collect_rrs->domain, zone, collect_rrs->type); + /* go up and process (possibly created) empty nonterminals, + * until we hit the apex or root */ + while(p && p->rrsets == NULL && !p->is_apex) { + nsec3_rrsets_changed_add_prehash(db, p, zone); + p = p->parent; + } +#endif + } else { +#ifndef PACKED_STRUCTS + struct rr **rrs; +#else + struct rrset *rrset_orig; +#endif + /* Add it... */ + rrset = collect_rrs->rrset; +#ifndef PACKED_STRUCTS + rrs = rrset->rrs; + rrset->rrs = region_alloc_array( + db->region, rrset->rr_count + collect_rrs->rr_count, sizeof(*rrs)); + if(!rrset->rrs) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + if(rrs) + memcpy(rrset->rrs, rrs, rrset->rr_count * sizeof(*rrs)); + region_recycle(db->region, rrs, rrset->rr_count * sizeof(*rrs)); +#else + rrset_orig = rrset; + rrset = region_alloc(db->region, + sizeof(rrset_type) + + (rrset_orig->rr_count+collect_rrs->rr_count)*sizeof(rr_type*)); + if(!rrset) { + log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); + exit(1); + } + memcpy(rrset, rrset_orig, + sizeof(rrset_type) + + rrset_orig->rr_count*sizeof(rr_type*)); + if(collect_rrs->rrset_prev) + collect_rrs->rrset_prev->next = rrset; + else collect_rrs->domain->rrsets = rrset; + region_recycle(db->region, rrset_orig, + sizeof(rrset_type) + + rrset_orig->rr_count*sizeof(rr_type*)); +#endif /* PACKED_STRUCTS */ + memcpy(rrset->rrs + rrset->rr_count, collect_rrs->rrs, collect_rrs->rr_count * sizeof(rr_type*)); + rrset->rr_count += collect_rrs->rr_count; + } + /* Check we have SOA */ + if (collect_rrs->domain == zone->apex) + apex_rrset_checks(db, rrset, collect_rrs->domain); + + collect_rrs->domain = NULL; + collect_rrs->type = -1; + collect_rrs->rrset = NULL; +#ifdef PACKED_STRUCTS + collect_rrs->rrset_prev = NULL; +#endif +#ifdef NSEC3 + for (int i = 0; i < collect_rrs->rr_count; i++) { + nsec3_add_rr_trigger(db, collect_rrs->rrs[i], zone); + } +#endif /* NSEC3 */ + collect_rrs->rr_count = 0; +} + + +int +add_RR(namedb_type* db, const dname_type* dname, + uint16_t type, uint16_t klass, uint32_t ttl, + buffer_type* packet, size_t rdatalen, zone_type *zone, + int* softfail, struct ixfr_store* ixfr_store, + struct collect_rrs* collect_rrs) +{ + domain_type* domain; + rr_type *rr; + int32_t code; + const nsd_type_descriptor_type *descriptor; + + if (collect_rrs == NULL) { + struct collect_rrs collect_rrs2; + + collect_rrs2.domain = NULL; + collect_rrs2.type = -1; + collect_rrs2.rrset = NULL; +#ifdef PACKED_STRUCTS + collect_rrs2.rrset_prev = NULL; +#endif + collect_rrs2.rr_count = 0; + if (!add_RR(db, dname, type, klass, ttl, packet, rdatalen, + zone, softfail, ixfr_store, &collect_rrs2)) { + return 0; + } + commit_RRset(db, zone, &collect_rrs2); + return 1; + } + if (!dname_is_subdomain(dname, domain_dname(zone->apex))) { + char zname[MAXDOMAINLEN * 5]; + domain_to_string_buf(zone->apex, zname); + VERBOSITY(2, (LOG_WARNING, + "out-of-zone record in transfer for zone %s: " + "owner %s is outside zone; skipping", + zname, dname_to_string(dname, NULL))); + buffer_skip(packet, rdatalen); + return 1; + } + domain = domain_table_insert(db->domains, dname); + assert(domain); + if (domain != collect_rrs->domain || type != collect_rrs->type + || collect_rrs->rr_count >= (int)(sizeof(collect_rrs->rrs) / sizeof(*collect_rrs->rrs))){ + commit_RRset(db, zone, collect_rrs); + collect_rrs->domain = domain; + collect_rrs->type = type; + collect_rrs->rrset = NULL; +#ifdef PACKED_STRUCTS + collect_rrs->rrset_prev = NULL; #endif } /* dnames in rdata are normalized, conform RFC 4035, * Section 6.2 */ - rdata_num = rdata_wireformat_to_rdata_atoms( - db->region, db->domains, type, rdatalen, packet, &rdatas); - if(rdata_num == -1) { - log_msg(LOG_ERR, "diff: bad rdata for %s", - dname_to_string(dname,0)); + descriptor = nsd_type_descriptor(type); + code = descriptor->read_rdata(db->domains, rdatalen, packet, &rr); + if(code < 0) { + log_msg(LOG_ERR, "diff: could not read rdata for %s %s %s", + dname_to_string(dname,0), rrtype_to_string(type), + read_rdata_fail_str(code)); return 0; } - rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num, 1); - if(rrnum != -1) { + rr->owner = domain; + rr->type = type; + rr->klass = klass; + rr->ttl = ttl; + + /* Now that the RR has been read with its RRtype specific read + * routine, store the read data and rdata, for an ixfr store. */ + if (ixfr_store) { + ixfr_store_addrr(ixfr_store, rr); + } + /* With the first RR for a RRset in this position in the zone transfer, + * find the RRset */ + if (collect_rrs->rr_count == 0) { +#ifndef PACKED_STRUCTS + collect_rrs->rrset = domain_find_rrset(collect_rrs->domain, zone, collect_rrs->type); +#else + collect_rrs->rrset = domain_find_rrset_and_prev(collect_rrs->domain, zone, collect_rrs->type, &collect_rrs->rrset_prev); +#endif + } + if (collect_rrs->rrset) { + /* Search for possible duplicates in existing RRset */ + for (int i = 0; i < collect_rrs->rrset->rr_count; i++) { + if (!equal_rr_rdata(descriptor, rr, collect_rrs->rrset->rrs[i])) + continue; + /* Discard the duplicates... */ + /* Lower the usage counter for domains in the rdata. */ + rr_lower_usage(db, rr); + region_recycle(db->region, rr, sizeof(*rr) + rr->rdlength); + DEBUG(DEBUG_XFRD, 2, (LOG_ERR, "diff: RR <%s, %s> already exists", + dname_to_string(dname,0), rrtype_to_string(type))); + /* ignore already existing RR: lenient accepting of messages */ + *softfail = 1; + return 1; + } + } + /* Search for possible duplicates in already batched RRs */ + for (int i = 0; i < collect_rrs->rr_count; i++) { + if (!equal_rr_rdata(descriptor, rr, collect_rrs->rrs[i])) + continue; + /* Discard the duplicates... */ + /* Lower the usage counter for domains in the rdata. */ + rr_lower_usage(db, rr); + region_recycle(db->region, rr, sizeof(*rr) + rr->rdlength); DEBUG(DEBUG_XFRD, 2, (LOG_ERR, "diff: RR <%s, %s> already exists", dname_to_string(dname,0), rrtype_to_string(type))); /* ignore already existing RR: lenient accepting of messages */ *softfail = 1; return 1; } - if(rrset->rr_count == 65535) { + if(collect_rrs->rr_count + ( collect_rrs->rrset + ? collect_rrs->rrset->rr_count : 0) >= 65535) { log_msg(LOG_ERR, "diff: too many RRs at %s", dname_to_string(dname,0)); return 0; } - - /* re-alloc the rrs and add the new */ - rrs_old = rrset->rrs; - rrset->rrs = region_alloc_array(db->region, - (rrset->rr_count+1), sizeof(rr_type)); - if(!rrset->rrs) { - log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); - exit(1); - } - if(rrs_old) - memcpy(rrset->rrs, rrs_old, rrset->rr_count * sizeof(rr_type)); - region_recycle(db->region, rrs_old, sizeof(rr_type) * rrset->rr_count); - rrset->rr_count ++; - - rrset->rrs[rrset->rr_count - 1].owner = domain; - rrset->rrs[rrset->rr_count - 1].rdatas = rdatas; - rrset->rrs[rrset->rr_count - 1].ttl = ttl; - rrset->rrs[rrset->rr_count - 1].type = type; - rrset->rrs[rrset->rr_count - 1].klass = klass; - rrset->rrs[rrset->rr_count - 1].rdata_count = rdata_num; - - /* see if it is a SOA */ - if(domain == zone->apex) { - apex_rrset_checks(db, rrset, domain); -#ifdef NSEC3 - if(type == TYPE_NSEC3PARAM && zone->nsec3_param) { - /* the pointer just changed, fix it up to point - * to the same record */ - assert(zone->nsec3_param >= rrs_old && - zone->nsec3_param < rrs_old+rrset->rr_count); - /* in this order to make sure no overflow/underflow*/ - zone->nsec3_param = (void*)((char*)zone->nsec3_param - - (char*)rrs_old + (char*)rrset->rrs); - } -#endif /* NSEC3 */ - } - -#ifdef NSEC3 - if(rrset_added) { - domain_type* p = domain->parent; - nsec3_add_rrset_trigger(db, domain, zone, type); - /* go up and process (possibly created) empty nonterminals, - * until we hit the apex or root */ - while(p && p->rrsets == NULL && !p->is_apex) { - nsec3_rrsets_changed_add_prehash(db, p, zone); - p = p->parent; - } - } - nsec3_add_rr_trigger(db, &rrset->rrs[rrset->rr_count - 1], zone); -#endif /* NSEC3 */ + collect_rrs->rrs[collect_rrs->rr_count++] = rr; return 1; } @@ -1002,6 +1212,7 @@ apply_ixfr(nsd_type* nsd, FILE *in, uint int qcount, ancount; buffer_type* packet; region_type* region; + struct collect_rrs collect_rrs; /* note that errors could not really happen due to format of the * packet since xfrd has checked all dnames and RRs before commit, @@ -1072,8 +1283,16 @@ apply_ixfr(nsd_type* nsd, FILE *in, uint } } - DEBUG(DEBUG_XFRD, 2, (LOG_INFO, "diff: started packet for zone %s", - domain_to_string(zone->apex))); + collect_rrs.domain = NULL; + collect_rrs.type = -1; + collect_rrs.rrset = NULL; +#ifdef PACKED_STRUCTS + collect_rrs.rrset_prev = NULL; +#endif + collect_rrs.rr_count = 0; + + DEBUG(DEBUG_XFRD, 2, (LOG_INFO, "diff: started packet %d/%d for zone %s", + (int)seq_nr, (int)seq_total, domain_to_string(zone->apex))); for(int i=0; i < ancount; ++i, ++(*rr_count)) { const dname_type *owner; @@ -1101,6 +1320,14 @@ apply_ixfr(nsd_type* nsd, FILE *in, uint region_destroy(region); return 0; } + if (klass != CLASS_IN && type != TYPE_OPT) { + log_msg(LOG_ERR, "bad xfr non-IN-class RR %s %s %s", + dname_to_string(owner,0), + rrclass_to_string(klass), + rrtype_to_string(type)); + region_destroy(region); + return 0; + } DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parsed count %d, ax %d, delmode %d", domain_to_string(zone->apex), *rr_count, *is_axfr, *delete_mode)); @@ -1125,11 +1352,6 @@ apply_ixfr(nsd_type* nsd, FILE *in, uint if (*rr_count == 0) { assert(!*is_axfr); assert(!*delete_mode); - if (klass != CLASS_IN) { - log_msg(LOG_ERR, "first RR not SOA IN"); - region_destroy(region); - return 0; - } if(dname_compare(domain_dname(zone->apex), owner) != 0) { log_msg(LOG_ERR, "SOA dname not equal to zone %s", domain_to_string(zone->apex)); @@ -1142,10 +1364,9 @@ apply_ixfr(nsd_type* nsd, FILE *in, uint region_destroy(region); return 0; } - buffer_skip(packet, rrlen); - if(ixfr_store) ixfr_store_add_newsoa(ixfr_store, ttl, packet, rrlen); + buffer_skip(packet, rrlen); continue; } else if (*rr_count == 1) { @@ -1190,6 +1411,14 @@ apply_ixfr(nsd_type* nsd, FILE *in, uint just before soa - so it gets deleted and added too */ DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s IXFRswapdel count %d, ax %d, delmode %d", domain_to_string(zone->apex), *rr_count, *is_axfr, *delete_mode)); + } else if(*is_axfr) { + if(!(seq_nr == seq_total-1 && i == ancount-1)) { + /* AXFR mode: a SOA at rr_count>=2 + * should not exist. Skip it rather + * than passing it to add_RR. */ + buffer_skip(packet, rrlen); + continue; + } } } else { if (*rr_count == 0) { @@ -1225,27 +1454,41 @@ axfr: dname_to_string(owner, 0), rrtype_to_string(type))); if(*delete_mode) { assert(!*is_axfr); + /* commit previous additions, if any. */ + commit_RRset(nsd->db, zone, &collect_rrs); /* delete this rr */ - if(ixfr_store) - ixfr_store_delrr(ixfr_store, owner, type, - klass, ttl, packet, rrlen, region); - if(!delete_RR(nsd->db, owner, type, klass, packet, - rrlen, zone, region, softfail)) { + if(!delete_RR(nsd->db, owner, type, klass, ttl, packet, + rrlen, zone, region, softfail, ixfr_store)) { region_destroy(region); return 0; } } else { /* add this rr */ - if(ixfr_store) - ixfr_store_addrr(ixfr_store, owner, type, - klass, ttl, packet, rrlen, region); - if(!add_RR(nsd->db, owner, type, klass, ttl, packet, - rrlen, zone, softfail)) { + if(!(add_RR(nsd->db, owner, type, klass, ttl, packet, + rrlen, zone, softfail, ixfr_store, &collect_rrs))) { + /* The collect_rrs->rrs that have not been committed yet, need to + have rr_lower_usage(db, collect_rrs.rrs[i]); for them. This is + needed to remove references to the db domain tree. However + the failure is fatal to the process, so it is not necessary. + But we do it anyway in case with a future change this will + not be fatal to the process anymore. + */ + /* With all socked up RRs, + * lower the usage counter for domains in the rdata. + */ + for (int i = 0; i < collect_rrs.rr_count; i++) { + /* Lower the usage counter for domains in the rdata. */ + rr_lower_usage( nsd->db, collect_rrs.rrs[i]); + region_recycle( nsd->db->region, collect_rrs.rrs[i] + , sizeof(*collect_rrs.rrs[i]) + + collect_rrs.rrs[i]->rdlength); + } region_destroy(region); return 0; } } } + commit_RRset(nsd->db, zone, &collect_rrs); region_destroy(region); return 1; } @@ -1263,10 +1506,9 @@ check_for_bad_serial(namedb_type* db, co zone = domain_find_zone(db, domain); if(zone && zone->apex == domain && zone->soa_rrset && old_serial) { - uint32_t memserial; - memcpy(&memserial, rdata_atom_data(zone->soa_rrset->rrs[0].rdatas[2]), - sizeof(uint32_t)); - if(old_serial != ntohl(memserial)) { + uint32_t memserial = 0; + retrieve_soa_rdata_serial(zone->soa_rrset->rrs[0], &memserial); + if(old_serial != memserial) { region_destroy(region); return 1; } @@ -1492,15 +1734,14 @@ void task_new_soainfo(struct udb_base* u apex = domain_dname(z->apex); sz = sizeof(struct task_list_d) + dname_total_size(apex); if(z->soa_rrset && hint == soainfo_ok) { - ns = domain_dname(rdata_atom_domain( - z->soa_rrset->rrs[0].rdatas[0])); - em = domain_dname(rdata_atom_domain( - z->soa_rrset->rrs[0].rdatas[1])); + ns = domain_dname(rdata_domain_ref(z->soa_rrset->rrs[0])); + em = domain_dname(rdata_domain_ref_offset( + z->soa_rrset->rrs[0], sizeof(void*))); sz += sizeof(uint32_t)*6 + sizeof(uint8_t)*2 + ns->name_size + em->name_size; } else { - ns = 0; - em = 0; + ns = NULL; + em = NULL; } /* create new task_list item */ @@ -1512,7 +1753,7 @@ void task_new_soainfo(struct udb_base* u TASKLIST(&e)->yesno = (uint64_t)hint; if(z->soa_rrset && hint == soainfo_ok) { - uint32_t ttl = htonl(z->soa_rrset->rrs[0].ttl); + uint32_t ttl = htonl(z->soa_rrset->rrs[0]->ttl); uint8_t* p = (uint8_t*)TASKLIST(&e)->zname; p += dname_total_size(apex); memmove(p, &ttl, sizeof(uint32_t)); @@ -1525,20 +1766,8 @@ void task_new_soainfo(struct udb_base* u p += sizeof(uint8_t); memmove(p, dname_name(em), em->name_size); p += em->name_size; - memmove(p, rdata_atom_data(z->soa_rrset->rrs[0].rdatas[2]), - sizeof(uint32_t)); - p += sizeof(uint32_t); - memmove(p, rdata_atom_data(z->soa_rrset->rrs[0].rdatas[3]), - sizeof(uint32_t)); - p += sizeof(uint32_t); - memmove(p, rdata_atom_data(z->soa_rrset->rrs[0].rdatas[4]), - sizeof(uint32_t)); - p += sizeof(uint32_t); - memmove(p, rdata_atom_data(z->soa_rrset->rrs[0].rdatas[5]), - sizeof(uint32_t)); - p += sizeof(uint32_t); - memmove(p, rdata_atom_data(z->soa_rrset->rrs[0].rdatas[6]), - sizeof(uint32_t)); + memmove(p, z->soa_rrset->rrs[0]->rdata + sizeof(void*)*2, + sizeof(uint32_t)*5); } udb_ptr_unlink(&e, udb); } @@ -1905,7 +2134,15 @@ task_process_add_zone(struct nsd* nsd, u return; } /* create zone */ +#ifdef NSEC3 + nsec3_superzone_clear_for_apex(nsd->db, zdname); +#endif z = find_or_create_zone(nsd->db, zdname, nsd->options, zname, pname); +#ifdef NSEC3 + /* If no z, it reinstates the content, if z created, it creates + * the content around the new zone apex (created with zone create). */ + nsec3_superzone_precompile_for_apex(nsd->db, zdname); +#endif if(!z) { region_recycle(nsd->db->region, (void*)zdname, dname_total_size(zdname)); @@ -1934,6 +2171,7 @@ task_process_del_zone(struct nsd* nsd, s return; #ifdef NSEC3 + nsec3_superzone_clear_for_apex(nsd->db, task->zname); nsec3_clear_precompile(nsd->db, zone); zone->nsec3_param = NULL; #endif @@ -1942,6 +2180,9 @@ task_process_del_zone(struct nsd* nsd, s /* remove from zonetree, apex, soa */ zopt = zone->opts; namedb_zone_delete(nsd->db, zone); +#ifdef NSEC3 + nsec3_superzone_precompile_for_apex(nsd->db, task->zname); +#endif /* remove from options (zone_list already edited by xfrd) */ zone_options_delete(nsd->options, zopt); } Index: usr.sbin/nsd/difffile.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/difffile.h,v diff -u -p -r1.7 difffile.h --- usr.sbin/nsd/difffile.h 3 Sep 2025 18:46:48 -0000 1.7 +++ usr.sbin/nsd/difffile.h 21 Sep 2026 16:29:07 -0000 @@ -15,6 +15,8 @@ #include "udb.h" struct nsd; struct nsdst; +struct ixfr_store; +struct collect_rrs; #define DIFF_PART_XXFR ('X'<<24 | 'X'<<16 | 'F'<<8 | 'R') #define DIFF_PART_XFRF ('X'<<24 | 'F'<<16 | 'R'<<8 | 'F') @@ -57,14 +59,16 @@ int diff_read_str(FILE* in, char* buf, s void delete_zone_rrs(namedb_type* db, zone_type* zone); /* delete an RR */ int delete_RR(namedb_type* db, const dname_type* dname, - uint16_t type, uint16_t klass, + uint16_t type, uint16_t klass, uint32_t ttl, buffer_type* packet, size_t rdatalen, zone_type *zone, - region_type* temp_region, int* softfail); + region_type* temp_region, int* softfail, + struct ixfr_store* ixfr_store); /* add an RR */ int add_RR(namedb_type* db, const dname_type* dname, uint16_t type, uint16_t klass, uint32_t ttl, buffer_type* packet, size_t rdatalen, zone_type *zone, - int* softfail); + int* softfail, struct ixfr_store* ixfr_store, + struct collect_rrs* collect_rrs); /* apply the xfr file identified by xfrfilenr to zone */ int apply_ixfr_for_zone(struct nsd* nsd, zone_type* zone, FILE* in, @@ -121,7 +125,7 @@ struct task_list_d { uint32_t oldserial, newserial; /** general variable. for some used to see if zname is present. */ uint64_t yesno; - struct dname zname[0]; + struct dname zname[]; }; #define TASKLIST(ptr) ((struct task_list_d*)UDB_PTR(ptr)) /** create udb for tasks */ Index: usr.sbin/nsd/dname.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/dname.c,v diff -u -p -r1.17 dname.c --- usr.sbin/nsd/dname.c 6 Sep 2025 17:41:37 -0000 1.17 +++ usr.sbin/nsd/dname.c 21 Sep 2026 16:29:07 -0000 @@ -26,7 +26,7 @@ const dname_type * dname_make(region_type *region, const uint8_t *name, int normalize) { size_t name_size = 0; - uint8_t label_offsets[MAXDOMAINLEN]; + uint8_t label_offsets[MAXDOMAINLEN/2+1]; uint8_t label_count = 0; const uint8_t *label = name; dname_type *result; @@ -42,17 +42,14 @@ dname_make(region_type *region, const ui ++label_count; name_size += label_length(label) + 1; - if (label_is_root(label)) - break; if (name_size > MAXDOMAINLEN) return NULL; + if (label_is_root(label)) + break; label = label_next(label); } - if (name_size > MAXDOMAINLEN) - return NULL; - assert(label_count <= MAXDOMAINLEN / 2 + 1); /* Reverse label offsets. */ @@ -473,7 +470,8 @@ dname_concatenate(region_type *region, { uint8_t temp[MAXDOMAINLEN]; - assert(left->name_size + right->name_size - 1 <= MAXDOMAINLEN); + if(left->name_size + right->name_size - 1 > MAXDOMAINLEN) + return NULL; memcpy(temp, dname_name(left), left->name_size - 1); memcpy(temp + left->name_size - 1, dname_name(right), right->name_size); @@ -619,4 +617,103 @@ is_dname_subdomain_of_case(const uint8_t /* The trailing portion is not at a label point. */ return 0; +} + +size_t +buf_dname_length(const uint8_t* buf, size_t len) +{ + size_t l = 0; + if(!buf || len == 0) + return l; + while(len > 0 && buf[0] != 0) { + size_t lablen = (size_t)(buf[0]); + if( (lablen&0xc0) ) + return 0; /* the name should be uncompressed */ + if(lablen+1 > len) + return 0; /* should fit in the buffer */ + l += lablen+1; + len -= lablen+1; + buf += lablen+1; + if(l > MAXDOMAINLEN) + return 0; + } + if(len == 0) + return 0; /* end label should fit in buffer */ + if(buf[0] != 0) + return 0; /* must end in root label */ + l += 1; /* for the end root label */ + if(l > MAXDOMAINLEN) + return 0; + return l; +} + +int +dname_make_buffered(struct dname_buffer* dname, uint8_t *name, int normalize) +{ + size_t name_size = 0; + uint8_t label_offsets[MAXDOMAINLEN/2+1]; + uint8_t label_count = 0; + const uint8_t *label = name; + ssize_t i; + + assert(name); + + while (1) { + if(!label_is_normal(label)) + return 0; + + label_offsets[label_count] = (uint8_t) (label - name); + ++label_count; + name_size += label_length(label) + 1; + + if (name_size > MAXDOMAINLEN) + return 0; + if (label_is_root(label)) + break; + + label = label_next(label); + } + + assert(label_count <= MAXDOMAINLEN / 2 + 1); + + /* Reverse label offsets. */ + for (i = 0; i < label_count / 2; ++i) { + uint8_t tmp = label_offsets[i]; + label_offsets[i] = label_offsets[label_count - i - 1]; + label_offsets[label_count - i - 1] = tmp; + } + + /* Move the name to the correct part of the result */ + memmove( ((char*)dname)+sizeof(struct dname)+ + label_count*sizeof(uint8_t), name, name_size); + dname->dname.name_size = name_size; + dname->dname.label_count = label_count; + memcpy((uint8_t *) dname_label_offsets((void*)dname), + label_offsets, label_count * sizeof(uint8_t)); + if (normalize) { + uint8_t *src = (uint8_t *) dname_name((void*)dname); + while (!label_is_root(src)) { + ssize_t len = label_length(src); + src++; + for (i = 0; i < len; ++i) { + *src = DNAME_NORMALIZE((unsigned char)*src); + src++; + } + } + } + return 1; +} + +int +dname_make_from_packet_buffered(struct dname_buffer* dname, + buffer_type *packet, int allow_pointers, int normalize) +{ + int wirelen = dname_make_wire_from_packet(dname->storage, + packet, allow_pointers); + if(wirelen == 0) + return 0; + if(!dname_make_buffered(dname, dname->storage, normalize)) + return 0; + assert(wirelen == dname->dname.name_size); + return wirelen; } Index: usr.sbin/nsd/dname.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/dname.h,v diff -u -p -r1.5 dname.h --- usr.sbin/nsd/dname.h 3 Sep 2025 18:46:48 -0000 1.5 +++ usr.sbin/nsd/dname.h 21 Sep 2026 16:29:07 -0000 @@ -403,4 +403,49 @@ int dname_equal_nocase(uint8_t* a, uint8 int is_dname_subdomain_of_case(const uint8_t* d, unsigned int len, const uint8_t* d2, unsigned int len2); +/* + * Calculate length of dname in uncompressed wireformat in buffer. + * @param buf: The buffer with the uncompressed dname. + * @param len: length of the buffer. + * @return 0 on error, otherwise the uncompressed wireformat dname + * length is returned. + */ +size_t buf_dname_length(const uint8_t* buf, size_t len); + +/* This structure is sufficient in size for a struct dname. It can + * be cast to a struct dname*, since it has the same data. */ +struct dname_buffer { + struct dname dname; + /* storage for labelcount, and for the wireformat name. + * The labelcount can be 1 byte per label, maxlen/2 + 1(root label). + * The wireformat name, MAXDOMAINLEN+1. + * This allocates storage for it, due to alignment, the struct + * dname may have padding. The content is stored after the + * sizeof(struct dname), with label_offsets and name. */ + uint8_t storage[MAXDOMAINLEN/2 + 1 + MAXDOMAINLEN+1 ]; +}; + +/* + * Make the dname and label offsets. + * @param dname: the buffer with size. The result is in here, at the + * start of the allocated size. The input is also buffered temporarily + * in here. + * @param name: input name, points into the buffer space of dname. + * @param normalize: if the dname has to be normalized. + * @return 0 on failure. + */ +int dname_make_buffered(struct dname_buffer* dname, uint8_t *name, + int normalize); + +/* + * Parse a domain name from packet and store it in the buffer. + * @param dname: the buffer with sufficient size for the dname. + * @param packet: packet, at the position of the dname. The position is moved. + * @param allow_pointers: set to true if compression pointers are allowed. + * @param normalize: set to true if the domain name has to be normalized. + * @return 0 on failure, or name_length when done. + */ +int dname_make_from_packet_buffered(struct dname_buffer* dname, + buffer_type *packet, int allow_pointers, int normalize); + #endif /* DNAME_H */ Index: usr.sbin/nsd/dns.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/dns.c,v diff -u -p -r1.23 dns.c --- usr.sbin/nsd/dns.c 6 Sep 2025 17:41:37 -0000 1.23 +++ usr.sbin/nsd/dns.c 21 Sep 2026 16:29:07 -0000 @@ -21,7 +21,7 @@ #endif #include "dns.h" -#include "zonec.h" +#include "rdata.h" /* Taken from RFC 1035, section 3.2.4. */ static lookup_table_type dns_rrclasses[] = { @@ -32,937 +32,1053 @@ static lookup_table_type dns_rrclasses[] { 0, NULL } }; -static rrtype_descriptor_type rrtype_descriptors[(RRTYPE_DESCRIPTORS_LENGTH+2)] = { +/* For a standard field, it is not optional, has no rdata field functions. */ +#define FIELD(name, size) { name, 0 /* is_optional */, size, NULL /* calc_len_func */, NULL /* calc_len_uncompressed_wire_func */ } + +/* For a field entry with all values, for optional fields, or with defined + * rdata field functions. */ +#define FIELD_ENTRY(name, is_optional, size, calc_len_func, cal_len_uncompressed_wire_func ) { name, is_optional, size, calc_len_func, cal_len_uncompressed_wire_func } + +static const struct nsd_rdata_descriptor generic_rdata_fields[] = { + FIELD("", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor a_rdata_fields[] = { + FIELD("address", 4) +}; + +static const struct nsd_rdata_descriptor ns_rdata_fields[] = { + FIELD("host", RDATA_COMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor md_rdata_fields[] = { + FIELD("madname", RDATA_UNCOMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor mf_rdata_fields[] = { + FIELD("madname", RDATA_UNCOMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor cname_rdata_fields[] = { + FIELD("host", RDATA_COMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor soa_rdata_fields[] = { + FIELD("primary", RDATA_COMPRESSED_DNAME), + FIELD("mailbox", RDATA_COMPRESSED_DNAME), + FIELD("serial", 4), + FIELD("refresh", 4), + FIELD("retry", 4), + FIELD("expire", 4), + FIELD("minimum", 4) +}; + +static const struct nsd_rdata_descriptor mb_rdata_fields[] = { + FIELD("madname", RDATA_COMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor mg_rdata_fields[] = { + FIELD("mgmname", RDATA_COMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor mr_rdata_fields[] = { + FIELD("newname", RDATA_COMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor wks_rdata_fields[] = { + FIELD("address", 4), + FIELD("protocol", 1), + FIELD("bitmap", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor ptr_rdata_fields[] = { + FIELD("ptrdname", RDATA_COMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor hinfo_rdata_fields[] = { + FIELD("cpu", RDATA_STRING), + FIELD("os", RDATA_STRING) +}; + +static const struct nsd_rdata_descriptor minfo_rdata_fields[] = { + FIELD("rmailbx", RDATA_COMPRESSED_DNAME), + FIELD("emailbx", RDATA_COMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor mx_rdata_fields[] = { + FIELD("priority", 2), + FIELD("hostname", RDATA_COMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor txt_rdata_fields[] = { + FIELD("text", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor rp_rdata_fields[] = { + FIELD("mailbox", RDATA_UNCOMPRESSED_DNAME), + FIELD("text", RDATA_UNCOMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor afsdb_rdata_fields[] = { + FIELD("subtype", 2), + FIELD("hostname", RDATA_UNCOMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor x25_rdata_fields[] = { + FIELD("address", RDATA_STRING) +}; + +static const struct nsd_rdata_descriptor isdn_rdata_fields[] = { + FIELD("address", RDATA_STRING), + FIELD_ENTRY("subaddress", 1, RDATA_STRING, NULL, NULL) +}; + +static const struct nsd_rdata_descriptor rt_rdata_fields[] = { + FIELD("preference", 2), + FIELD("hostname", RDATA_UNCOMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor nsap_rdata_fields[] = { + FIELD("address", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor nsap_ptr_rdata_fields[] = { + FIELD("hostname", RDATA_STRING) +}; + +static const struct nsd_rdata_descriptor sig_rdata_fields[] = { + FIELD("sigtype", 2), + FIELD("algorithm", 1), + FIELD("labels", 1), + FIELD("origttl", 4), + FIELD("expire", 4), + FIELD("inception", 4), + FIELD("keytag", 2), + FIELD("signer", RDATA_LITERAL_DNAME), + FIELD("signature", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor key_rdata_fields[] = { + FIELD("flags", 2), + FIELD("protocol", 1), + FIELD("algorithm", 1), + FIELD("publickey", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor px_rdata_fields[] = { + FIELD("preference", 2), + FIELD("map822", RDATA_UNCOMPRESSED_DNAME), + FIELD("mapx400", RDATA_UNCOMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor gpos_rdata_fields[] = { + FIELD("latitude", RDATA_STRING), + FIELD("longitude", RDATA_STRING), + FIELD("altitude", RDATA_STRING) +}; + +static const struct nsd_rdata_descriptor aaaa_rdata_fields[] = { + FIELD("address", 16) +}; + +static const struct nsd_rdata_descriptor loc_rdata_fields[] = { + FIELD("version", 1), + FIELD("size", 1), + FIELD("horizontal precision", 1), + FIELD("vertical precision", 1), + FIELD("latitude", 4), + FIELD("longitude", 4), + FIELD("altitude", 4), +}; + +static const struct nsd_rdata_descriptor nxt_rdata_fields[] = { + FIELD("next domain name", RDATA_UNCOMPRESSED_DNAME), + FIELD("type bit map", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor eid_rdata_fields[] = { + FIELD("end point identifier", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor nimloc_rdata_fields[] = { + FIELD("nimrod locator", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor srv_rdata_fields[] = { + FIELD("priority", 2), + FIELD("weight", 2), + FIELD("port", 2), + FIELD("target", RDATA_UNCOMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor atma_rdata_fields[] = { + FIELD("address", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor naptr_rdata_fields[] = { + FIELD("order", 2), + FIELD("preference", 2), + FIELD("flags", RDATA_STRING), + FIELD("services", RDATA_STRING), + FIELD("regex", RDATA_STRING), + FIELD("replacement", RDATA_UNCOMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor kx_rdata_fields[] = { + FIELD("preference", 2), + FIELD("exchanger", RDATA_UNCOMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor cert_rdata_fields[] = { + FIELD("type", 2), + FIELD("key tag", 2), + FIELD("algorithm", 1), + FIELD("certificate", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor a6_rdata_fields[] = { + FIELD("address", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor dname_rdata_fields[] = { + FIELD("source", RDATA_UNCOMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor sink_rdata_fields[] = { + FIELD("coding", 1), + FIELD("subcoding", 1), + FIELD("data", RDATA_REMAINDER), +}; + +static const struct nsd_rdata_descriptor apl_rdata_fields[] = { + FIELD_ENTRY("prefix", 1, RDATA_REMAINDER, NULL, NULL) +}; + +static const struct nsd_rdata_descriptor ds_rdata_fields[] = { + FIELD("keytag", 2), + FIELD("algorithm", 1), + FIELD("digtype", 1), + FIELD("digest", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor sshfp_rdata_fields[] = { + FIELD("algorithm", 1), + FIELD("ftype", 1), + FIELD("fingerprint", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor ipseckey_rdata_fields[] = { + FIELD("precedence", 1), + FIELD("gateway type", 1), + FIELD("algorithm", 1), + FIELD_ENTRY("gateway", 0, RDATA_IPSECGATEWAY, + ipseckey_gateway_length, ipseckey_gateway_length), + FIELD_ENTRY("public key", 1, RDATA_REMAINDER, NULL, NULL) +}; + +static const struct nsd_rdata_descriptor rrsig_rdata_fields[] = { + FIELD("rrtype", 2), + FIELD("algorithm", 1), + FIELD("labels", 1), + FIELD("origttl", 4), + FIELD("expire", 4), + FIELD("inception", 4), + FIELD("keytag", 2), + FIELD("signer", RDATA_LITERAL_DNAME), + FIELD("signature", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor nsec_rdata_fields[] = { + FIELD("next", RDATA_LITERAL_DNAME), + FIELD("types", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor dnskey_rdata_fields[] = { + FIELD("flags", 2), + FIELD("protocol", 1), + FIELD("algorithm", 1), + FIELD("publickey", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor dhcid_rdata_fields[] = { + FIELD("dhcpinfo", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor nsec3_rdata_fields[] = { + FIELD("algorithm", 1), + FIELD("flags", 1), + FIELD("iterations", 2), + FIELD("salt", RDATA_BINARY), + FIELD("next", RDATA_BINARY), + FIELD("types", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor nsec3param_rdata_fields[] = { + FIELD("algorithm", 1), + FIELD("flags", 1), + FIELD("iterations", 2), + FIELD("salt", RDATA_BINARY) +}; + +static const struct nsd_rdata_descriptor tlsa_rdata_fields[] = { + FIELD("usage", 1), + FIELD("selector", 1), + FIELD("matching type", 1), + FIELD("certificate association data", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor smimea_rdata_fields[] = { + FIELD("usage", 1), + FIELD("selector", 1), + FIELD("matching type", 1), + FIELD("certificate association data", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor hip_rdata_fields[] = { + FIELD("hip", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor ninfo_rdata_fields[] = { + FIELD("text", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor rkey_rdata_fields[] = { + FIELD("flags", 2), + FIELD("protocol", 1), + FIELD("algorithm", 1), + FIELD("publickey", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor talink_rdata_fields[] = { + FIELD("start or previous", RDATA_LITERAL_DNAME), + FIELD("end or next", RDATA_LITERAL_DNAME) +}; + +static const struct nsd_rdata_descriptor cds_rdata_fields[] = { + FIELD("keytag", 2), + FIELD("algorithm", 1), + FIELD("digtype", 1), + FIELD("digest", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor cdnskey_rdata_fields[] = { + FIELD("flags", 2), + FIELD("protocol", 1), + FIELD("algorithm", 1), + FIELD("publickey", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor openpgpkey_rdata_fields[] = { + FIELD("key", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor csync_rdata_fields[] = { + FIELD("serial", 4), + FIELD("flags", 2), + FIELD("types", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor zonemd_rdata_fields[] = { + FIELD("serial", 4), + FIELD("scheme", 1), + FIELD("algorithm", 1), + FIELD("digest", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor svcb_rdata_fields[] = { + FIELD("priority", 2), + FIELD("target", RDATA_UNCOMPRESSED_DNAME), + FIELD("params", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor https_rdata_fields[] = { + FIELD("priority", 2), + FIELD("target", RDATA_UNCOMPRESSED_DNAME), + FIELD("params", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor dsync_rdata_fields[] = { + FIELD("rrtype", 2), + FIELD("scheme", 1), + FIELD("port", 2), + FIELD("target", RDATA_LITERAL_DNAME) +}; + +static const struct nsd_rdata_descriptor hhit_rdata_fields[] = { + FIELD("cbor blob", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor brid_rdata_fields[] = { + FIELD("cbor blob", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor spf_rdata_fields[] = { + FIELD("text", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor nid_rdata_fields[] = { + FIELD("nid", 2), + FIELD("locator", 8) +}; + +static const struct nsd_rdata_descriptor l32_rdata_fields[] = { + FIELD("preference", 2), + FIELD("locator", 4) +}; + +static const struct nsd_rdata_descriptor l64_rdata_fields[] = { + FIELD("preference", 2), + FIELD("locator", 8) +}; + +static const struct nsd_rdata_descriptor lp_rdata_fields[] = { + FIELD("preference", 2), + FIELD("pointer", RDATA_UNCOMPRESSED_DNAME) +}; + +static const struct nsd_rdata_descriptor eui48_rdata_fields[] = { + FIELD("address", 6) +}; + +static const struct nsd_rdata_descriptor eui64_rdata_fields[] = { + FIELD("address", 8) +}; + +static const struct nsd_rdata_descriptor uri_rdata_fields[] = { + FIELD("priority", 2), + FIELD("weight", 2), + FIELD("target", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor caa_rdata_fields[] = { + FIELD("flags", 1), + FIELD("tag", RDATA_STRING), + FIELD("value", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor avc_rdata_fields[] = { + FIELD("text", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor doa_rdata_fields[] = { + FIELD("enterprise", 4), + FIELD("type", 4), + FIELD("location", 1), + FIELD("media type", RDATA_STRING), + FIELD("data", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor amtrelay_rdata_fields[] = { + FIELD("precedence", 1), + FIELD("discovery_type", 1), + FIELD_ENTRY("relay", 0, RDATA_AMTRELAY_RELAY, + amtrelay_relay_length, amtrelay_relay_length) +}; + +static const struct nsd_rdata_descriptor resinfo_rdata_fields[] = { + FIELD("text", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor wallet_rdata_fields[] = { + FIELD("wallet", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor cla_rdata_fields[] = { + FIELD("CLA", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor ipn_rdata_fields[] = { + FIELD("CBHE Node Number", 8) +}; + +static const struct nsd_rdata_descriptor ta_rdata_fields[] = { + FIELD("key", 2), + FIELD("algorithm", 1), + FIELD("type", 1), + FIELD("digest", RDATA_REMAINDER) +}; + +static const struct nsd_rdata_descriptor dlv_rdata_fields[] = { + FIELD("key", 2), + FIELD("algorithm", 1), + FIELD("type", 1), + FIELD("digest", RDATA_REMAINDER) +}; + +#define TYPE(name, code, bools, read, write, print, fields) \ + { code, name, bools, read, write, print, { sizeof(fields)/sizeof(fields[0]), fields } } + +#define UNKNOWN_TYPE(code) \ + { code, NULL /* mnemonic */, 0 /* has_references */, 0 /* is_compressible */, 0 /* has_dnames */, read_generic_rdata, write_generic_rdata, print_generic_rdata, { sizeof(generic_rdata_fields)/sizeof(generic_rdata_fields[0]), generic_rdata_fields } } + +/* The RR type has no references, it is a binary wireformat. + * has_references, is_compressible, has_dnames. */ +#define TYPE_HAS_NO_REFS 0, 0, 0 +/* The RR type has references, it has compressed dnames. */ +#define TYPE_HAS_COMPRESSED_DNAME 1, 1, 1 +/* The RR type has references, it has uncompressed dnames. */ +#define TYPE_HAS_UNCOMPRESSED_DNAME 1, 0, 1 +/* The RR type has no references, it has literal dnames. */ +#define TYPE_HAS_LITERAL_DNAME 0, 0, 1 +/* Set the bools, has_references, is_compressible, has_dnames. */ +#define TYPE_HAS_FLAGS(has_references, is_compressible, has_dnames) has_references, is_compressible, has_dnames + +const nsd_type_descriptor_type type_descriptors[] = { /* 0 */ - { 0, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + UNKNOWN_TYPE(0), /* Type 0 - Reserved [RFC 6895] */ /* 1 */ - { TYPE_A, "A", 1, 1, - { RDATA_WF_A }, { RDATA_ZF_A } }, + TYPE("A", TYPE_A, TYPE_HAS_NO_REFS, + read_a_rdata, write_generic_rdata, + print_a_rdata, a_rdata_fields), /* 2 */ - { TYPE_NS, "NS", 1, 1, - { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + TYPE("NS", TYPE_NS, TYPE_HAS_COMPRESSED_DNAME, + read_compressed_name_rdata, write_compressed_name_rdata, + print_name_rdata, ns_rdata_fields), /* 3 */ - { TYPE_MD, "MD", 1, 1, - { RDATA_WF_UNCOMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + TYPE("MD", TYPE_MD, TYPE_HAS_UNCOMPRESSED_DNAME, + read_uncompressed_name_rdata, write_uncompressed_name_rdata, + print_name_rdata, md_rdata_fields), /* 4 */ - { TYPE_MF, "MF", 1, 1, - { RDATA_WF_UNCOMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + TYPE("MF", TYPE_MF, TYPE_HAS_UNCOMPRESSED_DNAME, + read_uncompressed_name_rdata, write_uncompressed_name_rdata, + print_name_rdata, mf_rdata_fields), /* 5 */ - { TYPE_CNAME, "CNAME", 1, 1, - { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + TYPE("CNAME", TYPE_CNAME, TYPE_HAS_COMPRESSED_DNAME, + read_compressed_name_rdata, write_compressed_name_rdata, + print_name_rdata, cname_rdata_fields), /* 6 */ - { 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 } }, + TYPE("SOA", TYPE_SOA, TYPE_HAS_COMPRESSED_DNAME, + read_soa_rdata, write_soa_rdata, + print_soa_rdata, soa_rdata_fields), /* 7 */ - { TYPE_MB, "MB", 1, 1, - { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + TYPE("MB", TYPE_MB, TYPE_HAS_COMPRESSED_DNAME, + read_compressed_name_rdata, write_compressed_name_rdata, + print_name_rdata, mb_rdata_fields), /* 8 */ - { TYPE_MG, "MG", 1, 1, - { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + TYPE("MG", TYPE_MG, TYPE_HAS_COMPRESSED_DNAME, + read_compressed_name_rdata, write_compressed_name_rdata, + print_name_rdata, mg_rdata_fields), /* 9 */ - { TYPE_MR, "MR", 1, 1, - { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + TYPE("MR", TYPE_MR, TYPE_HAS_COMPRESSED_DNAME, + read_compressed_name_rdata, write_compressed_name_rdata, + print_name_rdata, mr_rdata_fields), /* 10 */ - { TYPE_NULL, "NULL", 1, 1, - { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + TYPE("NULL", TYPE_NULL, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, + print_generic_rdata, generic_rdata_fields), /* 11 */ - { TYPE_WKS, "WKS", 2, 2, - { RDATA_WF_A, RDATA_WF_BINARY }, - { RDATA_ZF_A, RDATA_ZF_SERVICES } }, + TYPE("WKS", TYPE_WKS, TYPE_HAS_NO_REFS, + read_wks_rdata, write_generic_rdata, + print_wks_rdata, wks_rdata_fields), /* 12 */ - { TYPE_PTR, "PTR", 1, 1, - { RDATA_WF_COMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + TYPE("PTR", TYPE_PTR, TYPE_HAS_COMPRESSED_DNAME, + read_compressed_name_rdata, write_compressed_name_rdata, + print_name_rdata, ptr_rdata_fields), /* 13 */ - { TYPE_HINFO, "HINFO", 2, 2, - { RDATA_WF_TEXT, RDATA_WF_TEXT }, { RDATA_ZF_TEXT, RDATA_ZF_TEXT } }, + TYPE("HINFO", TYPE_HINFO, TYPE_HAS_NO_REFS, + read_hinfo_rdata, write_generic_rdata, + print_hinfo_rdata, hinfo_rdata_fields), /* 14 */ - { TYPE_MINFO, "MINFO", 2, 2, - { RDATA_WF_COMPRESSED_DNAME, RDATA_WF_COMPRESSED_DNAME }, - { RDATA_ZF_DNAME, RDATA_ZF_DNAME } }, + TYPE("MINFO", TYPE_MINFO, TYPE_HAS_COMPRESSED_DNAME, + read_minfo_rdata, write_minfo_rdata, + print_minfo_rdata, minfo_rdata_fields), /* 15 */ - { TYPE_MX, "MX", 2, 2, - { RDATA_WF_SHORT, RDATA_WF_COMPRESSED_DNAME }, - { RDATA_ZF_SHORT, RDATA_ZF_DNAME } }, + TYPE("MX", TYPE_MX, TYPE_HAS_COMPRESSED_DNAME, + read_mx_rdata, write_mx_rdata, + print_mx_rdata, mx_rdata_fields), /* 16 */ - { TYPE_TXT, "TXT", 1, 1, - { RDATA_WF_TEXTS }, - { RDATA_ZF_TEXTS } }, + TYPE("TXT", TYPE_TXT, TYPE_HAS_NO_REFS, + read_txt_rdata, write_generic_rdata, + print_txt_rdata, txt_rdata_fields), /* 17 */ - { TYPE_RP, "RP", 2, 2, - { RDATA_WF_UNCOMPRESSED_DNAME, RDATA_WF_UNCOMPRESSED_DNAME }, - { RDATA_ZF_DNAME, RDATA_ZF_DNAME } }, + TYPE("RP", TYPE_RP, TYPE_HAS_UNCOMPRESSED_DNAME, + read_rp_rdata, write_rp_rdata, print_rp_rdata, + rp_rdata_fields), /* 18 */ - { TYPE_AFSDB, "AFSDB", 2, 2, - { RDATA_WF_SHORT, RDATA_WF_UNCOMPRESSED_DNAME }, - { RDATA_ZF_SHORT, RDATA_ZF_DNAME } }, + TYPE("AFSDB", TYPE_AFSDB, TYPE_HAS_UNCOMPRESSED_DNAME, + read_afsdb_rdata, write_afsdb_rdata, print_afsdb_rdata, + afsdb_rdata_fields), /* 19 */ - { TYPE_X25, "X25", 1, 1, - { RDATA_WF_TEXT }, - { RDATA_ZF_TEXT } }, + TYPE("X25", TYPE_X25, TYPE_HAS_NO_REFS, + read_x25_rdata, write_generic_rdata, print_x25_rdata, + x25_rdata_fields), /* 20 */ - { TYPE_ISDN, "ISDN", 1, 2, - { RDATA_WF_TEXT, RDATA_WF_TEXT }, - { RDATA_ZF_TEXT, RDATA_ZF_TEXT } }, + TYPE("ISDN", TYPE_ISDN, TYPE_HAS_NO_REFS, + read_isdn_rdata, write_generic_rdata, print_isdn_rdata, + isdn_rdata_fields), /* 21 */ - { TYPE_RT, "RT", 2, 2, - { RDATA_WF_SHORT, RDATA_WF_UNCOMPRESSED_DNAME }, - { RDATA_ZF_SHORT, RDATA_ZF_DNAME } }, + TYPE("RT", TYPE_RT, TYPE_HAS_UNCOMPRESSED_DNAME, + read_rt_rdata, write_rt_rdata, print_mx_rdata, + rt_rdata_fields), /* 22 */ - { TYPE_NSAP, "NSAP", 1, 1, - { RDATA_WF_BINARY }, - { RDATA_ZF_NSAP } }, + TYPE("NSAP", TYPE_NSAP, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, print_nsap_rdata, + nsap_rdata_fields), /* 23 */ - { TYPE_NSAP_PTR, "NSAP-PTR", 1, 1, - { RDATA_WF_TEXT }, { RDATA_ZF_UNQUOTED } }, + TYPE("NSAP-PTR", TYPE_NSAP_PTR, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, + print_nsap_ptr_rdata, nsap_ptr_rdata_fields), /* 24 */ - { 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 }, - { RDATA_ZF_RRTYPE, RDATA_ZF_ALGORITHM, RDATA_ZF_BYTE, RDATA_ZF_PERIOD, - RDATA_ZF_TIME, RDATA_ZF_TIME, RDATA_ZF_SHORT, - RDATA_ZF_LITERAL_DNAME, RDATA_ZF_BASE64 } }, + TYPE("SIG", TYPE_SIG, TYPE_HAS_LITERAL_DNAME, + read_rrsig_rdata, write_generic_rdata, print_rrsig_rdata, + sig_rdata_fields), /* 25 */ - { 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 } }, + TYPE("KEY", TYPE_KEY, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, print_key_rdata, + key_rdata_fields), /* 26 */ - { 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 } }, + TYPE("PX", TYPE_PX, TYPE_HAS_UNCOMPRESSED_DNAME, + read_px_rdata, write_px_rdata, print_px_rdata, + px_rdata_fields), /* 27 */ - { TYPE_GPOS, "GPOS", 3, 3, - { RDATA_WF_TEXT, RDATA_WF_TEXT, RDATA_WF_TEXT }, - { RDATA_ZF_UNQUOTED, RDATA_ZF_UNQUOTED, RDATA_ZF_UNQUOTED} }, + TYPE("GPOS", TYPE_GPOS, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, print_gpos_rdata, + gpos_rdata_fields), /* 28 */ - { TYPE_AAAA, "AAAA", 1, 1, - { RDATA_WF_AAAA }, - { RDATA_ZF_AAAA } }, + TYPE("AAAA", TYPE_AAAA, TYPE_HAS_NO_REFS, + read_aaaa_rdata, write_generic_rdata, print_aaaa_rdata, + aaaa_rdata_fields), /* 29 */ - { TYPE_LOC, "LOC", 1, 1, - { RDATA_WF_BINARY }, - { RDATA_ZF_LOC } }, + TYPE("LOC", TYPE_LOC, TYPE_HAS_NO_REFS, + read_loc_rdata, write_generic_rdata, print_loc_rdata, + loc_rdata_fields), /* 30 */ - { TYPE_NXT, "NXT", 2, 2, - { RDATA_WF_UNCOMPRESSED_DNAME, RDATA_WF_BINARY }, - { RDATA_ZF_DNAME, RDATA_ZF_NXT } }, + TYPE("NXT", TYPE_NXT, TYPE_HAS_UNCOMPRESSED_DNAME, + read_nxt_rdata, write_nxt_rdata, print_nxt_rdata, + nxt_rdata_fields), /* 31 */ - { TYPE_EID, "EID", 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_HEX } }, + TYPE("EID", TYPE_EID, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, print_eid_rdata, + eid_rdata_fields), /* 32 */ - { TYPE_NIMLOC, "NIMLOC", 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_HEX } }, + TYPE("NIMLOC", TYPE_NIMLOC, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, print_nimloc_rdata, + nimloc_rdata_fields), /* 33 */ - { 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 } }, + TYPE("SRV", TYPE_SRV, TYPE_HAS_UNCOMPRESSED_DNAME, + read_srv_rdata, write_srv_rdata, + print_srv_rdata, srv_rdata_fields), /* 34 */ - { TYPE_ATMA, "ATMA", 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_ATMA } }, + TYPE("ATMA", TYPE_ATMA, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, print_atma_rdata, + atma_rdata_fields), /* 35 */ - { 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 } }, + TYPE("NAPTR", TYPE_NAPTR, TYPE_HAS_UNCOMPRESSED_DNAME, + read_naptr_rdata, write_naptr_rdata, + print_naptr_rdata, naptr_rdata_fields), /* 36 */ - { TYPE_KX, "KX", 2, 2, - { RDATA_WF_SHORT, RDATA_WF_UNCOMPRESSED_DNAME }, - { RDATA_ZF_SHORT, RDATA_ZF_DNAME } }, + TYPE("KX", TYPE_KX, TYPE_HAS_UNCOMPRESSED_DNAME, + read_kx_rdata, write_kx_rdata, + print_mx_rdata, kx_rdata_fields), /* 37 */ - { 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 } }, + TYPE("CERT", TYPE_CERT, TYPE_HAS_NO_REFS, + read_cert_rdata, write_generic_rdata, + print_cert_rdata, cert_rdata_fields), /* 38 */ - { TYPE_A6, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + TYPE("A6", TYPE_A6, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, + print_generic_rdata, a6_rdata_fields), /* 39 */ - { TYPE_DNAME, "DNAME", 1, 1, - { RDATA_WF_UNCOMPRESSED_DNAME }, { RDATA_ZF_DNAME } }, + TYPE("DNAME", TYPE_DNAME, TYPE_HAS_UNCOMPRESSED_DNAME, + read_uncompressed_name_rdata, write_uncompressed_name_rdata, + print_name_rdata, dname_rdata_fields), /* 40 */ - { TYPE_SINK, "SINK", 3, 3, - { RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY }, - { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_BASE64 } }, - /* 41 */ - { TYPE_OPT, "OPT", 1, 1, - { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + TYPE("SINK", TYPE_SINK, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, print_sink_rdata, + sink_rdata_fields), + + UNKNOWN_TYPE(41), /* Type 41 - OPT */ + /* 42 */ - { 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, - 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, - 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, - 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, - 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, - RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL, RDATA_WF_APL }, - { RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, - RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL, RDATA_ZF_APL } }, + TYPE("APL", TYPE_APL, TYPE_HAS_NO_REFS, + read_apl_rdata, write_generic_rdata, + print_apl_rdata, apl_rdata_fields), /* 43 */ - { 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 } }, + TYPE("DS", TYPE_DS, TYPE_HAS_NO_REFS, + read_ds_rdata, write_generic_rdata, + print_ds_rdata, ds_rdata_fields), /* 44 */ - { TYPE_SSHFP, "SSHFP", 3, 3, - { RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY }, - { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_HEX } }, + TYPE("SSHFP", TYPE_SSHFP, TYPE_HAS_NO_REFS, + read_sshfp_rdata, write_generic_rdata, + print_sshfp_rdata, sshfp_rdata_fields), /* 45 */ - { 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 } }, + TYPE("IPSECKEY", TYPE_IPSECKEY, TYPE_HAS_LITERAL_DNAME, + read_ipseckey_rdata, write_generic_rdata, + print_ipseckey_rdata, ipseckey_rdata_fields), /* 46 */ - { 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 }, - { RDATA_ZF_RRTYPE, RDATA_ZF_ALGORITHM, RDATA_ZF_BYTE, RDATA_ZF_PERIOD, - RDATA_ZF_TIME, RDATA_ZF_TIME, RDATA_ZF_SHORT, - RDATA_ZF_LITERAL_DNAME, RDATA_ZF_BASE64 } }, + TYPE("RRSIG", TYPE_RRSIG, TYPE_HAS_LITERAL_DNAME, + read_rrsig_rdata, write_generic_rdata, + print_rrsig_rdata, rrsig_rdata_fields), /* 47 */ - { TYPE_NSEC, "NSEC", 2, 2, - { RDATA_WF_LITERAL_DNAME, RDATA_WF_BINARY }, - { RDATA_ZF_LITERAL_DNAME, RDATA_ZF_NSEC } }, + TYPE("NSEC", TYPE_NSEC, TYPE_HAS_LITERAL_DNAME, + read_nsec_rdata, write_generic_rdata, + print_nsec_rdata, nsec_rdata_fields), /* 48 */ - { 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 } }, + TYPE("DNSKEY", TYPE_DNSKEY, TYPE_HAS_NO_REFS, + read_dnskey_rdata, write_generic_rdata, + print_dnskey_rdata, dnskey_rdata_fields), /* 49 */ - { TYPE_DHCID, "DHCID", 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_BASE64 } }, + TYPE("DHCID", TYPE_DHCID, TYPE_HAS_NO_REFS, + read_dhcid_rdata, write_generic_rdata, + print_dhcid_rdata, dhcid_rdata_fields), /* 50 */ - { TYPE_NSEC3, "NSEC3", 6, 6, - { RDATA_WF_BYTE, /* hash type */ - RDATA_WF_BYTE, /* flags */ - RDATA_WF_SHORT, /* iterations */ - RDATA_WF_BINARYWITHLENGTH, /* salt */ - RDATA_WF_BINARYWITHLENGTH, /* next hashed name */ - RDATA_WF_BINARY /* type bitmap */ }, - { RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_SHORT, RDATA_ZF_HEX_LEN, - RDATA_ZF_BASE32, RDATA_ZF_NSEC } }, + TYPE("NSEC3", TYPE_NSEC3, TYPE_HAS_NO_REFS, + read_nsec3_rdata, write_generic_rdata, + print_nsec3_rdata, nsec3_rdata_fields), /* 51 */ - { 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 } }, + TYPE("NSEC3PARAM", TYPE_NSEC3PARAM, TYPE_HAS_NO_REFS, + read_nsec3param_rdata, write_generic_rdata, + print_nsec3param_rdata, nsec3param_rdata_fields), /* 52 */ - { 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 } }, + TYPE("TLSA", TYPE_TLSA, TYPE_HAS_NO_REFS, + read_tlsa_rdata, write_generic_rdata, + print_tlsa_rdata, tlsa_rdata_fields), /* 53 */ - { 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, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 55 - HIP [RFC 5205] */ - { 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 */ - { TYPE_NINFO, "NINFO", 1, 1, - { RDATA_WF_TEXTS }, - { RDATA_ZF_TEXTS } }, - /* 57 - RKEY */ - { 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 */ - { TYPE_TALINK, "TALINK", 2, 2, - { RDATA_WF_LITERAL_DNAME, RDATA_WF_LITERAL_DNAME }, - { RDATA_ZF_LITERAL_DNAME, RDATA_ZF_LITERAL_DNAME } }, - /* 59 - CDS */ - { 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", 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", 1, 1, - { RDATA_WF_BINARY }, { RDATA_ZF_BASE64 } }, - /* 62 - CSYNC */ - { 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", 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", 2, MAXRDATALEN, - { RDATA_WF_SHORT /* SvcFieldPriority */ - , RDATA_WF_UNCOMPRESSED_DNAME /* SvcDomainName */ - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM /* SvcFieldValue */ - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - }, - { RDATA_ZF_SHORT , RDATA_ZF_DNAME - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - } }, - /* 65 - HTTPS */ - { TYPE_HTTPS, "HTTPS", 2, MAXRDATALEN, - { RDATA_WF_SHORT /* SvcFieldPriority */ - , RDATA_WF_UNCOMPRESSED_DNAME /* SvcDomainName */ - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM /* SvcFieldValue */ - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - , RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM, RDATA_WF_SVCPARAM - }, - { RDATA_ZF_SHORT , RDATA_ZF_DNAME - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - , RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM, RDATA_ZF_SVCPARAM - } }, + TYPE("SMIMEA", TYPE_SMIMEA, TYPE_HAS_NO_REFS, + read_tlsa_rdata, write_generic_rdata, + print_tlsa_rdata, smimea_rdata_fields), + + UNKNOWN_TYPE(54), + + /* 55 */ + TYPE("HIP", TYPE_HIP, TYPE_HAS_LITERAL_DNAME, + read_hip_rdata, write_generic_rdata, + print_hip_rdata, hip_rdata_fields), + /* 56 */ + TYPE("NINFO", TYPE_NINFO, TYPE_HAS_NO_REFS, + read_txt_rdata, write_generic_rdata, + print_txt_rdata, ninfo_rdata_fields), + /* 57 */ + TYPE("RKEY", TYPE_RKEY, TYPE_HAS_NO_REFS, + read_rkey_rdata, write_generic_rdata, + print_rkey_rdata, rkey_rdata_fields), + /* 58 */ + TYPE("TALINK", TYPE_TALINK, TYPE_HAS_LITERAL_DNAME, + read_talink_rdata, write_generic_rdata, + print_talink_rdata, talink_rdata_fields), + /* 59 */ + TYPE("CDS", TYPE_CDS, TYPE_HAS_NO_REFS, + read_ds_rdata, write_generic_rdata, + print_ds_rdata, cds_rdata_fields), + /* 60 */ + TYPE("CDNSKEY", TYPE_CDNSKEY, TYPE_HAS_NO_REFS, + read_dnskey_rdata, write_generic_rdata, + print_dnskey_rdata, cdnskey_rdata_fields), + /* 61 */ + TYPE("OPENPGPKEY", TYPE_OPENPGPKEY, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, + print_openpgpkey_rdata, openpgpkey_rdata_fields), + /* 62 */ + TYPE("CSYNC", TYPE_CSYNC, TYPE_HAS_NO_REFS, + read_csync_rdata, write_generic_rdata, + print_csync_rdata, csync_rdata_fields), + /* 63 */ + TYPE("ZONEMD", TYPE_ZONEMD, TYPE_HAS_NO_REFS, + read_zonemd_rdata, write_generic_rdata, + print_zonemd_rdata, zonemd_rdata_fields), + /* 64 */ + TYPE("SVCB", TYPE_SVCB, TYPE_HAS_UNCOMPRESSED_DNAME, + read_svcb_rdata, write_svcb_rdata, + print_svcb_rdata, svcb_rdata_fields), + /* 65 */ + TYPE("HTTPS", TYPE_HTTPS, TYPE_HAS_UNCOMPRESSED_DNAME, + read_svcb_rdata, write_svcb_rdata, + print_svcb_rdata, https_rdata_fields), /* 66 */ - { TYPE_DSYNC, "DSYNC", 4, 4, - { RDATA_WF_SHORT , RDATA_WF_BYTE, RDATA_WF_SHORT - , RDATA_WF_LITERAL_DNAME }, - { RDATA_ZF_RRTYPE, RDATA_ZF_BYTE, RDATA_ZF_SHORT - , RDATA_ZF_LITERAL_DNAME } }, + TYPE("DSYNC", TYPE_DSYNC, TYPE_HAS_LITERAL_DNAME, + read_dsync_rdata, write_generic_rdata, + print_dsync_rdata, dsync_rdata_fields), /* 67 */ - { 67, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + TYPE("HHIT", TYPE_HHIT, TYPE_HAS_NO_REFS, + read_hhit_rdata, write_generic_rdata, + print_hhit_rdata, hhit_rdata_fields), /* 68 */ - { 68, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 69 */ - { 69, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 70 */ - { 70, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 71 */ - { 71, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 72 */ - { 72, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 73 */ - { 73, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 74 */ - { 74, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 75 */ - { 75, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 76 */ - { 76, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 77 */ - { 77, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 78 */ - { 78, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 79 */ - { 79, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 80 */ - { 80, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 81 */ - { 81, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 82 */ - { 82, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 83 */ - { 83, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 84 */ - { 84, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 85 */ - { 85, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 86 */ - { 86, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 87 */ - { 87, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 88 */ - { 88, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 89 */ - { 89, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 90 */ - { 90, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 91 */ - { 91, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 92 */ - { 92, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 93 */ - { 93, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 94 */ - { 94, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 95 */ - { 95, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 96 */ - { 96, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 97 */ - { 97, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 98 */ - { 98, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + TYPE("BRID", TYPE_BRID, TYPE_HAS_NO_REFS, + read_brid_rdata, write_generic_rdata, + print_brid_rdata, brid_rdata_fields), + + 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), + /* 99 */ - { TYPE_SPF, "SPF", 1, MAXRDATALEN, - { RDATA_WF_TEXTS }, { RDATA_ZF_TEXTS } }, - /* 100 - UINFO */ - { 100, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 101 - UID */ - { 101, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 102 - GID */ - { 102, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 103 - UNSPEC */ - { 103, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, + TYPE("SPF", TYPE_SPF, TYPE_HAS_NO_REFS, + read_txt_rdata, write_generic_rdata, + print_txt_rdata, spf_rdata_fields), + + UNKNOWN_TYPE(100), /* Type 100 - UINFO [The RR type code is reserved, no reference] */ + UNKNOWN_TYPE(101), /* Type 101 - UID [The RR type code is reserved, no reference] */ + UNKNOWN_TYPE(102), /* Type 102 - GID [The RR type code is reserved, no reference] */ + UNKNOWN_TYPE(103), /* Type 103 - UNSPEC [The RR type code is reserved, no reference] */ + /* 104 */ - { TYPE_NID, "NID", 2, 2, - { RDATA_WF_SHORT, RDATA_WF_ILNP64 }, - { RDATA_ZF_SHORT, RDATA_ZF_ILNP64 } }, + TYPE("NID", TYPE_NID, TYPE_HAS_NO_REFS, + read_nid_rdata, write_generic_rdata, + print_nid_rdata, nid_rdata_fields), /* 105 */ - { TYPE_L32, "L32", 2, 2, - { RDATA_WF_SHORT, RDATA_WF_A }, - { RDATA_ZF_SHORT, RDATA_ZF_A } }, + TYPE("L32", TYPE_L32, TYPE_HAS_NO_REFS, + read_l32_rdata, write_generic_rdata, + print_l32_rdata, l32_rdata_fields), /* 106 */ - { TYPE_L64, "L64", 2, 2, - { RDATA_WF_SHORT, RDATA_WF_ILNP64 }, - { RDATA_ZF_SHORT, RDATA_ZF_ILNP64 } }, + TYPE("L64", TYPE_L64, TYPE_HAS_NO_REFS, + read_l64_rdata, write_generic_rdata, + print_l64_rdata, l64_rdata_fields), /* 107 */ - { TYPE_LP, "LP", 2, 2, - { RDATA_WF_SHORT, RDATA_WF_UNCOMPRESSED_DNAME }, - { RDATA_ZF_SHORT, RDATA_ZF_DNAME } }, + TYPE("LP", TYPE_LP, TYPE_HAS_UNCOMPRESSED_DNAME, + read_lp_rdata, write_lp_rdata, + print_lp_rdata, lp_rdata_fields), /* 108 */ - { TYPE_EUI48, "EUI48", 1, 1, - { RDATA_WF_EUI48 }, { RDATA_ZF_EUI48 } }, + TYPE("EUI48", TYPE_EUI48, TYPE_HAS_NO_REFS, + read_eui48_rdata, write_generic_rdata, + print_eui48_rdata, eui48_rdata_fields), /* 109 */ - { TYPE_EUI64, "EUI64", 1, 1, - { RDATA_WF_EUI64 }, { RDATA_ZF_EUI64 } }, - /* 110 */ - { 110, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 111 */ - { 111, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 112 */ - { 112, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 113 */ - { 113, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 114 */ - { 114, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 115 */ - { 115, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 116 */ - { 116, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 117 */ - { 117, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 118 */ - { 118, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 119 */ - { 119, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 120 */ - { 120, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 121 */ - { 121, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 122 */ - { 122, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 123 */ - { 123, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 124 */ - { 124, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 125 */ - { 125, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 126 */ - { 126, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 127 */ - { 127, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 128 */ - { 128, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 129 */ - { 129, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 130 */ - { 130, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 131 */ - { 131, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 132 */ - { 132, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 133 */ - { 133, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 134 */ - { 134, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 135 */ - { 135, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 136 */ - { 136, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 137 */ - { 137, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 138 */ - { 138, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 139 */ - { 139, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 140 */ - { 140, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 141 */ - { 141, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 142 */ - { 142, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 143 */ - { 143, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 144 */ - { 144, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 145 */ - { 145, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 146 */ - { 146, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 147 */ - { 147, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 148 */ - { 148, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 149 */ - { 149, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 150 */ - { 150, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 151 */ - { 151, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 152 */ - { 152, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 153 */ - { 153, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 154 */ - { 154, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 155 */ - { 155, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 156 */ - { 156, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 157 */ - { 157, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 158 */ - { 158, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 159 */ - { 159, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 160 */ - { 160, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 161 */ - { 161, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 162 */ - { 162, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 163 */ - { 163, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 164 */ - { 164, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 165 */ - { 165, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 166 */ - { 166, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 167 */ - { 167, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 168 */ - { 168, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 169 */ - { 169, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 170 */ - { 170, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 171 */ - { 171, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 172 */ - { 172, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 173 */ - { 173, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 174 */ - { 174, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 175 */ - { 175, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 176 */ - { 176, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 177 */ - { 177, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 178 */ - { 178, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 179 */ - { 179, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 180 */ - { 180, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 181 */ - { 181, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 182 */ - { 182, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 183 */ - { 183, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 184 */ - { 184, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 185 */ - { 185, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 186 */ - { 186, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 187 */ - { 187, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 188 */ - { 188, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 189 */ - { 189, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 190 */ - { 190, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 191 */ - { 191, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 192 */ - { 192, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 193 */ - { 193, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 194 */ - { 194, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 195 */ - { 195, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 196 */ - { 196, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 197 */ - { 197, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 198 */ - { 198, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 199 */ - { 199, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 200 */ - { 200, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 201 */ - { 201, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 202 */ - { 202, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 203 */ - { 203, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 204 */ - { 204, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 205 */ - { 205, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 206 */ - { 206, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 207 */ - { 207, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 208 */ - { 208, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 209 */ - { 209, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 210 */ - { 210, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 211 */ - { 211, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 212 */ - { 212, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 213 */ - { 213, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 214 */ - { 214, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 215 */ - { 215, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 216 */ - { 216, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 217 */ - { 217, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 218 */ - { 218, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 219 */ - { 219, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 220 */ - { 220, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 221 */ - { 221, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 222 */ - { 222, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 223 */ - { 223, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 224 */ - { 224, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 225 */ - { 225, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 226 */ - { 226, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 227 */ - { 227, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 228 */ - { 228, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 229 */ - { 229, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 230 */ - { 230, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 231 */ - { 231, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 232 */ - { 232, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 233 */ - { 233, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 234 */ - { 234, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 235 */ - { 235, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 236 */ - { 236, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 237 */ - { 237, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 238 */ - { 238, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 239 */ - { 239, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 240 */ - { 240, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 241 */ - { 241, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 242 */ - { 242, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 243 */ - { 243, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 244 */ - { 244, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 245 */ - { 245, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 246 */ - { 246, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 247 */ - { 247, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 248 */ - { 248, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 249 - TKEY [RFC 2930] */ - { 249, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 250 - TSIG [RFC 2845] */ - { 250, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 251 - IXFR [RFC 1995] */ - { 251, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 252 - AXFR [RFC 1035, RFC 5936] */ - { 252, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 253 - MAILB [RFC 1035] */ - { 253, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 254 - MAILA [RFC 1035] */ - { 254, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 255 - * [RFC 1035, RFC 6895] */ - { 255, NULL, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 256 - URI */ - { 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", 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", 1, 1, - { RDATA_WF_TEXTS }, - { RDATA_ZF_TEXTS } }, - /* 259 - DOA */ - { TYPE_DOA, "DOA", 5, 5, - { RDATA_WF_LONG, RDATA_WF_LONG, RDATA_WF_BYTE, - RDATA_WF_TEXT, RDATA_WF_BINARY }, - { RDATA_ZF_LONG, RDATA_ZF_LONG, RDATA_ZF_BYTE, - RDATA_ZF_TEXT, RDATA_ZF_BASE64 } }, - /* 260 - AMTRELAY */ - { TYPE_AMTRELAY, "AMTRELAY", 3, 3, - { RDATA_WF_BYTE, RDATA_WF_BYTE , RDATA_WF_AMTRELAY_RELAY }, - { RDATA_ZF_BYTE, RDATA_ZF_AMTRELAY_D_TYPE, RDATA_ZF_AMTRELAY_RELAY } }, - /* 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 } }, - /* 264 - IPN */ - { TYPE_IPN, "IPN", 1, 1, { RDATA_WF_LONGLONG }, { RDATA_ZF_LONGLONG } }, - - /* 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", 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 } }, -}; + TYPE("EUI64", TYPE_EUI64, TYPE_HAS_NO_REFS, + read_eui64_rdata, write_generic_rdata, + print_eui64_rdata, eui64_rdata_fields), -rrtype_descriptor_type * -rrtype_descriptor_by_type(uint16_t type) -{ - if (type < RRTYPE_DESCRIPTORS_LENGTH) - 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]; -} + 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), -rrtype_descriptor_type * -rrtype_descriptor_by_name(const char *name) -{ - int i; + /* 128 */ + /* The mnemonic is included so it can be printed in type bitmaps.*/ + TYPE("NXNAME", TYPE_NXNAME, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, + print_generic_rdata, generic_rdata_fields), - for (i = 0; i < RRTYPE_DESCRIPTORS_LENGTH; ++i) { - if (rrtype_descriptors[i].name - && strcasecmp(rrtype_descriptors[i].name, name) == 0) - { - return &rrtype_descriptors[i]; - } - } + 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), /* Type 249 - TKEY [RFC 2930] */ + UNKNOWN_TYPE(250), /* Type 250 - TSIG */ + UNKNOWN_TYPE(251), /* Type 251 - IXFR */ + UNKNOWN_TYPE(252), /* Type 252 - AXFR */ + UNKNOWN_TYPE(253), /* Type 253 - MAILB */ + UNKNOWN_TYPE(254), /* Type 254 - MAILA */ + UNKNOWN_TYPE(255), /* Type 255 - ANY */ - if (rrtype_descriptors[PSEUDO_TYPE_DLV].name - && strcasecmp(rrtype_descriptors[PSEUDO_TYPE_DLV].name, name) == 0) - { - return &rrtype_descriptors[PSEUDO_TYPE_DLV]; - } + /* 256 */ + TYPE("URI", TYPE_URI, TYPE_HAS_NO_REFS, + read_uri_rdata, write_generic_rdata, + print_uri_rdata, uri_rdata_fields), + /* 257 */ + TYPE("CAA", TYPE_CAA, TYPE_HAS_NO_REFS, + read_caa_rdata, write_generic_rdata, + print_caa_rdata, caa_rdata_fields), + /* 258 */ + TYPE("AVC", TYPE_AVC, TYPE_HAS_NO_REFS, + read_txt_rdata, write_generic_rdata, + print_txt_rdata, avc_rdata_fields), + /* 259 */ + TYPE("DOA", TYPE_DOA, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, + print_doa_rdata, doa_rdata_fields), + /* 260 */ + TYPE("AMTRELAY", TYPE_AMTRELAY, TYPE_HAS_LITERAL_DNAME, + read_amtrelay_rdata, write_generic_rdata, + print_amtrelay_rdata, amtrelay_rdata_fields), + /* 261 */ + TYPE("RESINFO", TYPE_RESINFO, TYPE_HAS_NO_REFS, + read_txt_rdata, write_generic_rdata, + print_resinfo_rdata, resinfo_rdata_fields), + /* 262 */ + TYPE("WALLET", TYPE_WALLET, TYPE_HAS_NO_REFS, + read_txt_rdata, write_generic_rdata, + print_txt_rdata, wallet_rdata_fields), + /* 263 */ + TYPE("CLA", TYPE_CLA, TYPE_HAS_NO_REFS, + read_txt_rdata, write_generic_rdata, + print_txt_rdata, cla_rdata_fields), + /* 264 */ + TYPE("IPN", TYPE_IPN, TYPE_HAS_NO_REFS, + read_generic_rdata, write_generic_rdata, + print_ipn_rdata, ipn_rdata_fields), - return NULL; -} + /* 32768 */ + TYPE("TA", TYPE_TA, TYPE_HAS_NO_REFS, + read_dlv_rdata, write_generic_rdata, + print_dlv_rdata, ta_rdata_fields), + /* 32769 */ + TYPE("DLV", TYPE_DLV, TYPE_HAS_NO_REFS, + read_dlv_rdata, write_generic_rdata, + print_dlv_rdata, dlv_rdata_fields) +}; + +#undef UNKNOWN_TYPE +#undef TYPE +#undef FIELD +#undef FIELD_ENTRY +#undef TYPE_HAS_NO_REFS +#undef TYPE_HAS_COMPRESSED_DNAME +#undef TYPE_HAS_UNCOMPRESSED_DNAME +#undef TYPE_HAS_LITERAL_DNAME +#undef TYPE_HAS_FLAGS const char * rrtype_to_string(uint16_t rrtype) { static char buf[20]; - rrtype_descriptor_type *descriptor = rrtype_descriptor_by_type(rrtype); + const nsd_type_descriptor_type *descriptor = + nsd_type_descriptor(rrtype); if (descriptor->name) { return descriptor->name; } else { @@ -980,9 +1096,8 @@ rrtype_to_string(uint16_t rrtype) uint16_t rrtype_from_string(const char *name) { - char *end; - long rrtype; - rrtype_descriptor_type *entry; + char *end; + long rrtype; /* Because this routine is called during zone parse for every record, * we optimise for frequently occurring records. @@ -1063,9 +1178,9 @@ rrtype_from_string(const char *name) break; } - entry = rrtype_descriptor_by_name(name); - if (entry) { - return entry->type; + for (int i=0, n=sizeof(type_descriptors)/sizeof(type_descriptors[0]); i < n; i++) { + if (type_descriptors[i].name && strcasecmp(type_descriptors[i].name, name) == 0) + return type_descriptors[i].type; } if (strlen(name) < 5) @@ -1084,7 +1199,7 @@ rrtype_from_string(const char *name) if (rrtype < 0 || rrtype > 65535L) return 0; - return (uint16_t) rrtype; + return (uint16_t) rrtype; } const char * Index: usr.sbin/nsd/dns.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/dns.h,v diff -u -p -r1.22 dns.h --- usr.sbin/nsd/dns.h 6 Sep 2025 17:41:37 -0000 1.22 +++ usr.sbin/nsd/dns.h 21 Sep 2026 16:29:07 -0000 @@ -9,6 +9,11 @@ #ifndef DNS_H #define DNS_H +struct rr; +struct buffer; +struct domain; +struct domain_table; +struct query; enum rr_section { QUESTION_SECTION, @@ -138,7 +143,7 @@ typedef enum nsd_rc nsd_rc_type; #define TYPE_HIP 55 /* RFC 8005 */ #define TYPE_NINFO 56 /* NINFO/ninfo-completed-template */ #define TYPE_RKEY 57 /* RKEY/rkey-completed-template */ -#define TYPE_TALINK 58 /* draft-iet5f-dnsop-dnssec-trust-history */ +#define TYPE_TALINK 58 /* draft-ietf-dnsop-dnssec-trust-history */ #define TYPE_CDS 59 /* RFC 7344 */ #define TYPE_CDNSKEY 60 /* RFC 7344 */ #define TYPE_OPENPGPKEY 61 /* RFC 7929 */ @@ -146,7 +151,9 @@ typedef enum nsd_rc nsd_rc_type; #define TYPE_ZONEMD 63 /* RFC 8976 */ #define TYPE_SVCB 64 /* RFC 9460 */ #define TYPE_HTTPS 65 /* RFC 9460 */ -#define TYPE_DSYNC 66 /* draft-ietf-dnsop-generalized-notify */ +#define TYPE_DSYNC 66 /* RFC 9859 */ +#define TYPE_HHIT 67 /* RFC 9886 */ +#define TYPE_BRID 68 /* RFC 9886 */ #define TYPE_SPF 99 /* RFC 4408 */ @@ -157,12 +164,14 @@ typedef enum nsd_rc nsd_rc_type; #define TYPE_EUI48 108 /* RFC 7043 */ #define TYPE_EUI64 109 /* RFC 7043 */ -#define TYPE_TSIG 250 -#define TYPE_IXFR 251 -#define TYPE_AXFR 252 -#define TYPE_MAILB 253 /* A request for mailbox-related records (MB, MG or MR) */ -#define TYPE_MAILA 254 /* A request for mail agent RRs (Obsolete - see MX) */ -#define TYPE_ANY 255 /* any type (wildcard) */ +#define TYPE_NXNAME 128 /* RFC 9824 */ + +#define TYPE_TSIG 250 /* RFC 2845 */ +#define TYPE_IXFR 251 /* RFC 1995 */ +#define TYPE_AXFR 252 /* RFC 1035, RFC 5936 */ +#define TYPE_MAILB 253 /* A request for mailbox-related records (MB, MG or MR) [RFC 1035] */ +#define TYPE_MAILA 254 /* A request for mail agent RRs (Obsolete - see MX) [RFC 1035] */ +#define TYPE_ANY 255 /* any type (wildcard) [RFC 1035, RFC 6895] */ #define TYPE_URI 256 /* RFC 7553 */ #define TYPE_CAA 257 /* RFC 6844 */ #define TYPE_AVC 258 /* AVC/avc-completed-template */ @@ -171,12 +180,10 @@ typedef enum nsd_rc nsd_rc_type; #define TYPE_RESINFO 261 /* RFC 9606 */ #define TYPE_WALLET 262 /* WALLET/wallet-completed-template */ #define TYPE_CLA 263 /* CLA/cla-completed-template */ -#define TYPE_IPN 264 /* IPN/ipn-completed-template */ +#define TYPE_IPN 264 /* IPN/ipn-completed-template draft-johnson-dns-ipn-cla-07 */ #define TYPE_TA 32768 /* http://www.watson.org/~weiler/INI1999-19.pdf */ #define TYPE_DLV 32769 /* RFC 4431 */ -#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 @@ -188,12 +195,13 @@ typedef enum nsd_rc nsd_rc_type; #define SVCB_KEY_DOHPATH 7 #define SVCB_KEY_OHTTP 8 #define SVCB_KEY_TLS_SUPPORTED_GROUPS 9 -#define SVCPARAMKEY_COUNT 10 +#define SVCB_KEY_DOCPATH 10 +#define SVCB_KEY_PVD 11 +#define SVCB_KEY_OOTS 12 #define MAXLABELLEN 63 #define MAXDOMAINLEN 255 -#define MAXRDATALEN 64 /* This is more than enough, think multiple TXT. */ #define MAX_RDLENGTH 65535 /* Maximum size of a single RR. */ @@ -206,104 +214,206 @@ typedef enum nsd_rc nsd_rc_type; #define EUI64ADDRLEN (64/8) #define NSEC3_HASH_LEN 20 +#define NSEC3_OWNER_LABEL_LEN (NSEC3_HASH_LEN * 8 / 5) /* - * The different types of RDATA wireformat data. + * The following RDATA values are used in nsd_rdata_descriptor.length to + * indicate a specialized value. They are negative, the normal lengths + * are 0..65535 and length is int32_t. */ -enum rdata_wireformat -{ - RDATA_WF_COMPRESSED_DNAME, /* Possibly compressed domain name. */ - RDATA_WF_UNCOMPRESSED_DNAME, /* Uncompressed domain name. */ - RDATA_WF_LITERAL_DNAME, /* Literal (not downcased) dname. */ - RDATA_WF_BYTE, /* 8-bit integer. */ - RDATA_WF_SHORT, /* 16-bit integer. */ - RDATA_WF_LONG, /* 32-bit integer. */ - RDATA_WF_LONGLONG, /* 64-bit integer. */ - RDATA_WF_TEXT, /* Text string. */ - RDATA_WF_TEXTS, /* Text string sequence. */ - RDATA_WF_A, /* 32-bit IPv4 address. */ - RDATA_WF_AAAA, /* 128-bit IPv6 address. */ - RDATA_WF_BINARY, /* Binary data (unknown length). */ - RDATA_WF_BINARYWITHLENGTH, /* Binary data preceded by 1 byte length */ - 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_EUI64, /* 64-bit address. */ - RDATA_WF_LONG_TEXT, /* Long (>255) text string. */ - RDATA_WF_SVCPARAM, /* SvcParam [=] */ - RDATA_WF_HIP, /* HIP rdata up to the Rendezvous Servers */ - RDATA_WF_AMTRELAY_RELAY /* ip4, ip6, dname or nothing */ -}; -typedef enum rdata_wireformat rdata_wireformat_type; +/* The rdata is a compressed domain name. In namedb it is a reference + * with a pointer to struct domain. On the wire, the name can be a + * compressed name. The pointer is stored in line and is likely unaligned. */ +#define RDATA_COMPRESSED_DNAME -1 +/* The rdata is an uncompressed domain name. In namedb it is a reference + * with a pointer to struct domain. The pointer is stored in line and is + * likely unaligned. */ +#define RDATA_UNCOMPRESSED_DNAME -2 +/* The rdata is a literal domain name. It is not a reference to struct + * domain, and stored as uncompressed wireformat octets. */ +#define RDATA_LITERAL_DNAME -3 +/* The rdata is a string. It starts with a uint8_t length byte. */ +#define RDATA_STRING -4 +/* The rdata is binary. It starts with a uint8_t length byte. */ +#define RDATA_BINARY -5 +/* The rdata is of type IPSECGATEWAY because of its encoding elsewhere in + * the RR. */ +#define RDATA_IPSECGATEWAY -6 +/* The rdata is the remainder of the record, to the end of the bytes, possibly + * zero bytes. The length of the field is determined by the rdata length. */ +#define RDATA_REMAINDER -7 +/* The rdata is of type AMTRELAYRELAY because of its encoding elsewhere in + * the RR. */ +#define RDATA_AMTRELAY_RELAY -8 /* - * The different types of RDATA that can appear in the zone file. + * Function signature to determine length of the rdata field. + * @param rdlength: length of the input rdata. + * @param rdata: input bytes with rdata of the RR. + * @param offset: current byte position in rdata. + * offset is required for the ipsecgateway where we need to read + * a couple bytes back + * @param domain: this value can be returned as NULL, in which case the + * function return value is a length in bytes in wireformat. + * If this value is returned nonNULL, it is the special reference + * object that needs different treatment. The function return value + * is the length that needs to be skipped in rdata to get past the + * field, that is a reference when that is a pointer. + * For other types of objects an additional function argument could be + * added, and then handling in the caller. + * @return length in bytes. Or -1 on failure, like rdata length too short. */ -enum rdata_zoneformat -{ - RDATA_ZF_DNAME, /* Domain name. */ - RDATA_ZF_LITERAL_DNAME, /* DNS name (not lowercased domain name). */ - RDATA_ZF_TEXT, /* Text string. */ - RDATA_ZF_TEXTS, /* Text string sequence. */ - RDATA_ZF_BYTE, /* 8-bit integer. */ - RDATA_ZF_SHORT, /* 16-bit integer. */ - RDATA_ZF_LONG, /* 32-bit integer. */ - RDATA_ZF_LONGLONG, /* 64-bit integer. */ - RDATA_ZF_A, /* 32-bit IPv4 address. */ - RDATA_ZF_AAAA, /* 128-bit IPv6 address. */ - RDATA_ZF_RRTYPE, /* RR type. */ - RDATA_ZF_ALGORITHM, /* Cryptographic algorithm. */ - RDATA_ZF_CERTIFICATE_TYPE, - RDATA_ZF_PERIOD, /* Time period. */ - RDATA_ZF_TIME, - RDATA_ZF_BASE64, /* Base-64 binary data. */ - RDATA_ZF_BASE32, /* Base-32 binary data. */ - RDATA_ZF_HEX, /* Hexadecimal binary data. */ - RDATA_ZF_HEX_LEN, /* Hexadecimal binary data. Skip initial length byte. */ - RDATA_ZF_NSAP, /* NSAP. */ - RDATA_ZF_APL, /* APL. */ - RDATA_ZF_IPSECGATEWAY, /* IPSECKEY gateway ip4, ip6 or dname. */ - RDATA_ZF_SERVICES, /* Protocol and port number bitmap. */ - RDATA_ZF_NXT, /* NXT type bitmap. */ - RDATA_ZF_NSEC, /* NSEC type bitmap. */ - RDATA_ZF_LOC, /* Location data. */ - RDATA_ZF_ILNP64, /* 64-bit uncompressed IPv6 address. */ - RDATA_ZF_EUI48, /* EUI48 address. */ - RDATA_ZF_EUI64, /* EUI64 address. */ - RDATA_ZF_LONG_TEXT, /* Long (>255) text string. */ - 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 [=] */ - RDATA_ZF_HIP, /* HIP rdata up to the Rendezvous Servers */ - RDATA_ZF_ATMA, /* ATM Address */ - RDATA_ZF_AMTRELAY_D_TYPE,/* Discovery Optional and Type */ - RDATA_ZF_AMTRELAY_RELAY,/* ip4, ip6, dname or nothing */ - RDATA_ZF_UNKNOWN /* Unknown data. */ +typedef int32_t(*nsd_rdata_field_length_type)( + uint16_t rdlength, + const uint8_t *rdata, + uint16_t offset, + struct domain** domain); + +typedef struct nsd_rdata_descriptor nsd_rdata_descriptor_type; + +/* + * Descriptor table. For DNS RRTypes has information on the resource record + * type. the descriptor table shouldn't be used for validation. + * the read function will take care of that. it's used for + * implementing copy functions that are very specific, but where + * the data has already been checked for validity. + */ +struct nsd_rdata_descriptor { + /* Field name of the rdata, like 'primary server'. */ + const char *name; + + /* If the field is optional. When the field is not present, eg. no + * bytes of rdata, and it is optional, this is fine and the rdata + * is shorter. Also no following rdatas after it. Non optional + * rdatas must be present. */ + int is_optional; + + /* The length, in bytes, of the rdata field. Can be set to + * a specialized value, like RDATA_COMPRESSED_DNAME, + * RDATA_STRING, ..., if there is a length function, that is used. + * That is for any type where the length depends on a value in + * the rdata itself. */ + int32_t length; + + /* Determine size of rdata field. Returns the size of uncompressed + * rdata on the wire, or -1 on failure, like when it is malformed. + * So for references this is a different number. Used for ipseckey + * gateway, because the type depends on earlier data. Also amtrelay + * relay. This function takes the in-memory rdata representation. + * If the field has a special object, return -1 on failure or the + * length of the object in the rdata, with domain ptr returned to + * the special object. */ + nsd_rdata_field_length_type calculate_length; + + /* Determine size of rdata field. Like calculate_length, but this + * function takes uncompressed wireformat in the rdata that is passed. + */ + nsd_rdata_field_length_type calculate_length_uncompressed_wire; }; -typedef enum rdata_zoneformat rdata_zoneformat_type; -struct rrtype_descriptor -{ - uint16_t type; /* RR type */ - const char *name; /* Textual name. */ - uint32_t minimum; /* Minimum number of RDATAs. */ - uint32_t maximum; /* Maximum number of RDATAs. */ - uint8_t wireformat[MAXRDATALEN]; /* rdata_wireformat_type */ - uint8_t zoneformat[MAXRDATALEN]; /* rdata_zoneformat_type */ +typedef struct nsd_type_descriptor nsd_type_descriptor_type; +struct nsd_type_descriptor; + +/* The rdata is malformed. The wireformat is not correct. + * NSD cannot store a wireformat with a malformed domain name, when that + * name needs to become a reference to struct domain. */ +#define MALFORMED -1 +/* The rdata is not read, it is truncated, some was copied, but then + * an error occurred, like memory allocation failure. */ +#define TRUNCATED -2 + +/* + * Function signature to read rdata. From a packet into memory. + * @param domains: the domain table. + * @param rdlength: the length of the rdata in the packet. + * @param packet: the packet. + * @param rr: an RR is returned. + * @return the number of bytes that are read from the packet. Or negative + * for an error, like MALFORMED, TRUNCATED. + */ +typedef int32_t(*nsd_read_rdata_type)( + struct domain_table *domains, + uint16_t rdlength, + struct buffer *packet, + struct rr **rr); + +/* + * Function signature to write rdata. From memory to an answer. + * @param query: the query that is answered. + * @param rr: rr to add to the output, in query.packet. It prints the rdata + * wireformat to the packet, compressed if needed, not including the + * rdlength before the rdata. + */ +typedef void(*nsd_write_rdata_type)( + struct query *query, + const struct rr *rr); + +/* + * Function signature to print rdata. The string is in the buffer. + * The printed string starts with the rdata text. It does not have a newline + * at end. No space is put before it. + * @param output: buffer with string on return. The position is moved. + * @param rr: the record to print the rdata for. + * @return false on failure. The wireformat can not be printed in the + * nice output format. + */ +typedef int(*nsd_print_rdata_type)( + struct buffer *output, + const struct rr *rr); + +/* + * Descriptor for a DNS resource record type. + * There are conversion routines per type, for wire-to-internal, + * internal-to-wire, and internal-to-text. To stop an explosion in code, + * there is an rdata field descriptor array to convert between more formats. + * For internal to compressed wire format we implement a specialized routine so + * that answering of queries is as optimal as can be. + * Other formats are conversion from uncompressed wire format to compressed + * wire format. For conversion from uncompressed or compressed wire format + * to internal the same import routines can be used, by using a packet buffer + * and dname_make_from_packet. + */ +struct nsd_type_descriptor { + /* The RRType number */ + uint16_t type; + /* Mnemonic. */ + const char *name; + /* Whether internal RDATA contains direct pointers. + * This means the namedb memory contains an in line pointer to + * struct domain for domain names. */ + int has_references; + /* Whether RDATA contains compressible names. The output can be + * compressed on the packet. If true, also has_references is true, + * for the packet encode routine, that uses the references. */ + int is_compressible; + /* The type has domain names, like literal dnames in the format. */ + int has_dnames; + /* Read function that copies rdata for this type to struct rr. */ + nsd_read_rdata_type read_rdata; + /* Write function, that copies rdata from struct rr to packet. */ + nsd_write_rdata_type write_rdata; + /* Print function, that writes the struct rr to string. */ + nsd_print_rdata_type print_rdata; + /* Description of the rdata fields. Used by functions that need + * to iterate over the fields. There are binary fields and + * references, and wireformat domain names. */ + struct { + /* Length of the fields array. */ + size_t length; + /* The rdata field descriptors. */ + const nsd_rdata_descriptor_type *fields; + } rdata; }; -typedef struct rrtype_descriptor rrtype_descriptor_type; + +/* The length of the RRTYPE descriptors arrary */ +#define RRTYPE_DESCRIPTORS_LENGTH (TYPE_IPN + 2) /* * Indexed by type. The special type "0" can be used to get a * descriptor for unknown types (with one binary rdata). - * - * CLA + 1 */ -#define RRTYPE_DESCRIPTORS_LENGTH (TYPE_IPN + 1) -rrtype_descriptor_type *rrtype_descriptor_by_name(const char *name); -rrtype_descriptor_type *rrtype_descriptor_by_type(uint16_t type); +static inline const nsd_type_descriptor_type *nsd_type_descriptor( + uint16_t rrtype); const char *rrtype_to_string(uint16_t rrtype); @@ -317,5 +427,20 @@ uint16_t rrtype_from_string(const char * const char *rrclass_to_string(uint16_t rrclass); uint16_t rrclass_from_string(const char *name); + +/* The type descriptors array of length RRTYPE_DESCRIPTORS_LENGTH. */ +extern const nsd_type_descriptor_type type_descriptors[]; + +static inline const nsd_type_descriptor_type * +nsd_type_descriptor(uint16_t rrtype) +{ + if (rrtype <= TYPE_IPN) + return &type_descriptors[rrtype]; + if (rrtype == TYPE_TA) + return &type_descriptors[TYPE_IPN + 1]; + if (rrtype == TYPE_DLV) + return &type_descriptors[TYPE_IPN + 2]; + return &type_descriptors[0]; +} #endif /* DNS_H */ Index: usr.sbin/nsd/edns.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/edns.c,v diff -u -p -r1.9 edns.c --- usr.sbin/nsd/edns.c 3 Sep 2025 18:46:48 -0000 1.9 +++ usr.sbin/nsd/edns.c 21 Sep 2026 16:29:07 -0000 @@ -20,6 +20,7 @@ #include "edns.h" #include "nsd.h" #include "query.h" +#include "options.h" #if !defined(HAVE_SSL) || !defined(HAVE_CRYPTO_MEMCMP) /* we need fixed time compare, pull it in from tsig.c */ @@ -71,6 +72,8 @@ edns_init_record(edns_record_type *edns) edns->dnssec_ok = 0; edns->nsid = 0; edns->zoneversion = 0; + edns->padding = 0; + edns->cookie_seen = 0; edns->cookie_status = COOKIE_NOT_PRESENT; edns->cookie_len = 0; edns->ede = -1; /* -1 means no Extended DNS Error */ @@ -88,7 +91,7 @@ edns_handle_option(uint16_t optcode, uin switch(optcode) { case NSID_CODE: /* is NSID enabled? */ - if(nsd->nsid_len > 0) { + if(nsd->nsid_len > 0 && !edns->nsid) { edns->nsid = 1; /* we have to check optlen, and move the buffer along */ buffer_skip(packet, optlen); @@ -101,7 +104,8 @@ edns_handle_option(uint16_t optcode, uin break; case COOKIE_CODE: /* Cookies enabled? */ - if(nsd->do_answer_cookie) { + if(nsd->do_answer_cookie && !edns->cookie_seen) { + edns->cookie_seen = 1; if (optlen == 8) edns->cookie_status = COOKIE_INVALID; else if (optlen < 16 || optlen > 40) @@ -117,6 +121,11 @@ edns_handle_option(uint16_t optcode, uin buffer_skip(packet, optlen); } break; + case PADDING_CODE: + if(query->tcp || query->may_pad) + edns->padding = 1; + buffer_skip(packet, optlen); + break; case ZONEVERSION_CODE: edns->zoneversion = 1; if(optlen > 0) @@ -257,11 +266,8 @@ void cookie_verify(query_type *q, struct q->edns.cookie_status = COOKIE_INVALID; - cookie_time = (q->edns.cookie[12] << 24) - | (q->edns.cookie[13] << 16) - | (q->edns.cookie[14] << 8) - | q->edns.cookie[15]; - + cookie_time = read_uint32(q->edns.cookie + 12); + now_uint32 = *now_p ? *now_p : (*now_p = (uint32_t)time(NULL)); if(compare_1982(now_uint32, cookie_time) > 0) { Index: usr.sbin/nsd/edns.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/edns.h,v diff -u -p -r1.7 edns.h --- usr.sbin/nsd/edns.h 6 Sep 2025 17:41:37 -0000 1.7 +++ usr.sbin/nsd/edns.h 21 Sep 2026 16:29:07 -0000 @@ -19,6 +19,7 @@ struct query; #define OPT_HDR 4U /* NSID opt header length */ #define NSID_CODE 3 /* nsid option code */ #define COOKIE_CODE 10 /* COOKIE option code */ +#define PADDING_CODE 12 /* Padding 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 */ @@ -26,6 +27,9 @@ struct query; /* https://iana.org/assignments/dns-parameters/#zoneversion-type-values */ #define ZONEVERSION_SOA_SERIAL 0 +/* See RFC 8467 */ +#define PADDING_BLOCK_SZ 468 + struct edns_data { unsigned char ok[OPT_LEN]; @@ -64,6 +68,8 @@ struct edns_record int dnssec_ok; int nsid; int zoneversion; + int padding; + int cookie_seen; cookie_status_type cookie_status; size_t cookie_len; uint8_t cookie[40]; Index: usr.sbin/nsd/install-sh =================================================================== RCS file: /cvs/src/usr.sbin/nsd/install-sh,v diff -u -p -r1.3 install-sh --- usr.sbin/nsd/install-sh 6 Sep 2025 17:41:37 -0000 1.3 +++ usr.sbin/nsd/install-sh 21 Sep 2026 16:29:07 -0000 @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2023-11-23.18; # UTC +scriptversion=2020-11-14.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -124,9 +124,9 @@ it's up to you to specify -f if you want If -S is not specified, no backups are attempted. -Report bugs to . -GNU Automake home page: . -General help using GNU software: ." +Email bug reports to bug-automake@gnu.org. +Automake home page: https://www.gnu.org/software/automake/ +" while test $# -ne 0; do case $1 in Index: usr.sbin/nsd/ipc.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/ipc.c,v diff -u -p -r1.14 ipc.c --- usr.sbin/nsd/ipc.c 6 Sep 2025 17:41:37 -0000 1.14 +++ usr.sbin/nsd/ipc.c 21 Sep 2026 16:29:07 -0000 @@ -574,10 +574,19 @@ xfrd_handle_ipc_read(struct event* handl if(!xfrd->reload_failed) { xfrd_check_failed_updates(); xfrd->reload_cmd_first_sent = 0; + xfrd->num_reload_failed_repeat = 0; + xfrd->num_xfrs_in_reload = 0; } else { + if(xfrd->num_reload_failed_repeat++ > + xfrd->num_xfrs_in_reload) { + /* More fails than zones, it may be that + * reload crashes, delete failed updates. */ + xfrd_check_failed_updates(); + } /* make reload happen again, right away */ xfrd_set_reload_now(xfrd); } + xfrd->num_xfrs_in_reload = 0; xfrd_prepare_zones_for_reload(); xfrd->reload_failed = 0; break; Index: usr.sbin/nsd/ixfr.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/ixfr.c,v diff -u -p -r1.5 ixfr.c --- usr.sbin/nsd/ixfr.c 6 Sep 2025 17:41:37 -0000 1.5 +++ usr.sbin/nsd/ixfr.c 21 Sep 2026 16:29:07 -0000 @@ -185,34 +185,6 @@ static void pktcompression_insert_with_l } } -/* calculate length of dname in uncompressed wireformat in buffer */ -static size_t dname_length(const uint8_t* buf, size_t len) -{ - size_t l = 0; - if(!buf || len == 0) - return l; - while(len > 0 && buf[0] != 0) { - size_t lablen = (size_t)(buf[0]); - if( (lablen&0xc0) ) - return 0; /* the name should be uncompressed */ - if(lablen+1 > len) - return 0; /* should fit in the buffer */ - l += lablen+1; - len -= lablen+1; - buf += lablen+1; - if(l > MAXDOMAINLEN) - return 0; - } - if(len == 0) - return 0; /* end label should fit in buffer */ - if(buf[0] != 0) - return 0; /* must end in root label */ - l += 1; /* for the end root label */ - if(l > MAXDOMAINLEN) - return 0; - return l; -} - /* write a compressed domain name into the packet, * returns uncompressed wireformat length, * 0 if it does not fit and -1 on failure, bad dname. */ @@ -220,7 +192,7 @@ static int pktcompression_write_dname(st struct pktcompression* pcomp, const uint8_t* rr, size_t rrlen) { size_t wirelen = 0; - size_t dname_len = dname_length(rr, rrlen); + size_t dname_len = buf_dname_length(rr, rrlen); if(!rr || rrlen == 0 || dname_len == 0) return 0; while(rrlen > 0 && rr[0] != 0) { @@ -263,6 +235,118 @@ static int pktcompression_write_dname(st return wirelen; } +static int ixfr_write_rdata_pkt(struct buffer* packet, uint16_t tp, + struct pktcompression* pcomp, const uint8_t* rr, size_t rdlen) +{ + const struct nsd_type_descriptor* descriptor = nsd_type_descriptor(tp); + size_t i; + uint16_t offset; /* The offset in rr. */ + + /* The rr points at the start of the rdata of length rdlen. + * This is uncompressed wireformat. */ + + if(!descriptor->is_compressible) { + if(!buffer_available(packet, rdlen)) + return 0; + buffer_write(packet, rr, rdlen); + return 1; + } + + /* It is compressible, loop over the fields and write compressed + * domain names, when the rdata has a compressible name. */ + offset = 0; + for(i=0; i < descriptor->rdata.length; i++) { + const nsd_rdata_descriptor_type* field = + &descriptor->rdata.fields[i]; + uint16_t field_len = 0; + int already_written = 0; + if(rdlen == offset && field->is_optional) + break; /* There are no more rdata fields. */ + if(field->calculate_length_uncompressed_wire) { + /* Call field length function. */ + /* This is called with an uncompressed wireformat + * data buffer, instead of the in-memory data buffer. + * For IPSECKEY it does not matter, since it has + * a literal dname storage. */ + struct domain* domain; + int32_t l = field->calculate_length_uncompressed_wire( + rdlen, rr, offset, &domain); + if(l < 0) + return 1; /* attempt to skip malformed rr */ + field_len = l; + if(domain) { + /* Treat as uncompressed dname, to be safe. */ + /* Write as an uncompressed name. */ + if(!buffer_available(packet, + domain_dname(domain)->name_size)) + return 0; + buffer_write(packet, + dname_name(domain_dname(domain)), + domain_dname(domain)->name_size); + already_written = 1; + } + } else if(field->length >= 0) { + field_len = field->length; + } else { + size_t dlen; + int dname_len; + switch(field->length) { + /* The dnames are stored in uncompressed + * wireformat in the uncompressed wireformat + * string. */ + case RDATA_COMPRESSED_DNAME: + /* Attempt to compress the compressible + * name. */ + dname_len = pktcompression_write_dname(packet, + pcomp, rr+offset, rdlen-offset); + if(dname_len == -1) + return 1; /* attempt to skip malformed rr */ + if(dname_len == 0) + return 0; + field_len = dname_len; + already_written = 1; + break; + case RDATA_UNCOMPRESSED_DNAME: + case RDATA_LITERAL_DNAME: + /* Write as an uncompressed name. */ + if(rdlen-offset<1) + return 1; /* attempt to skip malformed rr */ + dlen = buf_dname_length(rr+offset, + rdlen-offset); + if(dlen == 0) + return 1; /* attempt to skip malformed rr */ + field_len = dlen; + break; + case RDATA_STRING: + case RDATA_BINARY: + if(rdlen-offset<1) + return 1; /* attempt to skip malformed rr */ + field_len = ((uint16_t)(rr+offset)[0]) + 1; + break; + case RDATA_IPSECGATEWAY: + case RDATA_AMTRELAY_RELAY: + /* This should have called the callback. */ + return 1; /* attempt to skip malformed rr */ + case RDATA_REMAINDER: + field_len = rdlen - offset; + break; + default: + /* Unknown specialized value. */ + return 1; /* attempt to skip malformed rr */ + } + } + if((size_t)offset+field_len > rdlen) + return 1; /* attempt to skip malformed rr */ + if(!already_written) { + if(!buffer_available(packet, field_len)) + return 0; + buffer_write(packet, rr+offset, field_len); + } + offset += field_len; + } + return 1; +} + /* write an RR into the packet with compression for domain names, * return 0 and resets position if it does not fit in the packet. */ static int ixfr_write_rr_pkt(struct query* query, struct buffer* packet, @@ -274,8 +358,6 @@ static int ixfr_write_rr_pkt(struct quer uint16_t tp; int dname_len; size_t rdlen; - size_t i; - rrtype_descriptor_type* descriptor; if(total_added == 0) { size_t oldmaxlen = query->maxlen; @@ -325,100 +407,11 @@ static int ixfr_write_rr_pkt(struct quer return 1; /* attempt to skip this malformed rr, could assert */ /* rdata */ - descriptor = rrtype_descriptor_by_type(tp); - for(i=0; imaximum; i++) { - size_t copy_len = 0; - if(rdlen == 0) - break; - - switch(rdata_atom_wireformat_type(tp, i)) { - case RDATA_WF_COMPRESSED_DNAME: - dname_len = pktcompression_write_dname(packet, pcomp, - rr, rdlen); - if(dname_len == -1) - return 1; /* attempt to skip malformed rr */ - if(dname_len == 0) { - buffer_set_position(packet, oldpos); - return 0; - } - rr += dname_len; - rdlen -= dname_len; - break; - case RDATA_WF_UNCOMPRESSED_DNAME: - case RDATA_WF_LITERAL_DNAME: - copy_len = rdlen; - break; - case RDATA_WF_BYTE: - copy_len = 1; - break; - case RDATA_WF_SHORT: - copy_len = 2; - break; - case RDATA_WF_LONG: - copy_len = 4; - break; - case RDATA_WF_LONGLONG: - copy_len = 8; - break; - case RDATA_WF_TEXTS: - case RDATA_WF_LONG_TEXT: - copy_len = rdlen; - break; - case RDATA_WF_TEXT: - case RDATA_WF_BINARYWITHLENGTH: - copy_len = 1; - if(rdlen > copy_len) - copy_len += rr[0]; - break; - case RDATA_WF_A: - copy_len = 4; - break; - case RDATA_WF_AAAA: - copy_len = 16; - break; - case RDATA_WF_ILNP64: - copy_len = 8; - break; - case RDATA_WF_EUI48: - copy_len = EUI48ADDRLEN; - break; - case RDATA_WF_EUI64: - copy_len = EUI64ADDRLEN; - break; - case RDATA_WF_BINARY: - copy_len = rdlen; - break; - case RDATA_WF_APL: - copy_len = (sizeof(uint16_t) /* address family */ - + sizeof(uint8_t) /* prefix */ - + sizeof(uint8_t)); /* length */ - if(copy_len <= rdlen) - copy_len += (rr[copy_len-1]&APL_LENGTH_MASK); - break; - case RDATA_WF_IPSECGATEWAY: - copy_len = rdlen; - break; - case RDATA_WF_SVCPARAM: - copy_len = 4; - if(copy_len <= rdlen) - copy_len += read_uint16(rr+2); - break; - default: - copy_len = rdlen; - break; - } - if(copy_len) { - if(!buffer_available(packet, copy_len)) { - buffer_set_position(packet, oldpos); - return 0; - } - if(copy_len > rdlen) - return 1; /* assert of skip malformed */ - buffer_write(packet, rr, copy_len); - rr += copy_len; - rdlen -= copy_len; - } + if(!ixfr_write_rdata_pkt(packet, tp, pcomp, rr, rdlen)) { + buffer_set_position(packet, oldpos); + return 0; } + /* write compressed rdata length */ buffer_write_u16_at(packet, rdpos, buffer_position(packet)-rdpos-2); if(total_added == 0) { @@ -446,6 +439,7 @@ static int parse_qserial(struct buffer* { unsigned int i; uint16_t type, rdlen; + size_t rdpos; /* we must have a SOA in the authority section */ if(NSCOUNT(packet) == 0) return 0; @@ -467,6 +461,7 @@ static int parse_qserial(struct buffer* type = buffer_read_u16(packet); buffer_skip(packet, 6); rdlen = buffer_read_u16(packet); + rdpos = buffer_position(packet); if(!buffer_available(packet, rdlen)) return 0; if(type == TYPE_SOA) { @@ -478,6 +473,9 @@ static int parse_qserial(struct buffer* return 0; /* malformed rname */ if(!buffer_available(packet, 4)) return 0; + if(buffer_position(packet) + 20 != + rdpos + (size_t)rdlen) + return 0; /* malformed SOA rdata */ *qserial = buffer_read_u32(packet); return 1; } @@ -486,14 +484,18 @@ static int parse_qserial(struct buffer* return 0; } +/* get serial from SOA rdata */ +static uint32_t soa_rdata_get_serial(uint8_t* rdata, uint16_t rdlength) +{ + if(rdlength < 2*sizeof(void*) /* name ptr */ + 4 /* serial */) + return 0; + return read_uint32(rdata+2*sizeof(void*)); +} + /* get serial from SOA RR */ static uint32_t soa_rr_get_serial(struct rr* rr) { - if(rr->rdata_count < 3) - return 0; - if(rr->rdatas[2].data[0] < 4) - return 0; - return read_uint32(&rr->rdatas[2].data[1]); + return soa_rdata_get_serial(rr->rdata, rr->rdlength); } /* get the current serial from the zone */ @@ -503,11 +505,7 @@ uint32_t zone_get_current_serial(struct return 0; if(zone->soa_rrset->rr_count == 0) return 0; - if(zone->soa_rrset->rrs[0].rdata_count < 3) - return 0; - if(zone->soa_rrset->rrs[0].rdatas[2].data[0] < 4) - return 0; - return read_uint32(&zone->soa_rrset->rrs[0].rdatas[2].data[1]); + return soa_rr_get_serial(zone->soa_rrset->rrs[0]); } /* iterator over ixfr data. find first element, eg. oldest zone version @@ -574,6 +572,7 @@ static struct ixfr_data* ixfr_data_prev( struct ixfr_data* cur, size_t* prevcount) { struct ixfr_data* prev; + int wrapped = 0; if(!cur || cur == (struct ixfr_data*)RBTREE_NULL) return NULL; if(cur->oldserial == ixfr->oldest_serial) @@ -607,6 +606,11 @@ static struct ixfr_data* ixfr_data_prev( /* We hit the first element in the tree, go again * at the last one. Wrap around. */ prev = (struct ixfr_data*)rbtree_last(ixfr->data); + if(wrapped) { + /* The lookup wrapped again, this is a loop. */ + return NULL; + } + wrapped = 1; } } /* no elements in list */ @@ -618,8 +622,11 @@ static int connect_ixfrs(struct zone_ixf uint32_t* end_serial) { struct ixfr_data* p = data; + size_t count = 0; while(p != NULL) { struct ixfr_data* next = ixfr_data_next(ixfr, p); + if(count++ > ixfr->data->count + 12) + return 0; /* loop */ if(next) { if(p->newserial != next->oldserial) { /* These ixfrs are not connected, @@ -695,6 +702,18 @@ static uint16_t ixfr_copy_rrs_into_packe query->ixfr_pos_of_newsoa = buffer_position(query->packet); } else { /* cannot add another RR, so return */ + if(total_added == 0) { + /* RR exceeds TCP_MAX_MESSAGE_LEN (65535 bytes): + * cannot fit in any DNS message. Abort the + * IXFR transfer rather than spinning. */ + VERBOSITY(2, (LOG_ERR, "ixfr_out: SOA RR in zone %s too large for any DNS message " + "(wire encoding exceeds %d bytes), aborting IXFR transfer", + + domain_to_string(query->zone->apex), + TCP_MAX_MESSAGE_LEN)); + RCODE_SET(query->packet, RCODE_SERVFAIL); + query->ixfr_is_done = 1; + } return total_added; } } @@ -708,6 +727,18 @@ static uint16_t ixfr_copy_rrs_into_packe total_added++; } else { /* cannot add another RR, so return */ + if(total_added == 0) { + /* RR exceeds TCP_MAX_MESSAGE_LEN (65535 bytes): + * cannot fit in any DNS message. Abort the + * IXFR transfer rather than spinning. */ + VERBOSITY(2, (LOG_ERR, "ixfr_out: SOA RR in zone %s too large for any DNS message " + "(wire encoding exceeds %d bytes), aborting IXFR transfer", + + domain_to_string(query->zone->apex), + TCP_MAX_MESSAGE_LEN)); + RCODE_SET(query->packet, RCODE_SERVFAIL); + query->ixfr_is_done = 1; + } return total_added; } } @@ -724,6 +755,21 @@ static uint16_t ixfr_copy_rrs_into_packe } else { /* the next record does not fit in the remaining * space of the packet */ + if(total_added == 0) { + /* RR exceeds TCP_MAX_MESSAGE_LEN (65535 bytes): + * cannot fit in any DNS message. Abort the + * IXFR transfer rather than spinning. */ + char apexstr[MAXDOMAINLEN * 5]; + char* ownerstr = ""; + if(rrlen) + ownerstr = wiredname2str(query->ixfr_data->del + query->ixfr_count_del); + domain_to_string_buf(query->zone->apex, apexstr); + VERBOSITY(2, (LOG_ERR, "ixfr_out: RR at %s in zone %s too large for any DNS message " + "(wire encoding exceeds %d bytes), aborting IXFR transfer", + ownerstr, apexstr, TCP_MAX_MESSAGE_LEN)); + RCODE_SET(query->packet, RCODE_SERVFAIL); + query->ixfr_is_done = 1; + } return total_added; } } @@ -740,6 +786,21 @@ static uint16_t ixfr_copy_rrs_into_packe } else { /* the next record does not fit in the remaining * space of the packet */ + if(total_added == 0) { + /* RR exceeds TCP_MAX_MESSAGE_LEN (65535 bytes): + * cannot fit in any DNS message. Abort the + * IXFR transfer rather than spinning. */ + char apexstr[MAXDOMAINLEN * 5]; + char* ownerstr = ""; + if(rrlen) + ownerstr = wiredname2str(query->ixfr_data->add + query->ixfr_count_add); + domain_to_string_buf(query->zone->apex, apexstr); + VERBOSITY(2, (LOG_ERR, "ixfr_out: RR at %s in zone %s too large for any DNS message " + "(wire encoding exceeds %d bytes), aborting IXFR transfer", + ownerstr, apexstr, TCP_MAX_MESSAGE_LEN)); + RCODE_SET(query->packet, RCODE_SERVFAIL); + query->ixfr_is_done = 1; + } return total_added; } } @@ -755,8 +816,10 @@ query_state_type query_ixfr(struct nsd * return QUERY_PROCESSED; pktcompression_init(&pcomp); - if (query->maxlen > IXFR_MAX_MESSAGE_LEN) + if (query->maxlen > IXFR_MAX_MESSAGE_LEN) { + buffer_set_position(query->packet, QHEADERSZ); query->maxlen = IXFR_MAX_MESSAGE_LEN; + } assert(!query_overflow(query)); /* only keep running values for most packets */ @@ -811,8 +874,8 @@ query_state_type query_ixfr(struct nsd * query_add_compression_domain(query, zone->apex, QHEADERSZ); if(packet_encode_rr(query, zone->apex, - &zone->soa_rrset->rrs[0], - zone->soa_rrset->rrs[0].ttl)) { + zone->soa_rrset->rrs[0], + zone->soa_rrset->rrs[0]->ttl)) { ANCOUNT_SET(query->packet, 1); } else { RCODE_SET(query->packet, RCODE_SERVFAIL); @@ -881,7 +944,8 @@ query_state_type query_ixfr(struct nsd * total_added = ixfr_copy_rrs_into_packet(query, &pcomp); - while(query->ixfr_count_add >= query->ixfr_data->add_len) { + while(!query->ixfr_is_done && + query->ixfr_count_add >= query->ixfr_data->add_len) { struct ixfr_data* next = ixfr_data_next(query->zone->ixfr, query->ixfr_data); /* finished the ixfr_data */ @@ -1081,7 +1145,8 @@ void ixfr_store_finish(struct ixfr_store ixfr_store_free(ixfr_store); return; } - zone_ixfr_add(ixfr_store->zone->ixfr, ixfr_store->data, 1); + zone_ixfr_add(ixfr_store->zone->ixfr, ixfr_store->data, 1, + ixfr_store->zone->opts->name); ixfr_store->data = NULL; /* free structure */ @@ -1089,7 +1154,7 @@ void ixfr_store_finish(struct ixfr_store } /* read SOA rdata section for SOA storage */ -static int read_soa_rdata(struct buffer* packet, uint8_t* primns, +static int read_soa_rdata_fields(struct buffer* packet, uint8_t* primns, int* primns_len, uint8_t* email, int* email_len, uint32_t* serial, uint32_t* refresh, uint32_t* retry, uint32_t* expire, uint32_t* minimum, size_t* sz) @@ -1178,7 +1243,7 @@ void ixfr_store_add_newsoa(struct ixfr_s buffer_set_position(packet, oldpos); return; } - if(!read_soa_rdata(packet, primns, &primns_len, email, &email_len, + if(!read_soa_rdata_fields(packet, primns, &primns_len, email, &email_len, &serial, &refresh, &retry, &expire, &minimum, &sz)) { log_msg(LOG_ERR, "ixfr_store newsoa: cannot parse packet"); ixfr_store_cancel(ixfr_store); @@ -1187,6 +1252,12 @@ void ixfr_store_add_newsoa(struct ixfr_s } rdlen_uncompressed = primns_len + email_len + 4 + 4 + 4 + 4 + 4; + if(ixfr_store->data->oldsoa && ixfr_store->data->oldserial == serial) { + log_msg(LOG_ERR, "ixfr_store newsoa: duplicate serial number"); + ixfr_store_cancel(ixfr_store); + buffer_set_position(packet, oldpos); + return; + } ixfr_store->data->newserial = serial; /* store the soa record */ @@ -1232,7 +1303,7 @@ void ixfr_store_add_oldsoa(struct ixfr_s buffer_set_position(packet, oldpos); return; } - if(!read_soa_rdata(packet, primns, &primns_len, email, &email_len, + if(!read_soa_rdata_fields(packet, primns, &primns_len, email, &email_len, &serial, &refresh, &retry, &expire, &minimum, &sz)) { log_msg(LOG_ERR, "ixfr_store oldsoa: cannot parse packet"); ixfr_store_cancel(ixfr_store); @@ -1241,6 +1312,12 @@ void ixfr_store_add_oldsoa(struct ixfr_s } rdlen_uncompressed = primns_len + email_len + 4 + 4 + 4 + 4 + 4; + if(ixfr_store->data->newsoa && ixfr_store->data->newserial == serial) { + log_msg(LOG_ERR, "ixfr_store oldsoa: duplicate serial number"); + ixfr_store_cancel(ixfr_store); + buffer_set_position(packet, oldpos); + return; + } ixfr_store->data->oldserial = serial; /* store the soa record */ @@ -1253,25 +1330,21 @@ void ixfr_store_add_oldsoa(struct ixfr_s buffer_set_position(packet, oldpos); } -/* store RR in data segment */ -static int ixfr_putrr(const struct dname* dname, uint16_t type, uint16_t klass, - uint32_t ttl, rdata_atom_type* rdatas, ssize_t rdata_num, - uint8_t** rrs, size_t* rrs_len, size_t* rrs_capacity) +/* store RR in data segment. + * return -1 on fail of wireformat, 0 on allocate failure, or 1 success. */ +static int ixfr_putrr(const rr_type* rr, uint8_t** rrs, size_t* rrs_len, + size_t* rrs_capacity) { - size_t rdlen_uncompressed, sz; + int32_t rdlen_uncompressed; + size_t sz; uint8_t* sp; - int i; + const dname_type* dname; - /* find rdatalen */ - rdlen_uncompressed = 0; - for(i=0; iname_size; - } else { - rdlen_uncompressed += rdatas[i].data[0]; - } - } + rdlen_uncompressed = rr_calculate_uncompressed_rdata_length(rr); + if (rdlen_uncompressed < 0) + return -1; /* malformed */ + + dname = domain_dname(rr->owner); sz = dname->name_size + 2 /*type*/ + 2 /*class*/ + 4 /*ttl*/ + 2 /*rdlen*/ + rdlen_uncompressed; @@ -1285,36 +1358,18 @@ static int ixfr_putrr(const struct dname *rrs_len += sz; memmove(sp, dname_name(dname), dname->name_size); sp += dname->name_size; - write_uint16(sp, type); - sp += 2; - write_uint16(sp, klass); - sp += 2; - write_uint32(sp, ttl); - sp += 4; - write_uint16(sp, rdlen_uncompressed); - sp += 2; - for(i=0; iname_size); - sp += domain_dname(rdatas[i].domain)->name_size; - } else { - memmove(sp, &rdatas[i].data[1], rdatas[i].data[0]); - sp += rdatas[i].data[0]; - } - } + write_uint16(sp, rr->type); + write_uint16(sp + 2, rr->klass); + write_uint32(sp + 4, rr->ttl); + write_uint16(sp + 8, rdlen_uncompressed); + rr_write_uncompressed_rdata(rr, sp+10, rdlen_uncompressed); return 1; } -void ixfr_store_putrr(struct ixfr_store* ixfr_store, const struct dname* dname, - uint16_t type, uint16_t klass, uint32_t ttl, struct buffer* packet, - uint16_t rrlen, struct region* temp_region, uint8_t** rrs, - size_t* rrs_len, size_t* rrs_capacity) +void ixfr_store_putrr(struct ixfr_store* ixfr_store, const rr_type* rr, + uint8_t** rrs, size_t* rrs_len, size_t* rrs_capacity) { - domain_table_type *temptable; - rdata_atom_type *rdatas; - ssize_t rdata_num; - size_t oldpos; + int code; if(ixfr_store->cancelled) return; @@ -1322,7 +1377,7 @@ void ixfr_store_putrr(struct ixfr_store* /* The SOA data is stored with separate calls. And then appended * during the finish operation. We do not have to store it here * when called from difffile's IXFR processing with type SOA. */ - if(type == TYPE_SOA) + if(rr->type == TYPE_SOA) return; /* make space for these RRs we have now; basically once we * grow beyond the current allowed amount an older IXFR is deleted. */ @@ -1331,76 +1386,57 @@ void ixfr_store_putrr(struct ixfr_store* if(ixfr_store->cancelled) return; - /* parse rdata */ - oldpos = buffer_position(packet); - temptable = domain_table_create(temp_region); - rdata_num = rdata_wireformat_to_rdata_atoms(temp_region, temptable, - type, rrlen, packet, &rdatas); - buffer_set_position(packet, oldpos); - if(rdata_num == -1) { - log_msg(LOG_ERR, "ixfr_store addrr: cannot parse packet"); - ixfr_store_cancel(ixfr_store); - return; - } + /* store rdata */ + code = ixfr_putrr(rr, rrs, rrs_len, rrs_capacity); - if(!ixfr_putrr(dname, type, klass, ttl, rdatas, rdata_num, - rrs, rrs_len, rrs_capacity)) { - log_msg(LOG_ERR, "ixfr_store addrr: cannot allocate space"); + if (code <= 0) { + if (code == -1) + log_msg(LOG_ERR, "ixfr_store addrr: cannot parse rdata format"); + else + log_msg(LOG_ERR, "ixfr_store addrr: cannot allocate space"); ixfr_store_cancel(ixfr_store); return; } } -void ixfr_store_delrr(struct ixfr_store* ixfr_store, const struct dname* dname, - uint16_t type, uint16_t klass, uint32_t ttl, struct buffer* packet, - uint16_t rrlen, struct region* temp_region) +void ixfr_store_delrr(struct ixfr_store* ixfr_store, const rr_type* rr) { if(ixfr_store->cancelled) return; - ixfr_store_putrr(ixfr_store, dname, type, klass, ttl, packet, rrlen, - temp_region, &ixfr_store->data->del, + ixfr_store_putrr(ixfr_store, rr, &ixfr_store->data->del, &ixfr_store->data->del_len, &ixfr_store->del_capacity); } -void ixfr_store_addrr(struct ixfr_store* ixfr_store, const struct dname* dname, - uint16_t type, uint16_t klass, uint32_t ttl, struct buffer* packet, - uint16_t rrlen, struct region* temp_region) +void ixfr_store_addrr(struct ixfr_store* ixfr_store, const rr_type* rr) { if(ixfr_store->cancelled) return; - ixfr_store_putrr(ixfr_store, dname, type, klass, ttl, packet, rrlen, - temp_region, &ixfr_store->data->add, + ixfr_store_putrr(ixfr_store, rr, &ixfr_store->data->add, &ixfr_store->data->add_len, &ixfr_store->add_capacity); } -int ixfr_store_addrr_rdatas(struct ixfr_store* ixfr_store, - const struct dname* dname, uint16_t type, uint16_t klass, - uint32_t ttl, rdata_atom_type* rdatas, ssize_t rdata_num) +int ixfr_store_addrr_rdatas(struct ixfr_store* ixfr_store, const rr_type *rr) { if(ixfr_store->cancelled) return 1; - if(type == TYPE_SOA) + if(rr->type == TYPE_SOA) return 1; - return ixfr_putrr(dname, type, klass, ttl, rdatas, rdata_num, - &ixfr_store->data->add, &ixfr_store->data->add_len, - &ixfr_store->add_capacity); + if(ixfr_putrr(rr, &ixfr_store->data->add, &ixfr_store->data->add_len, + &ixfr_store->add_capacity) <= 0) + return 0; + return 1; } int ixfr_store_add_newsoa_rdatas(struct ixfr_store* ixfr_store, - const struct dname* dname, uint16_t type, uint16_t klass, - uint32_t ttl, rdata_atom_type* rdatas, ssize_t rdata_num) + const rr_type* rr) { size_t capacity = 0; - uint32_t serial; if(ixfr_store->cancelled) return 1; - if(rdata_num < 2 || rdata_atom_size(rdatas[2]) < 4) + if(!retrieve_soa_rdata_serial(rr, &ixfr_store->data->newserial)) return 0; - memcpy(&serial, rdata_atom_data(rdatas[2]), sizeof(serial)); - ixfr_store->data->newserial = ntohl(serial); - if(!ixfr_putrr(dname, type, klass, ttl, rdatas, rdata_num, - &ixfr_store->data->newsoa, &ixfr_store->data->newsoa_len, - &ixfr_store->add_capacity)) + if(ixfr_putrr(rr, &ixfr_store->data->newsoa, + &ixfr_store->data->newsoa_len, &ixfr_store->add_capacity) <= 0) return 0; ixfr_trim_capacity(&ixfr_store->data->newsoa, &ixfr_store->data->newsoa_len, &capacity); @@ -1475,27 +1511,26 @@ int ixfr_store_oldsoa_uncompressed(struc uint8_t* dname, size_t dname_len, uint16_t type, uint16_t klass, uint32_t ttl, uint8_t* rdata, size_t rdata_len) { - size_t capacity = 0; + uint32_t serial; + size_t capacity = 0, index, count; if(ixfr_store->cancelled) return 1; if(!ixfr_storerr_uncompressed(dname, dname_len, type, klass, ttl, rdata, rdata_len, &ixfr_store->data->oldsoa, &ixfr_store->data->oldsoa_len, &capacity)) return 0; - { - uint32_t serial; - size_t index, count = 0; - if (!(count = skip_dname(rdata, rdata_len))) - return 0; - index = count; - if (!(count = skip_dname(rdata+index, rdata_len-index))) - return 0; - index += count; - if (rdata_len - index < 4) - return 0; - memcpy(&serial, rdata+index, sizeof(serial)); - ixfr_store->data->oldserial = ntohl(serial); - } + + if (!(count = skip_dname(rdata, rdata_len))) + return 0; + index = count; + if (!(count = skip_dname(rdata+index, rdata_len-index))) + return 0; + index += count; + if (rdata_len - index < 4) + return 0; + memcpy(&serial, rdata+index, sizeof(serial)); + ixfr_store->data->oldserial = ntohl(serial); + ixfr_trim_capacity(&ixfr_store->data->oldsoa, &ixfr_store->data->oldsoa_len, &capacity); return 1; @@ -1573,6 +1608,8 @@ static void zone_ixfr_remove_oldest(stru { if(ixfr->data->count > 0) { struct ixfr_data* oldest = ixfr_data_first(ixfr); + if(!oldest) + return; /* oldest_serial is stale, skip eviction */ if(ixfr->oldest_serial == oldest->oldserial) { if(ixfr->data->count > 1) { struct ixfr_data* next = ixfr_data_next(ixfr, oldest); @@ -1638,10 +1675,20 @@ void zone_ixfr_remove(struct zone_ixfr* ixfr_data_free(data); } -void zone_ixfr_add(struct zone_ixfr* ixfr, struct ixfr_data* data, int isnew) +void zone_ixfr_add(struct zone_ixfr* ixfr, struct ixfr_data* data, int isnew, + const char* zname) { memset(&data->node, 0, sizeof(data->node)); - if(ixfr->data->count == 0) { + data->node.key = &data->oldserial; + if(rbtree_insert(ixfr->data, &data->node) == NULL) { + /* duplicate oldserial in IXFR stream - reject the chunk. */ + log_msg(LOG_WARNING, "zone %s: chunk with duplicate " + "oldserial=%u discarded; broken journal chain prevented", + (zname?zname:""), (unsigned)data->oldserial); + ixfr_data_free(data); + return; + } + if(ixfr->data->count == 1) { ixfr->oldest_serial = data->oldserial; ixfr->newest_serial = data->oldserial; } else if(isnew) { @@ -1651,8 +1698,6 @@ void zone_ixfr_add(struct zone_ixfr* ixf /* added older entry, before the others */ ixfr->oldest_serial = data->oldserial; } - data->node.key = &data->oldserial; - rbtree_insert(ixfr->data, &data->node); ixfr->total_size += ixfr_data_size(data); } @@ -1893,7 +1938,7 @@ static int ixfr_rename_files(struct zone int dest_num_files) { struct ixfr_data* data, *startspot = NULL; - size_t prevcount = 0; + size_t prevcount = 0, count = 0; int destnum; if(!zone->ixfr || !zone->ixfr->data) return 1; @@ -1932,6 +1977,11 @@ static int ixfr_rename_files(struct zone * has been renamed to a temporary name */ startspot = data; data = ixfr_data_next(zone->ixfr, data); + if(count++ > zone->ixfr->data->count+12) { + /* loop */ + ixfr_delete_rest_files(zone, data, zfile, 1); + return 0; + } destnum--; } @@ -1982,45 +2032,20 @@ static int ixfr_write_file_header(struct return 1; } -/* print rdata on one line */ -static int -oneline_print_rdata(buffer_type *output, rrtype_descriptor_type *descriptor, - rr_type* record) -{ - size_t i; - size_t saved_position = buffer_position(output); - - for (i = 0; i < record->rdata_count; ++i) { - if (i == 0) { - buffer_printf(output, "\t"); - } else { - buffer_printf(output, " "); - } - if (!rdata_atom_to_string( - output, - (rdata_zoneformat_type) descriptor->zoneformat[i], - record->rdatas[i], record)) - { - buffer_set_position(output, saved_position); - return 0; - } - } - - return 1; -} - /* parse wireformat RR into a struct RR in temp region */ static int parse_wirerr_into_temp(struct zone* zone, char* fname, struct region* temp, uint8_t* buf, size_t len, - const dname_type** dname, struct rr* rr) + const dname_type** dname, struct rr** rr) { size_t bufpos = 0; - uint16_t rdlen; - ssize_t rdata_num; + uint16_t rdlen, tp, klass; + uint32_t ttl; + int32_t code; + const struct nsd_type_descriptor *descriptor; buffer_type packet; domain_table_type* owners; + struct domain *domain; owners = domain_table_create(temp); - memset(rr, 0, sizeof(*rr)); *dname = dname_make(temp, buf, 1); if(!*dname) { log_msg(LOG_ERR, "failed to write zone %s IXFR data %s: failed to parse dname", zone->opts->name, fname); @@ -2031,11 +2056,11 @@ static int parse_wirerr_into_temp(struct log_msg(LOG_ERR, "failed to write zone %s IXFR data %s: buffer too short", zone->opts->name, fname); return 0; } - rr->type = read_uint16(buf+bufpos); + tp = read_uint16(buf+bufpos); bufpos += 2; - rr->klass = read_uint16(buf+bufpos); + klass = read_uint16(buf+bufpos); bufpos += 2; - rr->ttl = read_uint32(buf+bufpos); + ttl = read_uint32(buf+bufpos); bufpos += 4; rdlen = read_uint16(buf+bufpos); bufpos += 2; @@ -2043,14 +2068,20 @@ static int parse_wirerr_into_temp(struct log_msg(LOG_ERR, "failed to write zone %s IXFR data %s: buffer too short for rdatalen", zone->opts->name, fname); return 0; } + domain = domain_table_insert(owners, *dname); buffer_create_from(&packet, buf+bufpos, rdlen); - rdata_num = rdata_wireformat_to_rdata_atoms( - temp, owners, rr->type, rdlen, &packet, &rr->rdatas); - if(rdata_num == -1) { - log_msg(LOG_ERR, "failed to write zone %s IXFR data %s: cannot parse rdata", zone->opts->name, fname); + descriptor = nsd_type_descriptor(tp); + code = descriptor->read_rdata(owners, rdlen, &packet, rr); + if(code < 0) { + log_msg(LOG_ERR, "failed to write zone %s IXFR data %s: cannot parse rdata %s %s %s", zone->opts->name, fname, + dname_to_string(*dname,0), rrtype_to_string(tp), + read_rdata_fail_str(code)); return 0; } - rr->rdata_count = rdata_num; + (*rr)->owner = domain; + (*rr)->type = tp; + (*rr)->klass = klass; + (*rr)->ttl = ttl; return 1; } @@ -2059,16 +2090,14 @@ static int parse_wirerr_into_temp(struct static int print_rr_oneline(struct buffer* rr_buffer, const dname_type* dname, struct rr* rr) { - rrtype_descriptor_type *descriptor; - descriptor = rrtype_descriptor_by_type(rr->type); + const nsd_type_descriptor_type *descriptor = nsd_type_descriptor( + rr->type); buffer_printf(rr_buffer, "%s", dname_to_string(dname, NULL)); buffer_printf(rr_buffer, "\t%lu\t%s\t%s", (unsigned long)rr->ttl, rrclass_to_string(rr->klass), rrtype_to_string(rr->type)); - if(!oneline_print_rdata(rr_buffer, descriptor, rr)) { - if(!rdata_atoms_to_unknown_string(rr_buffer, - descriptor, rr->rdata_count, rr->rdatas)) { + if (!print_rdata(rr_buffer, descriptor, rr)) { + if(!print_unknown_rdata(rr_buffer, descriptor, rr)) return 0; - } } return 1; } @@ -2078,7 +2107,7 @@ static int ixfr_write_rr(struct zone* zo uint8_t* buf, size_t len, struct region* temp, buffer_type* rr_buffer) { const dname_type* dname; - struct rr rr; + struct rr* rr; if(!parse_wirerr_into_temp(zone, fname, temp, buf, len, &dname, &rr)) { region_free_all(temp); @@ -2086,7 +2115,7 @@ static int ixfr_write_rr(struct zone* zo } buffer_clear(rr_buffer); - if(!print_rr_oneline(rr_buffer, dname, &rr)) { + if(!print_rr_oneline(rr_buffer, dname, rr)) { log_msg(LOG_ERR, "failed to write zone %s IXFR data %s: cannot spool RR string into buffer", zone->opts->name, fname); region_free_all(temp); return 0; @@ -2316,49 +2345,32 @@ static void ixfr_temp_deldomain(struct d static void clear_temp_table_of_rr(struct domain_table* temptable, struct zone* tempzone, struct rr* rr) { -#if 0 /* clear out by removing everything, alternate for the cleanout code */ - /* clear domains from the tempzone, - * the only domain left is the zone apex and its parents */ - domain_type* domain; -#ifdef USE_RADIX_TREE - struct radnode* first = radix_first(temptable->nametree); - domain = first?(domain_type*)first->elem:NULL; -#else - domain = (domain_type*)rbtree_first(temptable->names_to_domains); -#endif - while(domain != (domain_type*)RBTREE_NULL && domain) { - domain_type* next = domain_next(domain); - if(domain != tempzone->apex && - !domain_is_subdomain(tempzone->apex, domain)) { - domain_table_delete(temptable, domain); - } else { - if(!domain->parent /* is the root */ || - domain == tempzone->apex) - domain->usage = 1; - else domain->usage = 0; - } - domain = next; - } - - if(rr->owner == tempzone->apex) { - tempzone->apex->rrsets = NULL; - tempzone->soa_rrset = NULL; - tempzone->soa_nx_rrset = NULL; - tempzone->ns_rrset = NULL; - } - return; -#endif + const nsd_type_descriptor_type* descriptor = + nsd_type_descriptor(rr->type); /* clear domains in the rdata */ - unsigned i; - for(i=0; irdata_count; i++) { - if(rdata_atom_is_domain(rr->type, i)) { - /* clear out that dname */ - struct domain* domain = - rdata_atom_domain(rr->rdatas[i]); - domain->usage --; - if(domain != tempzone->apex && domain->usage == 0) - ixfr_temp_deldomain(temptable, domain, rr->owner); + if(descriptor->has_references) { + uint16_t offset = 0; + size_t i; + for(i=0; i < descriptor->rdata.length; i++) { + uint16_t field_len; + struct domain* domain; + if(rr->rdlength == offset && + descriptor->rdata.fields[i].is_optional) + break; /* There are no more rdata fields. */ + if(!lookup_rdata_field_entry(descriptor, i, rr, offset, + &field_len, &domain)) + break; /* malformed */ + if(domain != NULL) { + /* The field is a domain reference. */ + /* clear out that dname */ + domain->usage --; + if(domain != tempzone->apex && + domain->usage == 0) + ixfr_temp_deldomain(temptable, domain, + rr->owner); + } + offset += field_len; } } @@ -2383,6 +2395,7 @@ static int ixfr_data_readnewsoa(struct i uint32_t dest_serial) { size_t capacity = 0; + int code; if(rr->type != TYPE_SOA) { zone_error(parser, "zone %s ixfr data: IXFR data does not start with SOA", zone->opts->name); @@ -2410,9 +2423,13 @@ static int ixfr_data_readnewsoa(struct i 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)) { - zone_error(parser, "zone %s ixfr data: cannot allocate space", - zone->opts->name); + if((code=ixfr_putrr(rr, &data->newsoa, &data->newsoa_len, &capacity)) + <= 0) { + if(code == -1) + zone_error(parser, "zone %s ixfr data: cannot parse rdata format", + zone->opts->name); + else zone_error(parser, "zone %s ixfr data: cannot allocate space", + zone->opts->name); return 0; } clear_temp_table_of_rr(temptable, tempzone, rr); @@ -2449,7 +2466,7 @@ static int ixfr_data_readoldsoa(struct i 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)) { + if(!ixfr_putrr(rr, &data->oldsoa, &data->oldsoa_len, &capacity)) { zone_error(parser, "zone %s ixfr data: cannot allocate space", zone->opts->name); return 0; @@ -2467,7 +2484,7 @@ static int ixfr_data_readdel(struct ixfr struct domain_table* temptable, struct zone* tempzone) { size_t capacity = 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)) { + if(!ixfr_putrr(rr, &data->del, &data->del_len, &capacity)) { zone_error(parser, "zone %s ixdr data: cannot allocate space", zone->opts->name); return 0; @@ -2491,7 +2508,7 @@ static int ixfr_data_readadd(struct ixfr struct domain_table* temptable, struct zone* tempzone) { size_t capacity = 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)) { + if(!ixfr_putrr(rr, &data->add, &data->add_len, &capacity)) { zone_error(parser, "zone %s ixfr data: cannot allocate space", zone->opts->name); return 0; @@ -2509,7 +2526,8 @@ static int ixfr_data_readadd(struct ixfr struct ixfr_data_state { struct zone *zone; struct ixfr_data *data; - struct region *tempregion, *stayregion; + struct region *stayregion; + struct region *tempregion; struct domain_table *temptable; struct zone *tempzone; uint32_t *dest_serial; @@ -2531,9 +2549,9 @@ static int32_t ixfr_data_accept( 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; + const struct nsd_type_descriptor *descriptor; + int32_t code; assert(parser); @@ -2544,17 +2562,32 @@ static int32_t ixfr_data_accept( 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)); + descriptor = nsd_type_descriptor(type); + code = descriptor->read_rdata(state->temptable, rdlength, &buffer, &rr); + /* This has validated the fields on the rdata. The content can be + * dealt with, if this is successful, later on by iterating over the + * rdata fields. For compression, and for printout, the rdata field + * format is known to be good. + * If the field validation is not needed, the wireformat in the + * rdata, rdlength could have been used to add to the ixfr store. + * But it is more prudent to validate the rdata fields. */ + if(code < 0) { + if(verbosity >= 3) { + zone_log(parser, ZONE_ERROR, "the RR rdata fields are wrong for the type"); + } + VERBOSITY(3, (LOG_INFO, "zone %s IXFR bad RR, cannot parse " + "rdata of %s %s %s", state->zone->opts->name, + dname_to_string(dname, NULL), rrtype_to_string(type), + read_rdata_fail_str(code))); + if(code == TRUNCATED) + return ZONE_OUT_OF_MEMORY; + return ZONE_BAD_PARAMETER; + } 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, @@ -2621,6 +2654,7 @@ static int ixfr_data_read(struct nsd* ns const char* ixfrfile, uint32_t* dest_serial, int file_num) { struct ixfr_data_state state = { 0 }; + size_t ixfr_data_sz; if(!zone->apex) { return 0; @@ -2700,9 +2734,11 @@ static int ixfr_data_read(struct nsd* ns ixfr_data_free(state.data); return 0; } - zone_ixfr_add(zone->ixfr, state.data, 0); + ixfr_data_sz = ixfr_data_size(state.data); /* pick up size before + possible deletion of the item */ + zone_ixfr_add(zone->ixfr, state.data, 0, zone->opts->name); VERBOSITY(3, (LOG_INFO, "zone %s read %s IXFR data of %u bytes", - zone->opts->name, ixfrfile, (unsigned)ixfr_data_size(state.data))); + zone->opts->name, ixfrfile, (unsigned)ixfr_data_sz)); return 1; } Index: usr.sbin/nsd/ixfr.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/ixfr.h,v diff -u -p -r1.3 ixfr.h --- usr.sbin/nsd/ixfr.h 20 Dec 2023 17:29:01 -0000 1.3 +++ usr.sbin/nsd/ixfr.h 21 Sep 2026 16:29:07 -0000 @@ -181,21 +181,15 @@ void ixfr_store_add_newsoa(struct ixfr_s void ixfr_store_add_oldsoa(struct ixfr_store* ixfr_store, uint32_t ttl, struct buffer* packet, size_t rrlen); -void ixfr_store_delrr(struct ixfr_store* ixfr_store, const struct dname* dname, - uint16_t type, uint16_t klass, uint32_t ttl, struct buffer* packet, - uint16_t rrlen, struct region* temp_region); -void ixfr_store_addrr(struct ixfr_store* ixfr_store, const struct dname* dname, - uint16_t type, uint16_t klass, uint32_t ttl, struct buffer* packet, - uint16_t rrlen, struct region* temp_region); -int ixfr_store_addrr_rdatas(struct ixfr_store* ixfr_store, - const struct dname* dname, uint16_t type, uint16_t klass, - uint32_t ttl, rdata_atom_type* rdatas, ssize_t rdata_num); +void ixfr_store_delrr(struct ixfr_store* ixfr_store, + const rr_type *rr); +void ixfr_store_addrr(struct ixfr_store* ixfr_store, const rr_type *rr); +int ixfr_store_addrr_rdatas(struct ixfr_store* ixfr_store, const rr_type *rr); int ixfr_store_delrr_uncompressed(struct ixfr_store* ixfr_store, uint8_t* dname, size_t dname_len, uint16_t type, uint16_t klass, uint32_t ttl, uint8_t* rdata, size_t rdata_len); int ixfr_store_add_newsoa_rdatas(struct ixfr_store* ixfr_store, - const struct dname* dname, uint16_t type, uint16_t klass, - uint32_t ttl, rdata_atom_type* rdatas, ssize_t rdata_num); + const rr_type* rr); int ixfr_store_oldsoa_uncompressed(struct ixfr_store* ixfr_store, uint8_t* dname, size_t dname_len, uint16_t type, uint16_t klass, uint32_t ttl, uint8_t* rdata, size_t rdata_len); @@ -221,7 +215,8 @@ void zone_ixfr_make_space(struct zone_ix void zone_ixfr_remove(struct zone_ixfr* ixfr, struct ixfr_data* data); /* add ixfr data to the zone_ixfr */ -void zone_ixfr_add(struct zone_ixfr* ixfr, struct ixfr_data* data, int isnew); +void zone_ixfr_add(struct zone_ixfr* ixfr, struct ixfr_data* data, int isnew, + const char* zname); /* find serial number in ixfr list, or NULL if not found */ struct ixfr_data* zone_ixfr_find_serial(struct zone_ixfr* ixfr, Index: usr.sbin/nsd/ixfrcreate.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/ixfrcreate.c,v diff -u -p -r1.5 ixfrcreate.c --- usr.sbin/nsd/ixfrcreate.c 6 Sep 2025 17:41:37 -0000 1.5 +++ usr.sbin/nsd/ixfrcreate.c 21 Sep 2026 16:29:07 -0000 @@ -15,6 +15,7 @@ #include "namedb.h" #include "ixfr.h" #include "options.h" +#include "rdata.h" /* spool a uint16_t to file */ static int spool_u16(FILE* out, uint16_t val) @@ -47,44 +48,25 @@ static int spool_dname(FILE* out, dname_ return 1; } -/* calculate the rdatalen of an RR */ -static size_t rr_rdatalen_uncompressed(rr_type* rr) -{ - int i; - size_t rdlen_uncompressed = 0; - for(i=0; irdata_count; i++) { - if(rdata_atom_is_domain(rr->type, i)) { - rdlen_uncompressed += domain_dname(rr->rdatas[i].domain) - ->name_size; - } else { - rdlen_uncompressed += rr->rdatas[i].data[0]; - } - } - return rdlen_uncompressed; -} - /* spool the data for one rr into the file */ static int spool_rr_data(FILE* out, rr_type* rr) { - int i; uint16_t rdlen; + int32_t code; + uint8_t buf[MAX_RDLENGTH]; if(!spool_u32(out, rr->ttl)) return 0; - rdlen = rr_rdatalen_uncompressed(rr); + code = rr_calculate_uncompressed_rdata_length(rr); + if(code < 0) + return 0; + if((size_t)code > sizeof(buf)) + return 0; /* Buffer too small. The buffer has max length. */ + rdlen = (uint16_t)code; if(!spool_u16(out, rdlen)) return 0; - for(i=0; irdata_count; i++) { - if(rdata_atom_is_domain(rr->type, i)) { - if(!fwrite(dname_name(domain_dname( - rr->rdatas[i].domain)), domain_dname( - rr->rdatas[i].domain)->name_size, 1, out)) - return 0; - } else { - if(!fwrite(&rr->rdatas[i].data[1], - rr->rdatas[i].data[0], 1, out)) - return 0; - } - } + rr_write_uncompressed_rdata(rr, buf, rdlen); + if(!fwrite(buf, rdlen, 1, out)) + return 0; return 1; } @@ -94,14 +76,14 @@ static int spool_rrset(FILE* out, rrset_ int i; if(rrset->rr_count == 0) return 1; - if(!spool_u16(out, rrset->rrs[0].type)) + if(!spool_u16(out, rrset->rrs[0]->type)) return 0; - if(!spool_u16(out, rrset->rrs[0].klass)) + if(!spool_u16(out, rrset->rrs[0]->klass)) return 0; if(!spool_u16(out, rrset->rr_count)) return 0; for(i=0; irr_count; i++) { - if(!spool_rr_data(out, &rrset->rrs[i])) + if(!spool_rr_data(out, rrset->rrs[i])) return 0; } return 1; @@ -339,31 +321,11 @@ static int process_store_oldsoa(struct i /* see if rdata matches, true if equal */ static int rdata_match(struct rr* rr, uint8_t* rdata, uint16_t rdlen) { - size_t rdpos = 0; - int i; - for(i=0; irdata_count; i++) { - if(rdata_atom_is_domain(rr->type, i)) { - if(rdpos + domain_dname(rr->rdatas[i].domain)->name_size - > rdlen) - return 0; - if(memcmp(rdata+rdpos, - dname_name(domain_dname(rr->rdatas[i].domain)), - domain_dname(rr->rdatas[i].domain)->name_size) - != 0) - return 0; - rdpos += domain_dname(rr->rdatas[i].domain)->name_size; - } else { - if(rdpos + rr->rdatas[i].data[0] > rdlen) - return 0; - if(memcmp(rdata+rdpos, &rr->rdatas[i].data[1], - rr->rdatas[i].data[0]) != 0) - return 0; - rdpos += rr->rdatas[i].data[0]; - } - } - if(rdpos != rdlen) - return 0; - return 1; + /* The rr has in-memory representation. The rdata is uncompressed + * wireformat representation. */ + const struct nsd_type_descriptor *descriptor = nsd_type_descriptor( + rr->type); + return equal_rr_rdata_uncompressed_wire(descriptor, rr, rdata, rdlen); } /* find an rdata in an rrset, true if found and sets index found */ @@ -372,9 +334,9 @@ static int rrset_find_rdata(struct rrset { int i; for(i=0; irr_count; i++) { - if(rrset->rrs[i].ttl != ttl) + if(rrset->rrs[i]->ttl != ttl) continue; - if(rdata_match(&rrset->rrs[i], rdata, rdlen)) { + if(rdata_match(rrset->rrs[i], rdata, rdlen)) { *index = i; return 1; } @@ -457,10 +419,7 @@ static int process_diff_rrset(FILE* spoo continue; } /* not in the marked list, the RR is added */ - if(!ixfr_store_addrr_rdatas(store, domain_dname(domain), - rrset->rrs[i].type, rrset->rrs[i].klass, - rrset->rrs[i].ttl, rrset->rrs[i].rdatas, - rrset->rrs[i].rdata_count)) { + if(!ixfr_store_addrr_rdatas(store, rrset->rrs[i])) { log_msg(LOG_ERR, "out of memory"); return 0; } @@ -511,14 +470,11 @@ static int process_spool_delrrset(FILE* /* add the rrset to the added list */ static int process_add_rrset(struct ixfr_store* ixfr_store, - struct domain* domain, struct rrset* rrset) + struct rrset* rrset) { int i; for(i=0; irr_count; i++) { - if(!ixfr_store_addrr_rdatas(ixfr_store, domain_dname(domain), - rrset->rrs[i].type, rrset->rrs[i].klass, - rrset->rrs[i].ttl, rrset->rrs[i].rdatas, - rrset->rrs[i].rdata_count)) { + if(!ixfr_store_addrr_rdatas(ixfr_store, rrset->rrs[i])) { log_msg(LOG_ERR, "out of memory"); return 0; } @@ -543,7 +499,7 @@ static int process_marktypes(struct ixfr /* the item is in the marked list, skip it */ continue; } - if(!process_add_rrset(store, domain, s)) + if(!process_add_rrset(store, s)) return 0; } return 1; @@ -615,7 +571,7 @@ static int process_domain_add_RRs(struct for(s=domain->rrsets; s; s=s->next) { if(s->zone != zone) continue; - if(!process_add_rrset(store, domain, s)) + if(!process_add_rrset(store, s)) return 0; } return 1; @@ -920,10 +876,7 @@ static int ixfr_create_store_newsoa(stru log_msg(LOG_ERR, "error empty SOA rrset"); return 0; } - if(!ixfr_store_add_newsoa_rdatas(store, domain_dname(zone->apex), - zone->soa_rrset->rrs[0].type, zone->soa_rrset->rrs[0].klass, - zone->soa_rrset->rrs[0].ttl, zone->soa_rrset->rrs[0].rdatas, - zone->soa_rrset->rrs[0].rdata_count)) { + if(!ixfr_store_add_newsoa_rdatas(store, zone->soa_rrset->rrs[0])) { log_msg(LOG_ERR, "out of memory"); return 0; } Index: usr.sbin/nsd/metrics.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/metrics.c,v diff -u -p -r1.1.1.1 metrics.c --- usr.sbin/nsd/metrics.c 6 Sep 2025 17:38:33 -0000 1.1.1.1 +++ usr.sbin/nsd/metrics.c 21 Sep 2026 16:29:07 -0000 @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include "nsd.h" #include "xfrd.h" @@ -95,6 +97,7 @@ void daemon_metrics_close(struct daemon_ if (metrics->http_server) { evhttp_free(metrics->http_server); + metrics->http_server = NULL; } } @@ -291,6 +294,10 @@ daemon_metrics_attach(struct daemon_metr metrics->xfrd = xfrd; metrics->http_server = evhttp_new(xfrd->event_base); + if(!metrics->http_server) { + log_msg(LOG_ERR, "metrics: out of memory in evhttp_new"); + return; + } for(p = metrics->accept_list; p; p = p->next) { fd = p->accept_fd; if (evhttp_accept_socket(metrics->http_server, fd)) { @@ -343,33 +350,88 @@ metrics_http_callback(struct evhttp_requ } #ifdef BIND8_STATS -/** print long number*/ -static int -print_longnum(struct evbuffer *buf, char* desc, uint64_t x) -{ - if(x > (uint64_t)1024*1024*1024) { - /*more than a Gb*/ - size_t front = (size_t)(x / (uint64_t)1000000); - size_t back = (size_t)(x % (uint64_t)1000000); - return evbuffer_add_printf(buf, "%s%lu%6.6lu\n", desc, - (unsigned long)front, (unsigned long)back); +#define METRIC_MAX_LABELS 2 + +struct metrics_metric { + const char *prefix; + const char *name; + const char *type; + size_t label_count; + const char *label_names[METRIC_MAX_LABELS]; + const char *label_values[METRIC_MAX_LABELS]; +}; + +static void +metric_init_with_prefix(struct metrics_metric *metric, const char *prefix) { + metric->prefix = prefix; + metric->name = ""; + metric->label_count = 0; +} + +static void +metric_set_name_and_type(struct metrics_metric *metric, const char *name, const char *type) { + metric->name = name; + metric->type = type; +} + +/* Add a `name="value"` label to the metric. + * The value must not contain `\`, `\n`, or `"`. */ +static void +metric_push_label(struct metrics_metric *metric, const char *name, const char *value) { + assert(metric->label_count < METRIC_MAX_LABELS); + metric->label_names[metric->label_count] = name; + metric->label_values[metric->label_count] = value; + metric->label_count++; +} + +static void +metric_pop_label(struct metrics_metric *metric) { + assert(metric->label_count > 0); + metric->label_count--; +} + +static void +metric_print(struct metrics_metric *metric, struct evbuffer *buf, uint64_t value) { + evbuffer_add_printf(buf, "%s%s", metric->prefix, metric->name); + for (size_t i = 0; i < metric->label_count; i++) { + evbuffer_add_printf(buf, "%c%s=\"%s\"", + i == 0 ? '{' : ',', + metric->label_names[i], + metric->label_values[i]); + } + if (metric->label_count > 0) { + evbuffer_add_printf(buf, "} %" PRIu64 "\n", value); } else { - return evbuffer_add_printf(buf, "%s%lu\n", desc, (unsigned long)x); + evbuffer_add_printf(buf, " %" PRIu64 "\n", value); } } +/** Print a metric value of `integral + decimals_micro * 1e-6`. */ static void -print_metric_help_and_type(struct evbuffer *buf, char *prefix, char *name, - char *help, char *type) +metric_print_micros(struct metrics_metric *metric, struct evbuffer *buf, + unsigned long integral, unsigned long decimals_micro) { + assert(metric->label_count == 0 + && "metric_print_micros is not implemented for metrics with labels"); + evbuffer_add_printf(buf, "%s%s %lu.%6.6lu\n", + metric->prefix, metric->name, integral, decimals_micro); +} + +static void +metric_print_pop(struct metrics_metric *metric, struct evbuffer *buf, uint64_t value) { + metric_print(metric, buf, value); + metric_pop_label(metric); +} + +static void +metric_print_help(struct metrics_metric *metric, struct evbuffer *buf, const char *help) { evbuffer_add_printf(buf, "# HELP %s%s %s\n# TYPE %s%s %s\n", - prefix, name, help, prefix, name, type); + metric->prefix, metric->name, help, + metric->prefix, metric->name, metric->type); } static void -print_stat_block(struct evbuffer *buf, struct nsdst* st, - char *name) -{ +print_stat_block(struct evbuffer *buf, struct nsdst* st, struct metrics_metric *metric) { size_t i; const char* rcstr[] = {"NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", @@ -378,139 +440,139 @@ print_stat_block(struct evbuffer *buf, s "BADVERS" }; - char prefix[512] = {0}; - if (name) { - snprintf(prefix, sizeof(prefix), "nsd_zonestats_%s_", name); - } else { - snprintf(prefix, sizeof(prefix), "nsd_"); - } - /* nsd_queries_by_type_total */ - print_metric_help_and_type(buf, prefix, "queries_by_type_total", - "Total number of queries received by type.", - "counter"); + metric_set_name_and_type(metric, "queries_by_type_total", "counter"); + metric_print_help(metric, buf, "Total number of queries received by type."); for(i=0; i<= 255; i++) { if(metrics_inhibit_zero && st->qtype[i] == 0 && strncmp(rrtype_to_string(i), "TYPE", 4) == 0) continue; - evbuffer_add_printf(buf, "%squeries_by_type_total{type=\"%s\"} %lu\n", - prefix, rrtype_to_string(i), (unsigned long)st->qtype[i]); + metric_push_label(metric, "type", rrtype_to_string(i)); + metric_print_pop(metric, buf, (uint64_t)st->qtype[i]); } /* nsd_queries_by_class_total */ - print_metric_help_and_type(buf, prefix, "queries_by_class_total", - "Total number of queries received by class.", - "counter"); + metric_set_name_and_type(metric, "queries_by_class_total", "counter"); + metric_print_help(metric, buf, "Total number of queries received by class."); for(i=0; i<4; i++) { if(metrics_inhibit_zero && st->qclass[i] == 0 && i != CLASS_IN) continue; - evbuffer_add_printf(buf, "%squeries_by_class_total{class=\"%s\"} %lu\n", - prefix, rrclass_to_string(i), (unsigned long)st->qclass[i]); + metric_push_label(metric, "class", rrclass_to_string(i)); + metric_print_pop(metric, buf, (uint64_t)st->qclass[i]); } /* nsd_queries_by_opcode_total */ - print_metric_help_and_type(buf, prefix, "queries_by_opcode_total", - "Total number of queries received by opcode.", - "counter"); + metric_set_name_and_type(metric, "queries_by_opcode_total", "counter"); + metric_print_help(metric, buf, "Total number of queries received by opcode."); for(i=0; i<6; i++) { if(metrics_inhibit_zero && st->opcode[i] == 0 && i != OPCODE_QUERY) continue; - evbuffer_add_printf(buf, "%squeries_by_opcode_total{opcode=\"%s\"} %lu\n", - prefix, opcode2str(i), (unsigned long)st->opcode[i]); + metric_push_label(metric, "opcode", opcode2str(i)); + metric_print_pop(metric, buf, (uint64_t)st->opcode[i]); } /* nsd_queries_by_rcode_total */ - print_metric_help_and_type(buf, prefix, "queries_by_rcode_total", - "Total number of queries received by rcode.", - "counter"); + metric_set_name_and_type(metric, "queries_by_rcode_total", "counter"); + metric_print_help(metric, buf, "Total number of queries received by rcode."); for(i=0; i<17; i++) { if(metrics_inhibit_zero && st->rcode[i] == 0 && i > RCODE_YXDOMAIN) /*NSD does not use larger*/ continue; - evbuffer_add_printf(buf, "%squeries_by_rcode_total{rcode=\"%s\"} %lu\n", - prefix, rcstr[i], (unsigned long)st->rcode[i]); + metric_push_label(metric, "rcode", rcstr[i]); + metric_print_pop(metric, buf, (uint64_t)st->rcode[i]); } /* nsd_queries_by_transport_total */ - print_metric_help_and_type(buf, prefix, "queries_by_transport_total", - "Total number of queries received by transport.", - "counter"); - evbuffer_add_printf(buf, "%squeries_by_transport_total{transport=\"udp\"} %lu\n", prefix, (unsigned long)st->qudp); - evbuffer_add_printf(buf, "%squeries_by_transport_total{transport=\"udp6\"} %lu\n", prefix, (unsigned long)st->qudp6); + metric_set_name_and_type(metric, "queries_by_transport_total", "counter"); + metric_print_help(metric, buf, "Total number of queries received by transport."); + metric_push_label(metric, "transport", "udp"); + metric_print_pop(metric, buf, (uint64_t)st->qudp); + metric_push_label(metric, "transport", "udp6"); + metric_print_pop(metric, buf, (uint64_t)st->qudp6); /* nsd_queries_with_edns_total */ - print_metric_help_and_type(buf, prefix, "queries_with_edns_total", - "Total number of queries received with EDNS OPT.", - "counter"); - evbuffer_add_printf(buf, "%squeries_with_edns_total %lu\n", prefix, (unsigned long)st->edns); + metric_set_name_and_type(metric, "queries_with_edns_total", "counter"); + metric_print_help(metric, buf, "Total number of queries received with EDNS OPT."); + metric_print(metric, buf, (uint64_t)st->edns); /* nsd_queries_with_edns_failed_total */ - print_metric_help_and_type(buf, prefix, "queries_with_edns_failed_total", - "Total number of queries received with EDNS OPT where EDNS parsing failed.", - "counter"); - evbuffer_add_printf(buf, "%squeries_with_edns_failed_total %lu\n", prefix, (unsigned long)st->ednserr); + metric_set_name_and_type(metric, "queries_with_edns_failed_total", "counter"); + metric_print_help(metric, buf, "Total number of queries received with EDNS OPT where EDNS parsing failed."); + metric_print(metric, buf, (uint64_t)st->ednserr); /* nsd_connections_total */ - print_metric_help_and_type(buf, prefix, "connections_total", - "Total number of connections.", - "counter"); - evbuffer_add_printf(buf, "%sconnections_total{transport=\"tcp\"} %lu\n", prefix, (unsigned long)st->ctcp); - evbuffer_add_printf(buf, "%sconnections_total{transport=\"tcp6\"} %lu\n", prefix, (unsigned long)st->ctcp6); - evbuffer_add_printf(buf, "%sconnections_total{transport=\"tls\"} %lu\n", prefix, (unsigned long)st->ctls); - evbuffer_add_printf(buf, "%sconnections_total{transport=\"tls6\"} %lu\n", prefix, (unsigned long)st->ctls6); + metric_set_name_and_type(metric, "connections_total", "counter"); + metric_print_help(metric, buf, "Total number of connections."); + metric_push_label(metric, "transport", "tcp"); + metric_print_pop(metric, buf, (uint64_t)st->ctcp); + metric_push_label(metric, "transport", "tcp6"); + metric_print_pop(metric, buf, (uint64_t)st->ctcp6); + metric_push_label(metric, "transport", "tls"); + metric_print_pop(metric, buf, (uint64_t)st->ctls); + metric_push_label(metric, "transport", "tls6"); + metric_print_pop(metric, buf, (uint64_t)st->ctls6); /* nsd_xfr_requests_served_total */ - print_metric_help_and_type(buf, prefix, "xfr_requests_served_total", - "Total number of answered zone transfers.", - "counter"); - evbuffer_add_printf(buf, "%sxfr_requests_served_total{xfrtype=\"AXFR\"} %lu\n", prefix, (unsigned long)st->raxfr); - evbuffer_add_printf(buf, "%sxfr_requests_served_total{xfrtype=\"IXFR\"} %lu\n", prefix, (unsigned long)st->rixfr); + metric_set_name_and_type(metric, "xfr_requests_served_total", "counter"); + metric_print_help(metric, buf, "Total number of answered zone transfers."); + metric_push_label(metric, "xfrtype", "AXFR"); + metric_print_pop(metric, buf, (uint64_t)st->raxfr); + metric_push_label(metric, "xfrtype", "IXFR"); + metric_print_pop(metric, buf, (uint64_t)st->rixfr); /* nsd_queries_dropped_total */ - print_metric_help_and_type(buf, prefix, "queries_dropped_total", - "Total number of dropped queries.", - "counter"); - evbuffer_add_printf(buf, "%squeries_dropped_total %lu\n", prefix, (unsigned long)st->dropped); + metric_set_name_and_type(metric, "queries_dropped_total", "counter"); + metric_print_help(metric, buf, "Total number of dropped queries."); + metric_print(metric, buf, (uint64_t)st->dropped); /* nsd_queries_rx_failed_total */ - print_metric_help_and_type(buf, prefix, "queries_rx_failed_total", - "Total number of queries where receive failed.", - "counter"); - evbuffer_add_printf(buf, "%squeries_rx_failed_total %lu\n", prefix, (unsigned long)st->rxerr); + metric_set_name_and_type(metric, "queries_rx_failed_total", "counter"); + metric_print_help(metric, buf, "Total number of queries where receive failed."); + metric_print(metric, buf, (uint64_t)st->rxerr); /* nsd_answers_tx_failed_total */ - print_metric_help_and_type(buf, prefix, "answers_tx_failed_total", - "Total number of answers where transmit failed.", - "counter"); - evbuffer_add_printf(buf, "%sanswers_tx_failed_total %lu\n", prefix, (unsigned long)st->txerr); + metric_set_name_and_type(metric, "answers_tx_failed_total", "counter"); + metric_print_help(metric, buf, "Total number of answers where transmit failed."); + metric_print(metric, buf, (uint64_t)st->txerr); /* nsd_answers_without_aa_total */ - print_metric_help_and_type(buf, prefix, "answers_without_aa_total", - "Total number of NOERROR answers without AA flag set.", - "counter"); - evbuffer_add_printf(buf, "%sanswers_without_aa_total %lu\n", prefix, (unsigned long)st->nona); + metric_set_name_and_type(metric, "answers_without_aa_total", "counter"); + metric_print_help(metric, buf, "Total number of NOERROR answers without AA flag set."); + metric_print(metric, buf, (uint64_t)st->nona); /* nsd_answers_truncated_total */ - print_metric_help_and_type(buf, prefix, "answers_truncated_total", - "Total number of truncated answers.", - "counter"); - evbuffer_add_printf(buf, "%sanswers_truncated_total %lu\n", prefix, (unsigned long)st->truncated); + metric_set_name_and_type(metric, "answers_truncated_total", "counter"); + metric_print_help(metric, buf, "Total number of truncated answers."); + metric_print(metric, buf, (uint64_t)st->truncated); } #ifdef USE_ZONE_STATS + void metrics_zonestat_print_one(struct evbuffer *buf, char *name, struct nsdst *zst) { - char prefix[512] = {0}; - snprintf(prefix, sizeof(prefix), "nsd_zonestats_%s_", name); + char* name_valid; + struct metrics_metric metric; + metric_init_with_prefix(&metric, "nsd_zonestats_"); + + /* The zonestat name can contain characters like '"' that would break the + * label value; replace them. */ + name_valid = strdup(name); + if (!name_valid) { + log_msg(LOG_ERR, "malloc failure"); + return; + } + metrics_make_label_value_valid(name_valid); + metric_push_label(&metric, "zone", name_valid); - print_metric_help_and_type(buf, prefix, "queries_total", - "Total number of queries received.", "counter"); - evbuffer_add_printf(buf, "nsd_zonestats_%s_queries_total %lu\n", name, - (unsigned long)(zst->qudp + zst->qudp6 + zst->ctcp + + metric_set_name_and_type(&metric, "queries_total", "counter"); + metric_print_help(&metric, buf, "Total number of queries received."); + metric_print(&metric, buf, + (uint64_t)(zst->qudp + zst->qudp6 + zst->ctcp + zst->ctcp6 + zst->ctls + zst->ctls6)); - print_stat_block(buf, zst, name); + print_stat_block(buf, zst, &metric); + free(name_valid); } #endif /*USE_ZONE_STATS*/ @@ -521,23 +583,28 @@ metrics_print_stats(struct evbuffer *buf { size_t i; struct timeval elapsed, uptime; + struct metrics_metric metric; + char server_str[16] = {0}; + + metric_init_with_prefix(&metric, "nsd_"); /* nsd_queries_total */ - print_metric_help_and_type(buf, "nsd_", "queries_total", - "Total number of queries received.", "counter"); + metric_set_name_and_type(&metric, "queries_total", "counter"); + metric_print_help(&metric, buf, "Total number of queries received."); /*per CPU and total*/ for(i=0; insd->child_count; i++) { - evbuffer_add_printf(buf, "nsd_queries_total{server=\"%d\"} %lu\n", - (int)i, (unsigned long)xfrd->nsd->children[i].query_count); + sprintf(server_str, "%d", (int)i); + metric_push_label(&metric, "server", server_str); + metric_print_pop(&metric, buf, (uint64_t)xfrd->nsd->children[i].query_count); } - print_stat_block(buf, st, NULL); + print_stat_block(buf, st, &metric); /* uptime (in seconds) */ timeval_subtract(&uptime, now, &xfrd->nsd->metrics->boot_time); - print_metric_help_and_type(buf, "nsd_", "time_up_seconds_total", - "Uptime since server boot in seconds.", "counter"); - evbuffer_add_printf(buf, "nsd_time_up_seconds_total %lu.%6.6lu\n", + metric_set_name_and_type(&metric, "time_up_seconds_total", "counter"); + metric_print_help(&metric, buf, "Uptime since server boot in seconds."); + metric_print_micros(&metric, buf, (unsigned long)uptime.tv_sec, (unsigned long)uptime.tv_usec); /* time elapsed since last nsd-control stats reset (in seconds) */ @@ -548,48 +615,43 @@ metrics_print_stats(struct evbuffer *buf } else { timeval_subtract(&elapsed, now, &xfrd->nsd->metrics->stats_time); } - print_metric_help_and_type(buf, "nsd_", "time_elapsed_seconds", - "Time since last statistics printout and " - "reset (by nsd-control stats) in seconds.", - "untyped"); - evbuffer_add_printf(buf, "nsd_time_elapsed_seconds %lu.%6.6lu\n", + metric_set_name_and_type(&metric, "time_elapsed_seconds", "untyped"); + metric_print_help(&metric, buf, + "Time since last statistics printout and " + "reset (by nsd-control stats) in seconds."); + metric_print_micros(&metric, buf, (unsigned long)elapsed.tv_sec, (unsigned long)elapsed.tv_usec); /*mem info, database on disksize*/ - print_metric_help_and_type(buf, "nsd_", "size_db_on_disk_bytes", - "Size of DNS database on disk.", "gauge"); - print_longnum(buf, "nsd_size_db_on_disk_bytes ", st->db_disk); - - print_metric_help_and_type(buf, "nsd_", "size_db_in_mem_bytes", - "Size of DNS database in memory.", "gauge"); - print_longnum(buf, "nsd_size_db_in_mem_bytes ", st->db_mem); - - print_metric_help_and_type(buf, "nsd_", "size_xfrd_in_mem_bytes", - "Size of zone transfers and notifies in xfrd process, excluding TSIG data.", - "gauge"); - print_longnum(buf, "nsd_size_xfrd_in_mem_bytes ", region_get_mem(xfrd->region)); - - print_metric_help_and_type(buf, "nsd_", "size_config_on_disk_bytes", - "Size of zonelist file on disk, excluding nsd.conf.", - "gauge"); - print_longnum(buf, "nsd_size_config_on_disk_bytes ", - xfrd->nsd->options->zonelist_off); - - print_metric_help_and_type(buf, "nsd_", "size_config_in_mem_bytes", - "Size of config data in memory.", "gauge"); - print_longnum(buf, "nsd_size_config_in_mem_bytes ", region_get_mem( - xfrd->nsd->options->region)); + metric_set_name_and_type(&metric, "size_db_on_disk_bytes", "gauge"); + metric_print_help(&metric, buf, "Size of DNS database on disk."); + metric_print(&metric, buf, st->db_disk); + + metric_set_name_and_type(&metric, "size_db_in_mem_bytes", "gauge"); + metric_print_help(&metric, buf, "Size of DNS database in memory."); + metric_print(&metric, buf, st->db_mem); + + metric_set_name_and_type(&metric, "size_xfrd_in_mem_bytes", "gauge"); + metric_print_help(&metric, buf, "Size of zone transfers and notifies in xfrd process, excluding TSIG data."); + metric_print(&metric, buf, region_get_mem(xfrd->region)); + + metric_set_name_and_type(&metric, "size_config_on_disk_bytes", "gauge"); + metric_print_help(&metric, buf, "Size of zonelist file on disk, excluding nsd.conf."); + metric_print(&metric, buf, xfrd->nsd->options->zonelist_off); + + metric_set_name_and_type(&metric, "size_config_in_mem_bytes", "gauge"); + metric_print_help(&metric, buf, "Size of config data in memory."); + metric_print(&metric, buf, region_get_mem(xfrd->nsd->options->region)); /* number of zones serverd */ - print_metric_help_and_type(buf, "nsd_", "zones_primary", - "Number of primary zones served.", "gauge"); - evbuffer_add_printf(buf, "nsd_zones_primary %lu\n", - (unsigned long)(xfrd->notify_zones->count - xfrd->zones->count)); - - print_metric_help_and_type(buf, "nsd_", "zones_secondary", - "Number of secondary zones served.", "gauge"); - evbuffer_add_printf(buf, "nsd_zones_secondary %lu\n", - (unsigned long)xfrd->zones->count); + metric_set_name_and_type(&metric, "zones_primary", "gauge"); + metric_print_help(&metric, buf, "Number of primary zones served."); + metric_print(&metric, buf, + (uint64_t)(xfrd->notify_zones->count - xfrd->zones->count)); + + metric_set_name_and_type(&metric, "zones_secondary", "gauge"); + metric_print_help(&metric, buf, "Number of secondary zones served."); + metric_print(&metric, buf, (uint64_t)xfrd->zones->count); #ifdef USE_ZONE_STATS zonestat_print(NULL, buf, xfrd, clear, zonestats); /*per-zone statistics*/ Index: usr.sbin/nsd/metrics.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/metrics.h,v diff -u -p -r1.1.1.1 metrics.h --- usr.sbin/nsd/metrics.h 6 Sep 2025 17:38:33 -0000 1.1.1.1 +++ usr.sbin/nsd/metrics.h 21 Sep 2026 16:29:07 -0000 @@ -60,6 +60,7 @@ int daemon_metrics_open_ports(struct dae void daemon_metrics_attach(struct daemon_metrics* m, struct xfrd_state* xfrd); #ifdef BIND8_STATS + /** * Print stats as prometheus metrics to HTTP buffer * @param buf: the HTTP buffer to write to @@ -77,6 +78,35 @@ void metrics_print_stats(struct evbuffer struct timeval *rc_stats_time); #ifdef USE_ZONE_STATS + +/** + * Replace characters disallowed in Prometheus label values with '_'. + * + * According to [1], label values can be any UTF-8 characters, but backslash, + * double-quote, and line feed must be escaped. We could escape them but that + * changes the length of the string, and in practice for zonestats names you + * don't need these characters anyway so we just replace them. + * [1]: https://prometheus.io/docs/instrumenting/exposition_formats/#comments-help-text-and-type-information> + * + * @param value: the label value to edit. + * @return 1 if any changes were needed, 0 if the value was already valid. + */ +static inline int metrics_make_label_value_valid(char* value) { + int made_changes = 0; + char* s = value; + while(*s) { + switch (*s) { + case '\\': + case '"': + case '\n': + *s = '_'; + made_changes = 1; + } + s++; + } + return made_changes; +} + /** * Print zonestat metrics for a single zonestats object * @param buf: the HTTP buffer to write to Index: usr.sbin/nsd/namedb.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/namedb.c,v diff -u -p -r1.16 namedb.c --- usr.sbin/nsd/namedb.c 6 Sep 2025 17:41:37 -0000 1.16 +++ usr.sbin/nsd/namedb.c 21 Sep 2026 16:29:07 -0000 @@ -160,7 +160,7 @@ int domain_is_prehash(domain_table_type* void zone_del_domain_in_hash_tree(rbtree_type* tree, rbnode_type* node) { - if(!node->key) + if(!node->key || !tree) return; rbtree_delete(tree, node->key); /* note that domain is no longer in the tree */ @@ -226,20 +226,36 @@ do_deldomain(namedb_type* db, domain_typ /* see if nsec3-nodes are used */ if(domain->nsec3) { - if(domain->nsec3->nsec3_node.key) - zone_del_domain_in_hash_tree(nsec3_tree_zone(db, domain) - ->nsec3tree, &domain->nsec3->nsec3_node); + if(domain->nsec3->nsec3_node.key) { + zone_type* nsec3zone = nsec3_tree_zone(db, domain); + if(nsec3zone) + zone_del_domain_in_hash_tree( + nsec3zone->nsec3tree, + &domain->nsec3->nsec3_node); + } if(domain->nsec3->hash_wc) { - if(domain->nsec3->hash_wc->hash.node.key) - zone_del_domain_in_hash_tree(nsec3_tree_zone(db, domain) - ->hashtree, &domain->nsec3->hash_wc->hash.node); - if(domain->nsec3->hash_wc->wc.node.key) - zone_del_domain_in_hash_tree(nsec3_tree_zone(db, domain) - ->wchashtree, &domain->nsec3->hash_wc->wc.node); - } - if(domain->nsec3->ds_parent_hash && domain->nsec3->ds_parent_hash->node.key) - zone_del_domain_in_hash_tree(nsec3_tree_dszone(db, domain) - ->dshashtree, &domain->nsec3->ds_parent_hash->node); + if(domain->nsec3->hash_wc->hash.node.key) { + zone_type* hzone = nsec3_tree_zone(db, domain); + if(hzone) + zone_del_domain_in_hash_tree( + hzone->hashtree, + &domain->nsec3->hash_wc->hash.node); + } + if(domain->nsec3->hash_wc->wc.node.key) { + zone_type* wczone = nsec3_tree_zone(db, domain); + if(wczone) + zone_del_domain_in_hash_tree( + wczone->wchashtree, + &domain->nsec3->hash_wc->wc.node); + } + } + if(domain->nsec3->ds_parent_hash && domain->nsec3->ds_parent_hash->node.key) { + zone_type* dszone = nsec3_tree_dszone(db, domain); + if(dszone) + zone_del_domain_in_hash_tree( + dszone->dshashtree, + &domain->nsec3->ds_parent_hash->node); + } if(domain->nsec3->hash_wc) { region_recycle(db->domains->region, domain->nsec3->hash_wc, @@ -304,7 +320,10 @@ void zone_add_domain_in_hash_tree(region return; memset(node, 0, sizeof(rbnode_type)); node->key = domain; - rbtree_insert(*tree, node); + if(!rbtree_insert(*tree, node)) { + /* collision: clear key to prevent null content key in node. */ + node->key = NULL; + } } domain_table_type * @@ -539,6 +558,24 @@ domain_find_any_rrset(domain_type* domai return NULL; } +rrset_type * +domain_find_rrset_and_prev(domain_type* domain, zone_type* zone, uint16_t type, + rrset_type** prev) +{ + rrset_type* result = domain->rrsets, *prevp = NULL; + + while (result) { + if (result->zone == zone && rrset_rrtype(result) == type) { + *prev = prevp; + return result; + } + prevp = result; + result = result->next; + } + *prev = NULL; + return NULL; +} + zone_type * domain_find_zone(namedb_type* db, domain_type* domain) { @@ -647,11 +684,11 @@ zone_is_secure(zone_type* zone) uint16_t rr_rrsig_type_covered(rr_type* rr) { + uint16_t type; assert(rr->type == TYPE_RRSIG); - assert(rr->rdata_count > 0); - assert(rdata_atom_size(rr->rdatas[0]) == sizeof(uint16_t)); - - return ntohs(* (uint16_t *) rdata_atom_data(rr->rdatas[0])); + assert(rr->rdlength > 2); + memcpy(&type, rr->rdata, sizeof(type)); + return ntohs(type); } zone_type * @@ -721,7 +758,7 @@ rr_type *zone_rr_iter_next(struct zone_r while(iter->rrset != NULL) { if(iter->index < iter->rrset->rr_count) { - return &iter->rrset->rrs[iter->index++]; + return iter->rrset->rrs[iter->index++]; } iter->index = 0; if(iter->domain == NULL) { Index: usr.sbin/nsd/namedb.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/namedb.h,v diff -u -p -r1.19 namedb.h --- usr.sbin/nsd/namedb.h 6 Sep 2025 17:41:37 -0000 1.19 +++ usr.sbin/nsd/namedb.h 21 Sep 2026 16:29:07 -0000 @@ -23,7 +23,6 @@ struct udb_ptr; struct nsd; struct zone_ixfr; -typedef union rdata_atom rdata_atom_type; typedef struct rrset rrset_type; typedef struct rr rr_type; @@ -157,14 +156,25 @@ struct zone unsigned is_bad : 1; /* zone failed verification */ } ATTR_PACKED; -/* a RR in DNS */ +/* a RR in DNS. + * The RDATA is directly allocated with the RR structure and pointers to + * domains are directly stored (likely unaligned) in the RDATA. We do this + * primarily to save memory, but as the RDATA is directly stored with the RR, + * it is likely that data is cached with the RR which should have a positive + * impact on performance. When paired with direct conversion routines we are + * very likely to improve performance when importing zones and writing RRs out + * to the wire. That is in the rr.rdata array, it contains the rdata without + * the rdlength bytes. The rdlength contains the length of the rr.rdata array, + * that includes the pointer size for names that are pointer references to + * struct domain. + */ struct rr { domain_type* owner; - rdata_atom_type* rdatas; uint32_t ttl; uint16_t type; uint16_t klass; - uint16_t rdata_count; + uint16_t rdlength; + uint8_t rdata[]; /* c99 flexible array */ } ATTR_PACKED; /* @@ -175,25 +185,30 @@ struct rrset { rrset_type* next; zone_type* zone; - rr_type* rrs; +#ifndef PACKED_STRUCTS + /* If the storage is not packed, there is a pointer to a separate + * array of rr_type pointers. This is then accessed in an aligned + * manner. + * If the storage is packed, the pointer to the array is not there. + * The rr_type pointers for the rrs are stored after the struct rrset, + * contiguously allocated. This is then unaligned pointers, as they + * are after the packed structure. The region also stores items when + * packed structs is enabled without alignment, and thus there are + * unaligned accesses. It then has unaligned access for like struct + * domain. So unaligned access is used then, here too. This saves + * the space used by the rr_type** rrs array pointer. + * The rr pointers can then be accessed through the rrs[] member and, + * that is an unaligned access, and does not need an accessor function. + */ + rr_type** rrs; +#endif uint16_t rr_count; +#ifdef PACKED_STRUCTS + rr_type* rrs[]; /* c99 flexible array */ +#endif } ATTR_PACKED; /* - * The field used is based on the wireformat the atom is stored in. - * The allowed wireformats are defined by the rdata_wireformat_type - * enumeration. - */ -union rdata_atom -{ - /* RDATA_WF_COMPRESSED_DNAME, RDATA_WF_UNCOMPRESSED_DNAME */ - domain_type* domain; - - /* Default. */ - uint16_t* data; -}; - -/* * Create a new domain_table containing only the root domain. */ domain_table_type *domain_table_create(region_type *region); @@ -256,6 +271,8 @@ void domain_add_rrset(domain_type* domai rrset_type* domain_find_rrset(domain_type* domain, zone_type* zone, uint16_t type); rrset_type* domain_find_any_rrset(domain_type* domain, zone_type* zone); +rrset_type* domain_find_rrset_and_prev(domain_type* domain, zone_type* zone, + uint16_t type, rrset_type** prev); zone_type* domain_find_zone(namedb_type* db, domain_type* domain); zone_type* domain_find_parent_zone(namedb_type* db, zone_type* zone); @@ -345,28 +362,6 @@ struct namedb off_t diff_pos; }; -static inline int rdata_atom_is_domain(uint16_t type, size_t index); -static inline int rdata_atom_is_literal_domain(uint16_t type, size_t index); - -static inline domain_type * -rdata_atom_domain(rdata_atom_type atom) -{ - return atom.domain; -} - -static inline uint16_t -rdata_atom_size(rdata_atom_type atom) -{ - return *atom.data; -} - -static inline uint8_t * -rdata_atom_data(rdata_atom_type atom) -{ - return (uint8_t *) (atom.data + 1); -} - - /* Find the zone for the specified dname in DB. */ zone_type *namedb_find_zone(namedb_type *db, const dname_type *dname); /* @@ -379,8 +374,7 @@ void domain_table_deldomain(namedb_type* /** dbcreate.c */ int print_rrs(FILE* out, struct zone* zone); -/** marshal rdata into buffer, must be MAX_RDLENGTH in size */ -size_t rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz); + /* dbaccess.c */ int namedb_lookup (struct namedb* db, const dname_type* dname, @@ -413,40 +407,12 @@ int create_dirs(const char* path); int file_get_mtime(const char* file, struct timespec* mtime, int* nonexist); void allocate_domain_nsec3(domain_table_type *table, domain_type *result); -static inline int -rdata_atom_is_domain(uint16_t type, size_t index) -{ - const rrtype_descriptor_type *descriptor - = rrtype_descriptor_by_type(type); - return (index < descriptor->maximum - && (descriptor->wireformat[index] == RDATA_WF_COMPRESSED_DNAME - || descriptor->wireformat[index] == RDATA_WF_UNCOMPRESSED_DNAME)); -} - -static inline int -rdata_atom_is_literal_domain(uint16_t type, size_t index) -{ - const rrtype_descriptor_type *descriptor - = rrtype_descriptor_by_type(type); - return (index < descriptor->maximum - && (descriptor->wireformat[index] == RDATA_WF_LITERAL_DNAME)); -} - -static inline rdata_wireformat_type -rdata_atom_wireformat_type(uint16_t type, size_t index) -{ - const rrtype_descriptor_type *descriptor - = rrtype_descriptor_by_type(type); - assert(index < descriptor->maximum); - return (rdata_wireformat_type) descriptor->wireformat[index]; -} - static inline uint16_t rrset_rrtype(rrset_type* rrset) { assert(rrset); assert(rrset->rr_count > 0); - return rrset->rrs[0].type; + return rrset->rrs[0]->type; } static inline uint16_t @@ -454,7 +420,7 @@ rrset_rrclass(rrset_type* rrset) { assert(rrset); assert(rrset->rr_count > 0); - return rrset->rrs[0].klass; + return rrset->rrs[0]->klass; } /* Index: usr.sbin/nsd/nsd-checkconf.8.in =================================================================== RCS file: /cvs/src/usr.sbin/nsd/nsd-checkconf.8.in,v diff -u -p -r1.45 nsd-checkconf.8.in --- usr.sbin/nsd/nsd-checkconf.8.in 6 Sep 2025 17:41:37 -0000 1.45 +++ usr.sbin/nsd/nsd-checkconf.8.in 21 Sep 2026 16:29:07 -0000 @@ -1,4 +1,4 @@ -.TH "nsd\-checkconf" "8" "Sep 3, 2025" "NLnet Labs" "nsd 4.13.0" +.TH "nsd\-checkconf" "8" "sep 2, 2026" "NLnet Labs" "nsd 4.15.2" .\" Copyright (c) 2001\-2024, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" Index: usr.sbin/nsd/nsd-checkconf.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/nsd-checkconf.c,v diff -u -p -r1.39 nsd-checkconf.c --- usr.sbin/nsd/nsd-checkconf.c 6 Sep 2025 17:41:37 -0000 1.39 +++ usr.sbin/nsd/nsd-checkconf.c 21 Sep 2026 16:29:07 -0000 @@ -512,11 +512,17 @@ config_print_zone(nsd_options_type* opt, return; } if(strcasecmp(o, "proxy_protocol_port") == 0) { - struct proxy_protocol_port_list* p; + struct port_list* p; for(p = opt->proxy_protocol_port; p; p = p->next) printf("%d\n", p->port); return; } + if(strcasecmp(o, "udp_padding_port") == 0) { + struct port_list* p; + for(p = opt->udp_padding_port; p; p = p->next) + printf("%d\n", p->port); + return; + } printf("Server option not handled: %s\n", o); exit(1); } @@ -735,9 +741,14 @@ config_test_print_server(nsd_options_typ print_string_var("cookie-secret-file:", ""); } if(opt->proxy_protocol_port) { - struct proxy_protocol_port_list* p; + struct port_list* p; for(p = opt->proxy_protocol_port; p; p = p->next) printf("\tproxy-protocol-port: %d\n", p->port); + } + if(opt->udp_padding_port) { + struct port_list* p; + for(p = opt->udp_padding_port; p; p = p->next) + printf("\tudp-padding-port: %d\n", p->port); } #ifdef USE_METRICS Index: usr.sbin/nsd/nsd-checkzone.8.in =================================================================== RCS file: /cvs/src/usr.sbin/nsd/nsd-checkzone.8.in,v diff -u -p -r1.29 nsd-checkzone.8.in --- usr.sbin/nsd/nsd-checkzone.8.in 6 Sep 2025 17:41:37 -0000 1.29 +++ usr.sbin/nsd/nsd-checkzone.8.in 21 Sep 2026 16:29:07 -0000 @@ -1,4 +1,4 @@ -.TH "nsd\-checkzone" "8" "Sep 3, 2025" "NLnet Labs" "nsd 4.13.0" +.TH "nsd\-checkzone" "8" "sep 2, 2026" "NLnet Labs" "nsd 4.15.2" .\" Copyright (c) 2014-2024, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" Index: usr.sbin/nsd/nsd-control.8.in =================================================================== RCS file: /cvs/src/usr.sbin/nsd/nsd-control.8.in,v diff -u -p -r1.33 nsd-control.8.in --- usr.sbin/nsd/nsd-control.8.in 6 Sep 2025 17:41:37 -0000 1.33 +++ usr.sbin/nsd/nsd-control.8.in 21 Sep 2026 16:29:07 -0000 @@ -1,4 +1,4 @@ -.TH "nsd\-control" "8" "Sep 3, 2025" "NLnet Labs" "nsd 4.13.0" +.TH "nsd\-control" "8" "sep 2, 2026" "NLnet Labs" "nsd 4.15.2" .\" Copyright (c) 2011-2024, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" Index: usr.sbin/nsd/nsd-mem.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/nsd-mem.c,v diff -u -p -r1.9 nsd-mem.c --- usr.sbin/nsd/nsd-mem.c 6 Sep 2025 17:41:37 -0000 1.9 +++ usr.sbin/nsd/nsd-mem.c 21 Sep 2026 16:29:07 -0000 @@ -10,6 +10,7 @@ #include "config.h" #include +#include #include #include #include @@ -45,6 +46,8 @@ struct zone_mem { size_t data; /* unused space (in db.region) due to alignment */ size_t data_unused; + /* space in recycle_bin */ + size_t recycle_bin; /* count of number of domains */ size_t domaincount; @@ -56,6 +59,8 @@ struct tot_mem { size_t data; /* unused space (in db.region) due to alignment */ size_t data_unused; + /* space in recycle_bin */ + size_t recycle_bin; /* count of number of domains */ size_t domaincount; @@ -64,6 +69,8 @@ struct tot_mem { size_t opt_data; /* unused in options region */ size_t opt_unused; + /* space in recycle_bin */ + size_t opt_recycle_bin; /* dname compression table */ size_t compresstable; #ifdef RATELIMIT @@ -80,29 +87,55 @@ account_zone(struct namedb* db, struct z { zmem->data = region_get_mem(db->region); zmem->data_unused = region_get_mem_unused(db->region); + zmem->recycle_bin = region_get_recycle_size(db->region); zmem->domaincount = domain_table_count(db->domains); } +static char* +pretty_num(size_t x) +{ + static char buf[32]; + memset(buf, 0, sizeof(buf)); + if(snprintf(buf, sizeof(buf), "%12lld", (long long)x) <= 12) { + snprintf(buf, sizeof(buf), "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", + buf[0], buf[1], buf[2], (buf[2]==' '?' ':'.'), + buf[3], buf[4], buf[5], (buf[5]==' '?' ':'.'), + buf[6], buf[7], buf[8], (buf[8]==' '?' ':'.'), + buf[9], buf[10], buf[11]); + } + return buf; +} + +static const char* +skip_ws(const char* str) +{ + while (isspace(*str)) + str++; + return str; +} + static void pretty_mem(size_t x, const char* s) { - char buf[32]; - memset(buf, 0, sizeof(buf)); - if(snprintf(buf, sizeof(buf), "%12lld", (long long)x) > 12) { - printf("%12lld %s\n", (long long)x, s); - return; + printf("%s %s\n", pretty_num(x), s); +} + +static void +pretty_mem_recycle_bin(size_t x, const char* s, size_t recycle_bin_sz) +{ + printf("%s %s", pretty_num(x), s); + if (recycle_bin_sz) { + printf(" (of which %s in the recycle bin)", + skip_ws(pretty_num(recycle_bin_sz))); } - printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c %s\n", - buf[0], buf[1], buf[2], (buf[2]==' '?' ':'.'), - buf[3], buf[4], buf[5], (buf[5]==' '?' ':'.'), - buf[6], buf[7], buf[8], (buf[8]==' '?' ':'.'), - buf[9], buf[10], buf[11], s); + printf("\n"); } + static void print_zone_mem(struct zone_mem* z) { - pretty_mem(z->data, "zone data"); + pretty_mem_recycle_bin(z->data, "zone data", z->recycle_bin); pretty_mem(z->data_unused, "zone unused space (due to alignment)"); } @@ -111,6 +144,7 @@ account_total(struct nsd_options* opt, s { t->opt_data = region_get_mem(opt->region); t->opt_unused = region_get_mem_unused(opt->region); + t->opt_recycle_bin = region_get_recycle_size(opt->region); t->compresstable = sizeof(uint16_t) * (t->domaincount + 1 + EXTRA_DOMAIN_NUMBERS); t->compresstable *= opt->server_count; @@ -132,9 +166,9 @@ static void print_tot_mem(struct tot_mem* t) { printf("\ntotal\n"); - pretty_mem(t->data, "data"); + pretty_mem_recycle_bin(t->data, "data", t->recycle_bin); pretty_mem(t->data_unused, "unused space (due to alignment)"); - pretty_mem(t->opt_data, "options"); + pretty_mem_recycle_bin(t->opt_data, "options", t->opt_recycle_bin); pretty_mem(t->opt_unused, "options unused space (due to alignment)"); pretty_mem(t->compresstable, "name table (depends on servercount)"); #ifdef RATELIMIT @@ -150,6 +184,7 @@ add_mem(struct tot_mem* t, struct zone_m { t->data += z->data; t->data_unused += z->data_unused; + t->recycle_bin += z->recycle_bin; t->domaincount += z->domaincount; } Index: usr.sbin/nsd/nsd.8.in =================================================================== RCS file: /cvs/src/usr.sbin/nsd/nsd.8.in,v diff -u -p -r1.47 nsd.8.in --- usr.sbin/nsd/nsd.8.in 6 Sep 2025 17:41:37 -0000 1.47 +++ usr.sbin/nsd/nsd.8.in 21 Sep 2026 16:29:07 -0000 @@ -1,9 +1,9 @@ -.TH "NSD" "8" "Sep 3, 2025" "NLnet Labs" "NSD 4.13.0" +.TH "NSD" "8" "sep 2, 2026" "NLnet Labs" "NSD 4.15.2" .\" Copyright (c) 2001\-2024, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" .B nsd -\- Name Server Daemon (NSD) version 4.13.0. +\- Name Server Daemon (NSD) version 4.15.2. .SH "SYNOPSIS" .B nsd .RB [ \-4 ] Index: usr.sbin/nsd/nsd.conf.5.in =================================================================== RCS file: /cvs/src/usr.sbin/nsd/nsd.conf.5.in,v diff -u -p -r1.53 nsd.conf.5.in --- usr.sbin/nsd/nsd.conf.5.in 6 Sep 2025 17:41:37 -0000 1.53 +++ usr.sbin/nsd/nsd.conf.5.in 21 Sep 2026 16:29:07 -0000 @@ -1,4 +1,4 @@ -.TH "nsd.conf" "5" "Sep 3, 2025" "NLnet Labs" "nsd 4.13.0" +.TH "nsd.conf" "5" "sep 2, 2026" "NLnet Labs" "nsd 4.15.2" .\" Copyright (c) 2001\-2024, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" @@ -104,16 +104,19 @@ clause. There may only be one .B server: clause. .TP -.B ip\-address:\fR [@port] [servers] [bindtodevice] [setfib] +.B ip\-address:\fR [@port] [servers] [bindtodevice] [setfib] NSD will bind to the listed ip\-address. Can be given multiple times 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. .sp To limit which NSD server(s) listen on the given interface, specify one or -more servers separated by whitespace after [@port]. Ranges can be used as +more servers separated by whitespace after [@port]. +The NSD server has number 1 to server\-count processes to serve queries. +This specifies the server processes by number that handle the ip\-address. +Ranges can be used as a shorthand to specify multiple consecutive servers. By default every server -will listen. +will listen and handle traffic for this. .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. @@ -125,8 +128,19 @@ address of 0.0.0.0 then the kernel picks send to the internet, and it picks the wrong one. Typically needed for anycast instances. Use ip-transparent to be able to list addresses that turn on later (typical for certain load-balancing). +.sp +The bindtodevice option looks for the device for the address, and then +uses SO_BINDTODEVICE to bind the socket to the device. This is used as a +performance optimisation. And it allows the network socket to connect +directly to the network device. This speeds up the network stack processing. +The socket only processes packets received from that interface. +.sp +The setfib option uses the SO_SETFIB socket option so that the fib from +getaddrinfo for the address is set on the socket. This associates the FIB, +the routing table, from the interface for the socket. That bypasses the +routing selection for traffic. It is used as a performance optimisation. .TP -.B interface:\fR [@port] [servers] [bindtodevice] [setfib] +.B interface:\fR [@port] [servers] [bindtodevice] [setfib] Same as ip\-address (for ease of compatibility with unbound.conf). .TP .B ip\-transparent:\fR @@ -264,6 +278,14 @@ Note that not all platform supports sock Default is system default MSS determined by interface MTU and negotiation between NSD and other servers. .TP +.B tcp\-listen\-queue:\fR +Set the TCP backlog for listening sockets. This allows more pending +connections with their connection established waiting for TCP service. +Higher is better for more connections coming in at the same time. +High values also protect more against flooding and some side-channel issues. +Default is -1 on BSDs and Linux, and 256 on other systems. The -1 selects +the largest value allowed. +.TP .B xfrd\-tcp\-max:\fR Number of sockets for xfrd to use for outgoing zone transfers. Default 128. Increase it to allow more zone transfer sockets, like to 256. @@ -610,6 +632,18 @@ times, additional port numbers can be us interface definitions that use this port number expect PROXYv2 proxy protocol traffic, for UDP, TCP and for TLS service. .TP +.B allow\-proxy:\fR +The allowed proxy senders, that can send PROXYv2 traffic. Without an +allow\-proxy option any IP address is allowed. It can be given multiple times +to allow multiple netblocks, or with /32 or /128 specific addresses can be +listed. +.TP +.B udp\-padding\-port:\fR +UDP requests received over this port containing an EDNS0 padding option will, +unlike ordinary UDP requests, be answered with a padded response. The option +may be given more than once. +Listening to the port needs to be configured separately with a \fBip\-address\fR or \fBinterface\fR option. +.TP .B xdp\-interface:\fR The interface to use XDP with. This enables the use of AF_XDP sockets for UDP queries. Default is "", disabled. (EXPERIMENTAL) @@ -668,14 +702,21 @@ can help with drivers with broken AF_XDP .TP .B metrics\-enable:\fR Enable the prometheus metrics HTTP endpoint. It exposes the same statistics as -the \fInsd\-control\fR(8) stats_noreset command, but with metric names +the \fInsd\-control stats_noreset\fR command, but with metric names following the prometheus specification. (Requires libevent2) -To generate per zone metrics, specify the \fBzonestats\fR option on the desired -zones. - -Beware, that when using \fInsd\-control\fR(8) stats (instead of stats_noreset), -the statistics will be reset for the HTTP metrics endpoint as well. +To generate metrics per zone (or more accurately, per zonestats group), +specify the \fBzonestats\fR option on the desired zones. This will expose the +zonestats metrics with the zonestats name as a label called "zone", for +example with \fBzonestats: examplezone\fR: +.sp +.nf + nsd_zonestats_queries_total{zone="examplezone"} +.fi +.sp +Beware, that when using \fInsd\-control stats\fR (instead of \fInsd\-control +stats_noreset\fR), the statistics will be reset for the HTTP metrics endpoint +as well. .TP .B metrics\-interface:\fR NSD will bind to the listed addresses or interfaces to serve the prometheus @@ -894,6 +935,10 @@ The string is processed so that one stri 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). +The '/' slash character is escaped as "\\047" when it is inside the '%s' +string. +Directories are created as necessary, when the directory is specified +as part of the zonefile string. Useful for "%1/%2/%s.zone" or so. .sp .B %s\fR is replaced with the zone name. .sp @@ -1009,7 +1054,7 @@ SAN (Subject Alternative Name) DNS entry .BR auth-domain-name of the defined .BR tls-auth . -The certificate validify is also verified with +The certificate validity 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 Index: usr.sbin/nsd/nsd.conf.sample.in =================================================================== RCS file: /cvs/src/usr.sbin/nsd/nsd.conf.sample.in,v diff -u -p -r1.26 nsd.conf.sample.in --- usr.sbin/nsd/nsd.conf.sample.in 6 Sep 2025 17:41:37 -0000 1.26 +++ usr.sbin/nsd/nsd.conf.sample.in 21 Sep 2026 16:29:07 -0000 @@ -170,6 +170,10 @@ server: # Default is 0, system default MSS. # outgoing-tcp-mss: 0 + # Set the TCP backlog for listening sockets. + # Default 256, or -1 on BSDs and Linux. + # tcp-listen-queue: 256 + # reduce these settings to save memory for NSD, to about # xfrd-tcp-max: 32 and xfrd-tcp-pipeline: 128, also rrl-size: 1000 # other memory is determined by server-count, tcp-count and zone data @@ -282,6 +286,10 @@ server: # The interfaces that use these listed port numbers will support and # expect PROXYv2. For UDP and TCP/TLS interfaces. # proxy-protocol-port: portno for each of the port numbers. + + # Allowed proxy senders. Without an allow-proxy option, any IP address + # is allowed. This is the outer, proxy source address. + # allow-proxy: 192.0.2.0/24 # The interface to use XDP with. Default is "", disabled. # xdp-interface: eth0 Index: usr.sbin/nsd/nsec3.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/nsec3.c,v diff -u -p -r1.29 nsec3.c --- usr.sbin/nsd/nsec3.c 6 Sep 2025 17:41:37 -0000 1.29 +++ usr.sbin/nsd/nsec3.c 21 Sep 2026 16:29:07 -0000 @@ -70,9 +70,11 @@ cmp_nsec3_tree(const void* x, const void const domain_type* a = (const domain_type*)x; const domain_type* b = (const domain_type*)y; /* labelcount + 32long label */ - assert(dname_name(domain_dname_const(a))[0] == 32); - assert(dname_name(domain_dname_const(b))[0] == 32); - return memcmp(dname_name(domain_dname_const(a)), dname_name(domain_dname_const(b)), 33); + assert(dname_name(domain_dname_const(a))[0] == NSEC3_OWNER_LABEL_LEN); + assert(dname_name(domain_dname_const(b))[0] == NSEC3_OWNER_LABEL_LEN); + return memcmp( dname_name(domain_dname_const(a)) + , dname_name(domain_dname_const(b)) + , NSEC3_OWNER_LABEL_LEN + 1); } void nsec3_zone_trees_create(struct region* region, zone_type* zone) @@ -93,9 +95,13 @@ detect_nsec3_params(rr_type* nsec3_apex, { assert(salt && salt_len && iter); assert(nsec3_apex); - *salt_len = rdata_atom_data(nsec3_apex->rdatas[3])[0]; - *salt = (unsigned char*)(rdata_atom_data(nsec3_apex->rdatas[3])+1); - *iter = read_uint16(rdata_atom_data(nsec3_apex->rdatas[2])); + if(nsec3_apex->rdlength < 5) + return; + if(nsec3_apex->rdlength < 5 + (uint16_t)(nsec3_apex->rdata[4])) + return; + *salt_len = nsec3_apex->rdata[4]; + *salt = (unsigned char*)(nsec3_apex->rdata + 5); + *iter = read_uint16(nsec3_apex->rdata + 2); } const dname_type * @@ -119,6 +125,7 @@ nsec3_hash_and_store(zone_type* zone, co detect_nsec3_params(zone->nsec3_param, &nsec3_salt, &nsec3_saltlength, &nsec3_iterations); assert(nsec3_iterations >= 0 && nsec3_iterations <= 65536); + assert(dname); iterated_hash((unsigned char*)store, nsec3_salt, nsec3_saltlength, dname_name(dname), dname->name_size, nsec3_iterations); } @@ -140,9 +147,11 @@ nsec3_lookup_hash_and_wc(region_type* re domain->nsec3->hash_wc->hash.node.key = NULL; domain->nsec3->hash_wc->wc.node.key = NULL; nsec3_hash_and_store(zone, dname, domain->nsec3->hash_wc->hash.hash); - wcard = dname_parse(tmpregion, "*"); - wcard = dname_concatenate(tmpregion, wcard, dname); - nsec3_hash_and_store(zone, wcard, domain->nsec3->hash_wc->wc.hash); + if(dname->name_size + 2 <= MAXDOMAINLEN) { + wcard = dname_parse(tmpregion, "*"); + wcard = dname_concatenate(tmpregion, wcard, dname); + nsec3_hash_and_store(zone, wcard, domain->nsec3->hash_wc->wc.hash); + } } static void @@ -162,13 +171,25 @@ nsec3_lookup_hash_ds(region_type* region static int nsec3_has_soa(rr_type* rr) { - if(rdata_atom_size(rr->rdatas[NSEC3_RDATA_BITMAP]) >= 3 && /* has types in bitmap */ - rdata_atom_data(rr->rdatas[NSEC3_RDATA_BITMAP])[0] == 0 && /* first window = 0, */ - /* [1]: bitmap length must be >= 1 */ - /* [2]: bit[6] = SOA, thus mask first bitmap octet with 0x02 */ - (rdata_atom_data(rr->rdatas[NSEC3_RDATA_BITMAP])[2]&0x02)) { /* SOA bit set */ + size_t offset; + /* byte + byte + short + string + string + bitmap */ + if(rr->rdlength < 6) + return 0; + offset = 4; + if(rr->rdlength < offset+1 || + rr->rdlength < offset+1+rr->rdata[offset]) + return 0; + offset += 1 + rr->rdata[offset]; + if(rr->rdlength < offset+1 || + rr->rdlength < offset+1+rr->rdata[offset]) + return 0; + offset += 1 + rr->rdata[offset]; + if (rr->rdlength >= offset + 3 && /* has types in bitmap */ + rr->rdata[offset] == 0 && /* first window = 0, */ + /* [1]: bitmap length must be >= 1 */ + /* [2]: bit[6] = SOA, thus mask first bitmap octet with 0x02 */ + (rr->rdata[offset+2] & 0x02)) return 1; - } return 0; } @@ -185,6 +206,23 @@ check_apex_soa(namedb_type* namedb, zone nsec3_hash_and_store(zone, dname, h); tmpregion = region_create(xalloc, free); hashed_apex = nsec3_b32_create(tmpregion, zone, h); + if(!hashed_apex) { + if(!nolog) { + if((int)domain_dname(zone->apex)->name_size >= 223) { + log_msg( LOG_ERR + , "apex %s too long for NSEC3 hash label" + , dname_to_string( + domain_dname(zone->apex), NULL)); + } else { + log_msg( LOG_ERR + , "cannot create NSEC3 hash label at %s" + , dname_to_string( + domain_dname(zone->apex), NULL)); + } + } + region_destroy(tmpregion); + return NULL; + } domain = domain_table_find(namedb->domains, hashed_apex); if(!domain) { if(!nolog) { @@ -208,13 +246,14 @@ check_apex_soa(namedb_type* namedb, zone return NULL; } for(j=0; jrr_count; j++) { - if(nsec3_has_soa(&nsec3_rrset->rrs[j])) { + if(nsec3_has_soa(nsec3_rrset->rrs[j]) && + nsec3_rr_uses_params(nsec3_rrset->rrs[j], zone)) { region_destroy(tmpregion); - return &nsec3_rrset->rrs[j]; + return nsec3_rrset->rrs[j]; } } if(!nolog) { - log_msg(LOG_ERR, "%s NSEC3PARAM entry: hash(apex) NSEC3 has no SOA flag.", + log_msg(LOG_ERR, "%s NSEC3PARAM entry: hash(apex) NSEC3 has no SOA flag or different params.", domain_to_string(zone->apex)); log_msg(LOG_ERR, "hash(apex)= %s", dname_to_string(hashed_apex, NULL)); @@ -226,21 +265,26 @@ check_apex_soa(namedb_type* namedb, zone static void nsec3param_to_str(struct rr* rr, char* str, size_t buflen) { - rdata_atom_type* rd = rr->rdatas; size_t len; + if(rr->rdlength < 5 || rr->rdlength < 5 + (uint16_t)rr->rdata[4]) { + str[0]=0; + return; + } len = snprintf(str, buflen, "%u %u %u ", - (unsigned)rdata_atom_data(rd[0])[0], - (unsigned)rdata_atom_data(rd[1])[0], - (unsigned)read_uint16(rdata_atom_data(rd[2]))); - if(rdata_atom_data(rd[3])[0] == 0) { + (unsigned)rr->rdata[0], (unsigned)rr->rdata[1], + (unsigned)read_uint16(rr->rdata+2)); + if(rr->rdata[4] == 0) { if(buflen > len + 2) str[len++] = '-'; } else { - len += hex_ntop(rdata_atom_data(rd[3])+1, - rdata_atom_data(rd[3])[0], str+len, buflen-len-1); + len += hex_ntop(rr->rdata+5, rr->rdata[4], str+len, buflen-len-1); } - if(buflen > len + 1) + if(buflen > len + 1) { str[len] = 0; + } else { + assert(buflen > 0); + str[buflen-1] = 0; + } } static struct rr* @@ -253,14 +297,13 @@ db_find_nsec3param(struct namedb* db, st return NULL; /* find first nsec3param we can support (SHA1, no flags) */ for(i=0; irr_count; i++) { - rdata_atom_type* rd = rrset->rrs[i].rdatas; /* do not use the RR that is going to be deleted (in IXFR) */ - if(&rrset->rrs[i] == avoid_rr) continue; - if(rrset->rrs[i].rdata_count < 4) continue; - if(rdata_atom_data(rd[0])[0] == NSEC3_SHA1_HASH && - rdata_atom_data(rd[1])[0] == 0) { + if(rrset->rrs[i] == avoid_rr) continue; + if(rrset->rrs[i]->rdlength < 5) continue; /* require salt field */ + if(rrset->rrs[i]->rdata[0] == NSEC3_SHA1_HASH && + rrset->rrs[i]->rdata[1] == 0) { if(checkchain) { - z->nsec3_param = &rrset->rrs[i]; + z->nsec3_param = rrset->rrs[i]; if(!check_apex_soa(db, z, 1)) { char str[MAX_RDLENGTH*2+16]; nsec3param_to_str(z->nsec3_param, @@ -271,12 +314,12 @@ db_find_nsec3param(struct namedb* db, st } if(2 <= verbosity) { char str[MAX_RDLENGTH*2+16]; - nsec3param_to_str(&rrset->rrs[i], str, + nsec3param_to_str(rrset->rrs[i], str, sizeof(str)); VERBOSITY(2, (LOG_INFO, "rehash of zone %s with parameters %s", domain_to_string(z->apex), str)); } - return &rrset->rrs[i]; + return rrset->rrs[i]; } } return NULL; @@ -292,27 +335,99 @@ nsec3_find_zone_param(struct namedb* db, /* check params ok for one RR */ static int -nsec3_rdata_params_ok(rdata_atom_type* prd, rdata_atom_type* rd) +nsec3_rdata_params_ok(const rr_type *prr, const rr_type* rr) { - return (rdata_atom_data(rd[0])[0] == - rdata_atom_data(prd[0])[0] && /* hash algo */ - rdata_atom_data(rd[2])[0] == - rdata_atom_data(prd[2])[0] && /* iterations 0 */ - rdata_atom_data(rd[2])[1] == - rdata_atom_data(prd[2])[1] && /* iterations 1 */ - rdata_atom_data(rd[3])[0] == - rdata_atom_data(prd[3])[0] && /* salt length */ - memcmp(rdata_atom_data(rd[3])+1, - rdata_atom_data(prd[3])+1, rdata_atom_data(rd[3])[0]) - == 0 ); + if(prr->rdlength > rr->rdlength) + return 0; /* The salt has to fit */ + if(prr->rdlength < 5) + return 0; /* Malformed */ + return prr->rdata[0] == rr->rdata[0] && + (memcmp(prr->rdata+2, rr->rdata+2, prr->rdlength-2) == 0); } + +/* Copied verbatim from simdzone/src/generic/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 */ +}; + + int nsec3_rr_uses_params(rr_type* rr, zone_type* zone) { - if(!rr || rr->rdata_count < 4) + /* TODO: Test validness with base32hex parser from simdzone */ + const uint8_t* wire; + + if(!rr || rr->rdlength < 6) return 0; - return nsec3_rdata_params_ok(zone->nsec3_param->rdatas, rr->rdatas); + wire = dname_name(domain_dname_const(rr->owner)); + if(wire[0] == NSEC3_OWNER_LABEL_LEN + && b32rmap[wire[ 1]] < 32 && b32rmap[wire[ 2]] < 32 + && b32rmap[wire[ 3]] < 32 && b32rmap[wire[ 4]] < 32 + && b32rmap[wire[ 5]] < 32 && b32rmap[wire[ 6]] < 32 + && b32rmap[wire[ 7]] < 32 && b32rmap[wire[ 8]] < 32 + && b32rmap[wire[ 9]] < 32 && b32rmap[wire[10]] < 32 + && b32rmap[wire[11]] < 32 && b32rmap[wire[12]] < 32 + && b32rmap[wire[13]] < 32 && b32rmap[wire[14]] < 32 + && b32rmap[wire[15]] < 32 && b32rmap[wire[16]] < 32 + && b32rmap[wire[17]] < 32 && b32rmap[wire[18]] < 32 + && b32rmap[wire[19]] < 32 && b32rmap[wire[20]] < 32 + && b32rmap[wire[21]] < 32 && b32rmap[wire[22]] < 32 + && b32rmap[wire[23]] < 32 && b32rmap[wire[24]] < 32 + && b32rmap[wire[25]] < 32 && b32rmap[wire[26]] < 32 + && b32rmap[wire[27]] < 32 && b32rmap[wire[28]] < 32 + && b32rmap[wire[29]] < 32 && b32rmap[wire[30]] < 32 + && b32rmap[wire[31]] < 32 && b32rmap[wire[32]] < 32) + return nsec3_rdata_params_ok(zone->nsec3_param, rr); + return 0; +} + +int +nsec3_has_owner_for_zone(domain_type* domain, zone_type* zone) +{ + if(dname_name(domain_dname_const(domain))[0] != NSEC3_OWNER_LABEL_LEN) + return 0; /* not b32.name */ + if(!(((size_t)domain_dname(domain)->label_count) == + ((size_t)domain_dname(zone->apex)->label_count)+1 && + ((size_t)domain_dname(domain)->name_size) == + ((size_t)domain_dname(zone->apex)->name_size) + + NSEC3_OWNER_LABEL_LEN+1 && + memcmp(dname_name(domain_dname_const(domain))+ + NSEC3_OWNER_LABEL_LEN+1, dname_name(domain_dname_const( + zone->apex)), domain_dname(zone->apex)->name_size) == 0)) + return 0; /* not b32.zonename */ + return 1; } int @@ -323,8 +438,10 @@ nsec3_in_chain_count(domain_type* domain int count = 0; if(!rrset || !zone->nsec3_param) return 0; /* no NSEC3s, none in the chain */ + if(!nsec3_has_owner_for_zone(domain, zone)) + return 0; /* wrong owner name */ for(i=0; irr_count; i++) { - if(nsec3_rr_uses_params(&rrset->rrs[i], zone)) + if(nsec3_rr_uses_params(rrset->rrs[i], zone)) count++; } return count; @@ -372,6 +489,89 @@ hash_tree_clear(rbtree_type* tree) tree->root = RBTREE_NULL; } +/* Clear nsec3 precompile for walked to domain */ +static void +nsec3_clear_precompile_walk(struct namedb* db, zone_type* zone, + domain_type* walk) +{ + if(walk->nsec3) { + if(nsec3_condition_hash(walk, zone)) { + walk->nsec3->nsec3_node.key = NULL; + walk->nsec3->nsec3_cover = NULL; + walk->nsec3->nsec3_wcard_child_cover = NULL; + walk->nsec3->nsec3_is_exact = 0; + if (walk->nsec3->hash_wc) { + region_recycle(db->domains->region, + walk->nsec3->hash_wc, + sizeof(nsec3_hash_wc_node_type)); + walk->nsec3->hash_wc = NULL; + } + } + if(nsec3_condition_dshash(walk, zone)) { + walk->nsec3->nsec3_ds_parent_cover = NULL; + walk->nsec3->nsec3_ds_parent_is_exact = 0; + if (walk->nsec3->ds_parent_hash) { + region_recycle(db->domains->region, + walk->nsec3->ds_parent_hash, + sizeof(nsec3_hash_node_type)); + walk->nsec3->ds_parent_hash = NULL; + } + } + } +} + +/* Find the zone above apex (that is the apex of a zone), NULL if none. */ +static struct zone* +find_superzone_of(struct namedb* db, const dname_type* apex) +{ + struct domain* apex_domain = domain_table_find(db->domains, apex); + if(apex_domain && apex_domain->parent) + return domain_find_zone(db, apex_domain->parent); + return NULL; +} + +void +nsec3_superzone_clear_for_apex(struct namedb* db, const dname_type* apex) +{ + domain_type* apex_domain, *walk; + struct zone* zone; /* the super zone of apex */ + zone = find_superzone_of(db, apex); + if(!zone) return; /* no super zone above the apex */ + if(!zone->nsec3_param) return; /* super is not an NSEC3 zone */ + + apex_domain = domain_table_find(db->domains, apex); + if(!domain_is_subdomain(apex_domain, zone->apex)) + return; /* robustness check */ + walk = apex_domain; + while(walk && domain_is_subdomain(walk, apex_domain)) { + if(walk->nsec3) { + if(nsec3_condition_hash(walk, zone)) { + zone_del_domain_in_hash_tree(zone->nsec3tree, + &walk->nsec3->nsec3_node); + if(walk->nsec3->hash_wc) { + zone_del_domain_in_hash_tree( + zone->hashtree, + &walk->nsec3->hash_wc->hash.node); + zone_del_domain_in_hash_tree( + zone->wchashtree, + &walk->nsec3->hash_wc->wc.node); + } + } + if(nsec3_condition_dshash(walk, zone)) { + if(walk->nsec3->ds_parent_hash) { + zone_del_domain_in_hash_tree(zone->dshashtree, + &walk->nsec3->ds_parent_hash->node); + } + } + } + nsec3_clear_precompile_walk(db, zone, walk); + walk = domain_next(walk); + } + /* clear nsec3_last if that was cleared */ + zone->nsec3_last = ((zone->nsec3tree && rbtree_last(zone->nsec3tree) != RBTREE_NULL)? + (domain_type*)rbtree_last(zone->nsec3tree)->key:NULL); +} + void nsec3_clear_precompile(struct namedb* db, zone_type* zone) { @@ -388,30 +588,7 @@ nsec3_clear_precompile(struct namedb* db /* wipe precompile */ walk = zone->apex; while(walk && domain_is_subdomain(walk, zone->apex)) { - if(walk->nsec3) { - if(nsec3_condition_hash(walk, zone)) { - walk->nsec3->nsec3_node.key = NULL; - walk->nsec3->nsec3_cover = NULL; - walk->nsec3->nsec3_wcard_child_cover = NULL; - walk->nsec3->nsec3_is_exact = 0; - if (walk->nsec3->hash_wc) { - region_recycle(db->domains->region, - walk->nsec3->hash_wc, - sizeof(nsec3_hash_wc_node_type)); - walk->nsec3->hash_wc = NULL; - } - } - if(nsec3_condition_dshash(walk, zone)) { - walk->nsec3->nsec3_ds_parent_cover = NULL; - walk->nsec3->nsec3_ds_parent_is_exact = 0; - if (walk->nsec3->ds_parent_hash) { - region_recycle(db->domains->region, - walk->nsec3->ds_parent_hash, - sizeof(nsec3_hash_node_type)); - walk->nsec3->ds_parent_hash = NULL; - } - } - } + nsec3_clear_precompile_walk(db, zone, walk); walk = domain_next(walk); } zone->nsec3_last = NULL; @@ -544,8 +721,6 @@ nsec3_precompile_domain(struct namedb* d /* add into tree */ zone_add_domain_in_hash_tree(db->region, &zone->hashtree, cmp_hash_tree, domain, &domain->nsec3->hash_wc->hash.node); - zone_add_domain_in_hash_tree(db->region, &zone->wchashtree, - cmp_wchash_tree, domain, &domain->nsec3->hash_wc->wc.node); /* lookup in tree cover ptr (or exact) */ exact = nsec3_find_cover(zone, domain->nsec3->hash_wc->hash.hash, @@ -555,10 +730,23 @@ nsec3_precompile_domain(struct namedb* d domain->nsec3->nsec3_is_exact = 1; else domain->nsec3->nsec3_is_exact = 0; - /* find cover for *.domain for wildcard denial */ - (void)nsec3_find_cover(zone, domain->nsec3->hash_wc->wc.hash, - sizeof(domain->nsec3->hash_wc->wc.hash), &result); - domain->nsec3->nsec3_wcard_child_cover = result; + /* If the wildcard (*.domain) fits within MAXDOMAINLEN, then add it to + * the wildcard hashtree and find the cover for it. Note that, in this + * case, its hash value has been computed (nsec3_lookup_hash_and_wc()). + */ + if(domain_dname(domain)->name_size + 2 <= MAXDOMAINLEN) { + zone_add_domain_in_hash_tree(db->region, &zone->wchashtree, + cmp_wchash_tree, domain, &domain->nsec3->hash_wc->wc.node); + (void)nsec3_find_cover(zone, domain->nsec3->hash_wc->wc.hash, + sizeof(domain->nsec3->hash_wc->wc.hash), &result); + domain->nsec3->nsec3_wcard_child_cover = result; + } else { + /* Setting to NULL is safe, because nsec3_add_rrset() is the + * only usage of nsec3_wcard_child_cover, and simply doesn't + * try to add anythin if it is NULL + */ + domain->nsec3->nsec3_wcard_child_cover = NULL; + } } void @@ -582,20 +770,43 @@ nsec3_precompile_domain_ds(struct namedb cmp_dshash_tree, domain, &domain->nsec3->ds_parent_hash->node); } -static void -parse_nsec3_name(const dname_type* dname, uint8_t* hash, size_t buflen) +static inline int +b32hex_p8ton5(const uint8_t* p, uint8_t* n) +{ + uint8_t ofs; + if((ofs = b32rmap[p[0]]) > 31) return 0; + n[0] = (uint8_t)(ofs << 3); + if((ofs = b32rmap[p[1]]) > 31) return 0; + n[0] |= (uint8_t)(ofs >> 2); + n[1] = (uint8_t)(ofs << 6); + if((ofs = b32rmap[p[2]]) > 31) return 0; + n[1] |= (uint8_t)(ofs << 1); + if((ofs = b32rmap[p[3]]) > 31) return 0; + n[1] |= (uint8_t)(ofs >> 4); + n[2] = (uint8_t)(ofs << 4); + if((ofs = b32rmap[p[4]]) > 31) return 0; + n[2] |= (uint8_t)(ofs >> 1); + n[3] = (uint8_t)(ofs << 7); + if((ofs = b32rmap[p[5]]) > 31) return 0; + n[3] |= (uint8_t)(ofs << 2); + if((ofs = b32rmap[p[6]]) > 31) return 0; + n[3] |= (uint8_t)(ofs >> 3); + n[4] = (uint8_t)(ofs << 5); + if((ofs = b32rmap[p[7]]) > 31) return 0; + n[4] |= ofs; + return 1; +} + +static int +parse_nsec3_name(const dname_type* dname, uint8_t* hash) { - /* first label must be the match, */ - size_t lablen = (buflen-1) * 8 / 5; + /* TODO: Do this with base32hex parser from simdzone */ const uint8_t* wire = dname_name(dname); - assert(lablen == 32 && buflen == NSEC3_HASH_LEN+1); - /* labels of length 32 for SHA1, and must have space+1 for convert */ - if(wire[0] != lablen) { - /* not NSEC3 */ - memset(hash, 0, buflen); - return; - } - (void)b32_pton((char*)wire+1, hash, buflen); + return wire[0] == NSEC3_OWNER_LABEL_LEN + && b32hex_p8ton5(wire + 1, hash) + && b32hex_p8ton5(wire + 9, hash + 5) + && b32hex_p8ton5(wire + 17, hash + 10) + && b32hex_p8ton5(wire + 25, hash + 15); } void @@ -612,6 +823,46 @@ nsec3_precompile_nsec3rr(namedb_type* db } } +/* nsec3 precompile for walked to domain */ +static void +nsec3_precompile_walk(struct namedb* db, zone_type* zone, domain_type* walk, + struct region* tmpregion) +{ + if(nsec3_condition_hash(walk, zone)) { + nsec3_precompile_domain(db, walk, zone, tmpregion); + region_free_all(tmpregion); + } + if(nsec3_condition_dshash(walk, zone)) + nsec3_precompile_domain_ds(db, walk, zone); +} + +void +nsec3_superzone_precompile_for_apex(struct namedb* db, const dname_type* apex) +{ + region_type* tmpregion; + domain_type* apex_domain, *walk; + struct zone* zone; /* the super zone of apex */ + zone = find_superzone_of(db, apex); + if(!zone) return; /* no super zone above the apex */ + if(!zone->nsec3_param) return; /* super is not an NSEC3 zone */ + + apex_domain = domain_table_find(db->domains, apex); + if(!domain_is_subdomain(apex_domain, zone->apex)) + return; /* robustness check */ + tmpregion = region_create(xalloc, free); + for(walk=apex_domain; walk && domain_is_subdomain(walk, apex_domain); + walk = domain_next(walk)) { + if(nsec3_in_chain_count(walk, zone) != 0) { + nsec3_precompile_nsec3rr(db, walk, zone); + } + } + for(walk=apex_domain; walk && domain_is_subdomain(walk, apex_domain); + walk = domain_next(walk)) { + nsec3_precompile_walk(db, zone, walk, tmpregion); + } + region_destroy(tmpregion); +} + void nsec3_precompile_newparam(namedb_type* db, zone_type* zone) { @@ -631,12 +882,7 @@ nsec3_precompile_newparam(namedb_type* d /* hash and precompile zone */ for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex); walk = domain_next(walk)) { - if(nsec3_condition_hash(walk, zone)) { - nsec3_precompile_domain(db, walk, zone, tmpregion); - region_free_all(tmpregion); - } - if(nsec3_condition_dshash(walk, zone)) - nsec3_precompile_domain_ds(db, walk, zone); + nsec3_precompile_walk(db, zone, walk, tmpregion); if(++c % ZONEC_PCT_COUNT == 0 && time(NULL) > s + ZONEC_PCT_TIME) { s = time(NULL); VERBOSITY(1, (LOG_INFO, "nsec3 %s %d %%", @@ -761,8 +1007,13 @@ process_range(zone_type* zone, domain_ty * already allocated, and we need not allocate it here */ /* set start */ if(start) { - uint8_t hash[NSEC3_HASH_LEN+1]; - parse_nsec3_name(domain_dname(start), hash, sizeof(hash)); + uint8_t hash[NSEC3_HASH_LEN]; + if(!parse_nsec3_name(domain_dname(start), hash)) { + log_msg( LOG_WARNING + , "NSEC3 %s skipped due to invalid owner name" + , dname_to_string(domain_dname(start), 0)); + return; + } /* if exact match on first, set is_exact */ if(process_first(zone->hashtree, hash, &p, init_lookup_key_hash_tree)) { ((domain_type*)(p->key))->nsec3->nsec3_cover = nsec3; @@ -787,8 +1038,13 @@ process_range(zone_type* zone, domain_ty } /* set end */ if(end) { - uint8_t hash[NSEC3_HASH_LEN+1]; - parse_nsec3_name(domain_dname(end), hash, sizeof(hash)); + uint8_t hash[NSEC3_HASH_LEN]; + if(!parse_nsec3_name(domain_dname(end), hash)) { + log_msg( LOG_WARNING + , "NSEC3 %s skipped due to invalid owner name" + , dname_to_string(domain_dname(end), 0)); + return; + } process_end(zone->hashtree, hash, &p_end, init_lookup_key_hash_tree); process_end(zone->wchashtree, hash, &pwc_end, init_lookup_key_wc_tree); process_end(zone->dshashtree, hash, &pds_end, init_lookup_key_ds_tree); @@ -1108,9 +1364,9 @@ domain_has_only_NSEC3(struct domain* dom { if(!zone || rrset->zone == zone) { - if(rrset->rrs[0].type == TYPE_NSEC3) + if(rrset->rrs[0]->type == TYPE_NSEC3) nsec3_seen = 1; - else if(rrset->rrs[0].type != TYPE_RRSIG) + else if(rrset->rrs[0]->type != TYPE_RRSIG) return 0; } rrset = rrset->next; Index: usr.sbin/nsd/nsec3.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/nsec3.h,v diff -u -p -r1.4 nsec3.h --- usr.sbin/nsd/nsec3.h 20 Dec 2023 17:29:01 -0000 1.4 +++ usr.sbin/nsd/nsec3.h 21 Sep 2026 16:29:07 -0000 @@ -80,6 +80,8 @@ void nsec3_hash_and_store(struct zone* z int nsec3_rr_uses_params(struct rr* rr, struct zone* zone); /* number of NSEC3s that are in the zone chain */ int nsec3_in_chain_count(struct domain* domain, struct zone* zone); +/* if the NSEC3 record is b32.zone name */ +int nsec3_has_owner_for_zone(struct domain* domain, struct zone* zone); /* find previous NSEC3, or, lastinzone, or, NULL */ struct domain* nsec3_chain_find_prev(struct zone* zone, struct domain* domain); /* clear nsec3 precompile for the zone */ @@ -104,6 +106,12 @@ void nsec3_precompile_nsec3rr(struct nam struct zone* zone); /* precompile entire zone, assumes all is null at start */ void nsec3_precompile_newparam(struct namedb* db, struct zone* zone); +/* clear super zone of apex, for names subdomain of apex. */ +void nsec3_superzone_clear_for_apex(struct namedb* db, + const struct dname* apex); +/* precompile super zone of apex, for names subdomain of apex. */ +void nsec3_superzone_precompile_for_apex(struct namedb* db, + const struct dname* apex); /* create b32.zone for a hash, allocated in the region */ const struct dname* nsec3_b32_create(struct region* region, struct zone* zone, unsigned char* hash); Index: usr.sbin/nsd/options.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/options.c,v diff -u -p -r1.32 options.c --- usr.sbin/nsd/options.c 6 Sep 2025 17:41:37 -0000 1.32 +++ usr.sbin/nsd/options.c 21 Sep 2026 16:29:07 -0000 @@ -29,6 +29,7 @@ #include "rrl.h" #include "bitset.h" #include "xfrd.h" +#include "metrics.h" #include "configparser.h" config_parser_state_type* cfg_parser = 0; @@ -65,8 +66,8 @@ nsd_options_create(region_type* region) opt->ip_addresses = NULL; opt->ip_transparent = 0; opt->ip_freebind = 0; - opt->send_buffer_size = 0; - opt->receive_buffer_size = 0; + opt->send_buffer_size = 4*1024*1024; + opt->receive_buffer_size = 1*1024*1024; opt->debug_mode = 0; opt->verbosity = 0; opt->hide_version = 0; @@ -94,6 +95,7 @@ nsd_options_create(region_type* region) opt->tcp_timeout = TCP_TIMEOUT; opt->tcp_mss = 0; opt->outgoing_tcp_mss = 0; + opt->tcp_listen_queue = TCP_BACKLOG; opt->ipv4_edns_size = EDNS_MAX_MESSAGE_LEN; opt->ipv6_edns_size = EDNS_MAX_MESSAGE_LEN; opt->pidfile = PIDFILE; @@ -150,6 +152,8 @@ nsd_options_create(region_type* region) opt->tls_cert_bundle = NULL; opt->tls_auth_xfr_only = 0; opt->proxy_protocol_port = NULL; + opt->allow_proxy = NULL; + opt->udp_padding_port = NULL; opt->answer_cookie = 1; opt->cookie_secret = NULL; opt->cookie_staging_secret = NULL; @@ -332,6 +336,9 @@ parse_options_file(struct nsd_options* o 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); + else if (!opt->tls_auth_port) + c_warning("provide-xfr has a tls-auth-name," + " but no tls-auth-port is configured"); } if(acl->nokey || acl->blocked) continue; @@ -958,10 +965,11 @@ zone_list_close(struct nsd_options* opt) } static void -c_error_va_list_pos(int showpos, const char* fmt, va_list args) +c_error_va_list_pos(int showpos, int is_error, const char* fmt, va_list args) { char* at = NULL; - cfg_parser->errors++; + if(is_error) + cfg_parser->errors++; if(showpos && c_text && c_text[0]!=0) { at = c_text; } @@ -974,7 +982,8 @@ c_error_va_list_pos(int showpos, const c snprintf(m, sizeof(m), "at '%s': ", at); (*cfg_parser->err)(cfg_parser->err_arg, m); } - (*cfg_parser->err)(cfg_parser->err_arg, "error: "); + (*cfg_parser->err)(cfg_parser->err_arg, + is_error ? "error: " : "warning: "); vsnprintf(m, sizeof(m), fmt, args); (*cfg_parser->err)(cfg_parser->err_arg, m); (*cfg_parser->err)(cfg_parser->err_arg, "\n"); @@ -982,7 +991,7 @@ c_error_va_list_pos(int showpos, const c } fprintf(stderr, "%s:%d: ", cfg_parser->filename, cfg_parser->line); if(at) fprintf(stderr, "at '%s': ", at); - fprintf(stderr, "error: "); + fprintf(stderr, is_error ? "error: " : "warning: "); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); } @@ -998,7 +1007,22 @@ c_error(const char *fmt, ...) } va_start(ap, fmt); - c_error_va_list_pos(showpos, fmt, ap); + c_error_va_list_pos(showpos, 1, fmt, ap); + va_end(ap); +} + +void +c_warning(const char *fmt, ...) +{ + va_list ap; + int showpos = 0; + + if (strcmp(fmt, "syntax error") == 0 || strcmp(fmt, "parse error") == 0) { + showpos = 1; + } + + va_start(ap, fmt); + c_error_va_list_pos(showpos, 0, fmt, ap); va_end(ap); } @@ -1941,6 +1965,28 @@ key_options_add_modify(struct nsd_option } int +acl_check_incoming_proxy(struct acl_options* acl, struct query* q, + struct acl_options** reason) +{ + if(reason) + *reason = NULL; + + while(acl) + { + DEBUG(DEBUG_XFRD,2, (LOG_INFO, "proxy testing allow-proxy acl %s", + acl->ip_address_spec)); + if(acl_addr_matches_proxy(acl, q)) { + if(reason) + *reason = acl; + return 1; + } + acl = acl->next; + } + + return -1; +} + +int acl_check_incoming_block_proxy(struct acl_options* acl, struct query* q, struct acl_options** reason) { @@ -1993,7 +2039,21 @@ acl_check_incoming(struct acl_options* a 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)) { +#ifdef HAVE_SSL + if (acl->tls_auth_name && !q->tls_auth) { + /* the acl requires a TLS client cert with name, but + * the connection did not came over a "tls-auth-port:" + */ + number++; + acl = acl->next; + continue; + } +#endif + if(acl_addr_matches(acl, q) && acl_key_matches(acl, q) +#ifdef HAVE_SSL + && acl_tls_auth_name_matches(acl, q) +#endif + ) { if(!match) { match = acl; /* remember first match */ @@ -2005,27 +2065,6 @@ 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; } @@ -2189,9 +2228,9 @@ acl_addr_match_range_v4(uint32_t* minval /* check treats x as one huge number */ /* if outside bounds, we are done */ - if(*minval > *x) + if(ntohl(*minval) > ntohl(*x)) return 0; - if(*maxval < *x) + if(ntohl(*maxval) < ntohl(*x)) return 0; return 1; @@ -2212,10 +2251,10 @@ acl_addr_match_range_v6(uint32_t* minval { /* if outside bounds, we are done */ if(checkmin) - if(minval[i] > x[i]) + if(ntohl(minval[i]) > ntohl(x[i])) return 0; if(checkmax) - if(maxval[i] < x[i]) + if(ntohl(maxval[i]) < ntohl(x[i])) return 0; /* if x is equal to a bound, that bound needs further checks */ if(checkmin && minval[i]!=x[i]) @@ -2238,13 +2277,18 @@ acl_addr_match_range_v6(uint32_t* minval * Copyright (C) 2012, iSEC Partners. * License: MIT License * Author: Alban Diquet + * + * Modified 20260805 W.C.A. Wijngaards - added san_present for RFC6125 + * conformance change. */ static int matches_subject_alternative_name( - const char *acl_cert_cn, size_t acl_cert_cn_len, const X509 *cert) + const char *acl_cert_cn, size_t acl_cert_cn_len, const X509 *cert, + int* san_present) { int result = 0; int san_names_nb = -1; STACK_OF(GENERAL_NAME) *san_names = NULL; + *san_present = 0; /* 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); @@ -2261,6 +2305,7 @@ static int matches_subject_alternative_n /* Skip non-DNS SAN entries. */ if (current_name->type != GEN_DNS) continue; + *san_present = 1; /* DNS SAN entry is present */ #if HAVE_ASN1_STRING_GET0_DATA str = (const char *)ASN1_STRING_get0_data(current_name->d.dNSName); #else @@ -2353,7 +2398,7 @@ static int matches_common_name( int acl_tls_hostname_matches(SSL* tls_auth, const char *acl_cert_cn) { - int result = 0; + int result = 0, san_present; size_t acl_cert_cn_len; X509 *client_cert; @@ -2381,8 +2426,9 @@ acl_tls_hostname_matches(SSL* tls_auth, */ 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))) + /* follow RFC6125#section-6.4.4 check SAN DNS first, and + * common name if there is no SAN DNS present. */ + if (!(result = matches_subject_alternative_name(acl_cert_cn, acl_cert_cn_len, client_cert, &san_present)) && !san_present) result = matches_common_name(acl_cert_cn, acl_cert_cn_len, client_cert); X509_free(client_cert); @@ -2430,6 +2476,35 @@ acl_key_matches(struct acl_options* acl, return 1; } +#ifdef HAVE_SSL +int +acl_tls_auth_name_matches(struct acl_options* acl, struct query* q) +{ + /* If no name specified, no name is required */ + if (!acl->tls_auth_name) + return 1; + + /* we have auth_domain_name in tls_auth */ + if (!acl->tls_auth_options + || !acl->tls_auth_options->auth_domain_name) { + /* 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); + return 0; + } + if (!acl_tls_hostname_matches(q->tls_auth, + acl->tls_auth_options->auth_domain_name)) { + VERBOSITY(6, (LOG_DEBUG, "client cert does not match %s %s", + acl->tls_auth_name, + acl->tls_auth_options->auth_domain_name)); + return 0; + } + 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; + return 1; +} +#endif + int acl_same_host(struct acl_options* a, struct acl_options* b) { @@ -2526,6 +2601,20 @@ replace_str(char* str, size_t len, const } } +/* replace occurences of '/' with "\047", as a way to escape the '/'. + * This is to escape the slash character when it is part of the domain + * name string. It can be used normally when it is in the config string. */ +const char* +escape_slash_zonefile(const char* input) +{ + static char f[1024]; + if(!strchr(input, '/')) + return input; + strlcpy(f, input, sizeof(f)); + replace_str(f, sizeof(f), "/", "\\047"); + return f; +} + const char* config_cook_string(struct zone_options* zone, const char* input) { @@ -2548,7 +2637,8 @@ config_cook_string(struct zone_options* if(strstr(f, "%x")) replace_str(f, sizeof(f), "%x", get_end_label(zone, 3)); if(strstr(f, "%s")) - replace_str(f, sizeof(f), "%s", zone->name); + replace_str(f, sizeof(f), "%s", escape_slash_zonefile( + zone->name)); return f; } @@ -2581,7 +2671,8 @@ config_make_zonefile(struct zone_options if(strstr(f, "%x")) replace_str(f, sizeof(f), "%x", get_end_label(zone, 3)); if(strstr(f, "%s")) - replace_str(f, sizeof(f), "%s", zone->name); + replace_str(f, sizeof(f), "%s", escape_slash_zonefile( + zone->name)); if (nsd->chrootdir && nsd->chrootdir[0] && f[0] == '/' && strncmp(f, nsd->chrootdir, strlen(nsd->chrootdir)) == 0) /* -1 because chrootdir ends in trailing slash */ @@ -2924,12 +3015,30 @@ unsigned getzonestatid(struct nsd_option { #ifdef USE_ZONE_STATS const char* statname; + char* statname_valid; + int name_was_modified; struct zonestatname* n; rbnode_type* res; /* try to find the instantiated zonestat name */ if(!zopt->pattern->zonestats || zopt->pattern->zonestats[0]==0) return 0; /* no zone stats */ statname = config_cook_string(zopt, zopt->pattern->zonestats); + + #ifdef USE_METRICS + /* warn when we will lossily change the zonestat name in metrics */ + statname_valid = strdup(statname); + if(!statname_valid) { + log_msg(LOG_ERR, "malloc failed: %s", strerror(errno)); + exit(1); + } + name_was_modified = metrics_make_label_value_valid(statname_valid); + if (name_was_modified) { + log_msg(LOG_WARNING, "zonestats name \"%s\" contains disallowed characters, using \"%s\" in metrics", + statname, statname_valid); + } + free(statname_valid); + #endif /* USE_METRICS */ + res = rbtree_search(opt->zonestatnames, statname); if(res) return ((struct zonestatname*)res)->id; @@ -3107,10 +3216,8 @@ resolve_interface_names(struct nsd_optio } int -sockaddr_uses_proxy_protocol_port(struct nsd_options* options, - struct sockaddr* addr) +sockaddr_uses_port(struct sockaddr* addr, struct port_list* p) { - struct proxy_protocol_port_list* p; int port; #ifdef INET6 struct sockaddr_storage* ss = (struct sockaddr_storage*)addr; @@ -3130,7 +3237,6 @@ sockaddr_uses_proxy_protocol_port(struct return 0; /* unknown family */ } #endif - p = options->proxy_protocol_port; while(p) { if(p->port == port) return 1; Index: usr.sbin/nsd/options.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/options.h,v diff -u -p -r1.31 options.h --- usr.sbin/nsd/options.h 6 Sep 2025 17:41:37 -0000 1.31 +++ usr.sbin/nsd/options.h 21 Sep 2026 16:29:07 -0000 @@ -21,7 +21,7 @@ struct dname; struct tsig_key; struct buffer; struct nsd; -struct proxy_protocol_port_list; +struct port_list; typedef struct nsd_options nsd_options_type; @@ -103,6 +103,7 @@ struct nsd_options { int tcp_timeout; int tcp_mss; int outgoing_tcp_mss; + int tcp_listen_queue; size_t ipv4_edns_size; size_t ipv6_edns_size; const char* pidfile; @@ -146,7 +147,11 @@ struct nsd_options { int tls_auth_xfr_only; /* proxy protocol port list */ - struct proxy_protocol_port_list* proxy_protocol_port; + struct port_list* proxy_protocol_port; + /* Allowed proxy senders (the outer IP addr), it allows all if empty */ + struct acl_options* allow_proxy; + /* udp-padding-port list */ + struct port_list* udp_padding_port; /** remote control section. enable toggle. */ int control_enable; @@ -462,9 +467,9 @@ struct tls_auth_options { char* client_key_pw; }; -/* proxy protocol port option list */ -struct proxy_protocol_port_list { - struct proxy_protocol_port_list* next; +/* port option list */ +struct port_list { + struct port_list* next; int port; }; @@ -598,6 +603,7 @@ int acl_addr_matches_proxy(struct acl_op 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_tls_auth_name_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); int acl_addr_match_range_v4(uint32_t* minval, uint32_t* x, uint32_t* maxval, size_t sz); @@ -605,6 +611,9 @@ int acl_addr_match_range_v4(uint32_t* mi /* check acl list for blocks on address, return 0 if none, -1 if blocked. */ int acl_check_incoming_block_proxy(struct acl_options* acl, struct query* q, struct acl_options** reason); +/* check acl list, proxy addr, if match return 1, -1 if no matches. */ +int acl_check_incoming_proxy(struct acl_options* acl, struct query* q, + struct acl_options** reason); /* returns true if acls are both from the same host */ int acl_same_host(struct acl_options* a, struct acl_options* b); @@ -640,6 +649,7 @@ const char* config_make_zonefile(struct /* parsing helpers */ void c_error(const char* msg, ...) ATTR_FORMAT(printf, 1,2); +void c_warning(const char* msg, ...) ATTR_FORMAT(printf, 1,2); int c_wrap(void); struct acl_options* parse_acl_info(region_type* region, char* ip, const char* key); @@ -664,8 +674,7 @@ void warn_if_directory(const char* filet * names. */ void resolve_interface_names(struct nsd_options* options); -/* See if the sockaddr port number is listed in the proxy protocol ports. */ -int sockaddr_uses_proxy_protocol_port(struct nsd_options* options, - struct sockaddr* addr); +/* See if the sockaddr port number is listed in the ports. */ +int sockaddr_uses_port(struct sockaddr* addr, struct port_list* ports); #endif /* OPTIONS_H */ Index: usr.sbin/nsd/packet.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/packet.c,v diff -u -p -r1.3 packet.c --- usr.sbin/nsd/packet.c 14 May 2020 06:08:40 -0000 1.3 +++ usr.sbin/nsd/packet.c 21 Sep 2026 16:29:07 -0000 @@ -14,6 +14,7 @@ #include "packet.h" #include "query.h" #include "rdata.h" +#include "dns.h" int round_robin = 0; int minimal_responses = 0; @@ -52,7 +53,8 @@ packet_encode_rr(query_type *q, domain_t size_t truncation_mark; uint16_t rdlength = 0; size_t rdlength_pos; - uint16_t j; + const nsd_type_descriptor_type *descriptor = + nsd_type_descriptor(rr->type); assert(q); assert(owner); @@ -73,26 +75,7 @@ packet_encode_rr(query_type *q, domain_t rdlength_pos = buffer_position(q->packet); buffer_skip(q->packet, sizeof(rdlength)); - for (j = 0; j < rr->rdata_count; ++j) { - switch (rdata_atom_wireformat_type(rr->type, j)) { - case RDATA_WF_COMPRESSED_DNAME: - encode_dname(q, rdata_atom_domain(rr->rdatas[j])); - break; - case RDATA_WF_UNCOMPRESSED_DNAME: - { - const dname_type *dname = domain_dname( - rdata_atom_domain(rr->rdatas[j])); - buffer_write(q->packet, - dname_name(dname), dname->name_size); - break; - } - default: - buffer_write(q->packet, - rdata_atom_data(rr->rdatas[j]), - rdata_atom_size(rr->rdatas[j])); - break; - } - } + descriptor->write_rdata(q, rr); if (!query_overflow(q)) { rdlength = (buffer_position(q->packet) - rdlength_pos @@ -147,8 +130,8 @@ packet_encode_rrset(query_type *query, start = (uint16_t)(round_robin_off++ % rrset->rr_count); else start = 0; for (i = start; i < rrset->rr_count; ++i) { - if (packet_encode_rr(query, owner, &rrset->rrs[i], - rrset->rrs[i].ttl)) { + if (packet_encode_rr(query, owner, rrset->rrs[i], + rrset->rrs[i]->ttl)) { ++added; } else { all_added = 0; @@ -157,8 +140,8 @@ packet_encode_rrset(query_type *query, } } for (i = 0; i < start; ++i) { - if (packet_encode_rr(query, owner, &rrset->rrs[i], - rrset->rrs[i].ttl)) { + if (packet_encode_rr(query, owner, rrset->rrs[i], + rrset->rrs[i]->ttl)) { ++added; } else { all_added = 0; @@ -173,12 +156,12 @@ packet_encode_rrset(query_type *query, (rrsig = domain_find_rrset(owner, rrset->zone, TYPE_RRSIG))) { for (i = 0; i < rrsig->rr_count; ++i) { - if (rr_rrsig_type_covered(&rrsig->rrs[i]) + if (rr_rrsig_type_covered(rrsig->rrs[i]) == rrset_rrtype(rrset)) { if (packet_encode_rr(query, owner, - &rrsig->rrs[i], - rrset_rrtype(rrset)==TYPE_SOA?rrset->rrs[0].ttl:rrsig->rrs[i].ttl)) + rrsig->rrs[i], + rrset_rrtype(rrset)==TYPE_SOA?rrset->rrs[0]->ttl:rrsig->rrs[i]->ttl)) { ++added; } else { @@ -263,46 +246,54 @@ rr_type * packet_read_rr(region_type *region, domain_table_type *owners, buffer_type *packet, int question_section) { + const nsd_type_descriptor_type *descriptor; const dname_type *owner; - uint16_t rdlength; - ssize_t rdata_count; - rdata_atom_type *rdatas; - rr_type *result = (rr_type *) region_alloc(region, sizeof(rr_type)); + struct domain* domain; + uint16_t type, class, rdlength; + uint32_t ttl; + struct rr *rr; + int32_t code; owner = dname_make_from_packet(region, packet, 1, 1); if (!owner || !buffer_available(packet, 2*sizeof(uint16_t))) { return NULL; } - result->owner = domain_table_insert(owners, owner); - result->type = buffer_read_u16(packet); - result->klass = buffer_read_u16(packet); + domain = domain_table_insert(owners, owner); + type = buffer_read_u16(packet); + class = buffer_read_u16(packet); if (question_section) { + rr_type *result = (rr_type *) region_alloc(region, + sizeof(rr_type)); + result->owner = domain; + result->type = type; + result->klass = class; result->ttl = 0; - result->rdata_count = 0; - result->rdatas = NULL; + result->rdlength = 0; return result; } else if (!buffer_available(packet, sizeof(uint32_t) + sizeof(uint16_t))) { return NULL; } - result->ttl = buffer_read_u32(packet); + ttl = buffer_read_u32(packet); rdlength = buffer_read_u16(packet); if (!buffer_available(packet, rdlength)) { return NULL; } - rdata_count = rdata_wireformat_to_rdata_atoms( - region, owners, result->type, rdlength, packet, &rdatas); - if (rdata_count == -1) { + descriptor = nsd_type_descriptor(type); + code = descriptor->read_rdata(owners, rdlength, packet, &rr); + if(code < 0) { return NULL; } - result->rdata_count = rdata_count; - result->rdatas = rdatas; + rr->owner = domain; + rr->type = type; + rr->klass = class; + rr->ttl = ttl; - return result; + return rr; } int packet_read_query_section(buffer_type *packet, Index: usr.sbin/nsd/query.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/query.c,v diff -u -p -r1.44 query.c --- usr.sbin/nsd/query.c 6 Sep 2025 17:41:37 -0000 1.44 +++ usr.sbin/nsd/query.c 21 Sep 2026 16:29:07 -0000 @@ -36,6 +36,7 @@ #include "options.h" #include "nsec3.h" #include "tsig.h" +#include "rdata.h" /* [Bug #253] Adding unnecessary NS RRset may lead to undesired truncation. * This function determines if the final response packet needs the NS RRset @@ -248,7 +249,9 @@ query_reset(query_type *q, size_t maxlen region_free_all(q->region); q->remote_addrlen = (socklen_t)sizeof(q->remote_addr); q->client_addrlen = (socklen_t)sizeof(q->client_addr); - q->is_proxied = 0; + q->may_pad = 0; + if(!is_tcp) + q->is_proxied = 0; q->maxlen = maxlen; q->reserved_space = 0; buffer_clear(q->packet); @@ -496,6 +499,7 @@ answer_notify(struct nsd* nsd, struct qu { int s = nsd->serve2xfrd_fd_send[nsd->this_child->child_num]; uint16_t sz; + uint32_t sz32; uint32_t acl_send = htonl(acl_num); uint32_t acl_xfr; size_t pos; @@ -516,8 +520,11 @@ answer_notify(struct nsd* nsd, struct qu 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) + sz32 = buffer_limit(query->packet) + sizeof(acl_send) + sizeof(acl_xfr); + if(sz32 > 0xFFFF) + return query_error(query, NSD_RC_SERVFAIL); + sz = (uint16_t)sz32; sz = htons(sz); if(!write_socket(s, &sz, sizeof(sz)) || !write_socket(s, buffer_begin(query->packet), @@ -706,7 +713,7 @@ struct additional_rr_types rt_additional static void add_additional_rrsets(struct query *query, answer_type *answer, - rrset_type *master_rrset, size_t rdata_index, + rrset_type *master_rrset, uint16_t rdata_offset, int allow_glue, struct additional_rr_types types[]) { size_t i; @@ -714,11 +721,11 @@ add_additional_rrsets(struct query *quer assert(query); assert(answer); assert(master_rrset); - assert(rdata_atom_is_domain(rrset_rrtype(master_rrset), rdata_index)); for (i = 0; i < master_rrset->rr_count; ++i) { int j; - domain_type *additional = rdata_atom_domain(master_rrset->rrs[i].rdatas[rdata_index]); + domain_type *additional = rdata_domain_ref_offset( + master_rrset->rrs[i], rdata_offset); domain_type *match = additional; assert(additional); @@ -796,30 +803,40 @@ add_rrset(struct query *query, case TYPE_NS: #if defined(INET6) /* if query over IPv6, swap A and AAAA; put AAAA first */ - add_additional_rrsets(query, answer, rrset, 0, 1, + add_additional_rrsets(query, answer, rrset, + 0, 1, (query->client_addr.ss_family == AF_INET6)? swap_aaaa_additional_rr_types: default_additional_rr_types); #else - add_additional_rrsets(query, answer, rrset, 0, 1, + add_additional_rrsets(query, answer, rrset, + 0, 1, default_additional_rr_types); #endif break; case TYPE_MB: - add_additional_rrsets(query, answer, rrset, 0, 0, + add_additional_rrsets(query, answer, rrset, + 0, 0, default_additional_rr_types); break; case TYPE_MX: + add_additional_rrsets(query, answer, rrset, + 2, 0, + default_additional_rr_types); + break; case TYPE_KX: - add_additional_rrsets(query, answer, rrset, 1, 0, + add_additional_rrsets(query, answer, rrset, + 2, 0, default_additional_rr_types); break; case TYPE_RT: - add_additional_rrsets(query, answer, rrset, 1, 0, + add_additional_rrsets(query, answer, rrset, + 2, 0, rt_additional_rr_types); break; case TYPE_SRV: - add_additional_rrsets(query, answer, rrset, 3, 0, + add_additional_rrsets(query, answer, rrset, + 6, 0, default_additional_rr_types); break; default: @@ -856,10 +873,10 @@ query_synthesize_cname(struct query* q, for(j=0; jrrset_count; ++j) { if(answer->section[j] == ANSWER_SECTION && answer->rrsets[j]->rr_count == 1 && - answer->rrsets[j]->rrs[0].type == TYPE_CNAME && - dname_compare(domain_dname(answer->rrsets[j]->rrs[0].owner), from_name) == 0 && - answer->rrsets[j]->rrs[0].rdata_count == 1 && - dname_compare(domain_dname(answer->rrsets[j]->rrs[0].rdatas->domain), to_name) == 0) { + answer->rrsets[j]->rrs[0]->type == TYPE_CNAME && + dname_compare(domain_dname(answer->rrsets[j]->rrs[0]->owner), from_name) == 0 && + answer->rrsets[j]->rrs[0]->rdlength >= 1 && + dname_compare(domain_dname(rdata_domain_ref(answer->rrsets[j]->rrs[0])), to_name) == 0) { DEBUG(DEBUG_QUERY,2, (LOG_INFO, "loop for synthesized CNAME rrset for query %s", dname_to_string(q->qname, NULL))); return 0; } @@ -915,20 +932,26 @@ query_synthesize_cname(struct query* q, *to_closest_match = cname_dest; /* allocate the CNAME RR */ - rrset = (rrset_type*) region_alloc(q->region, sizeof(rrset_type)); + rrset = (rrset_type*) region_alloc(q->region, sizeof(rrset_type) +#ifdef PACKED_STRUCTS + + sizeof(rr_type*) +#endif + ); memset(rrset, 0, sizeof(rrset_type)); rrset->zone = q->zone; rrset->rr_count = 1; - rrset->rrs = (rr_type*) region_alloc(q->region, sizeof(rr_type)); - memset(rrset->rrs, 0, sizeof(rr_type)); - rrset->rrs->owner = cname_domain; - rrset->rrs->ttl = ttl; - rrset->rrs->type = TYPE_CNAME; - rrset->rrs->klass = CLASS_IN; - rrset->rrs->rdata_count = 1; - rrset->rrs->rdatas = (rdata_atom_type*)region_alloc(q->region, - sizeof(rdata_atom_type)); - rrset->rrs->rdatas->domain = cname_dest; +#ifndef PACKED_STRUCTS + rrset->rrs = (rr_type**) region_alloc(q->region, sizeof(rr_type*)); +#endif + rrset->rrs[0] = (rr_type*) region_alloc(q->region, + sizeof(rr_type)+sizeof(void*)); + memset(rrset->rrs[0], 0, sizeof(rr_type)); + rrset->rrs[0]->owner = cname_domain; + rrset->rrs[0]->ttl = ttl; + rrset->rrs[0]->type = TYPE_CNAME; + rrset->rrs[0]->klass = CLASS_IN; + rrset->rrs[0]->rdlength = sizeof(void*); + memcpy(rrset->rrs[0]->rdata, &cname_dest, sizeof(void*)); if(!add_rrset(q, answer, ANSWER_SECTION, cname_domain, rrset)) { DEBUG(DEBUG_QUERY,2, (LOG_INFO, "could not add synthesized CNAME rrset to packet for query %s", dname_to_string(q->qname, NULL))); @@ -1010,7 +1033,8 @@ answer_nodata(struct query *query, answe answer_soa(query, answer); #ifdef NSEC3 - if (query->edns.dnssec_ok && query->zone->nsec3_param) { + if (query->edns.dnssec_ok && query->zone->nsec3_param && + zone_is_secure(query->zone)) { nsec3_answer_nodata(query, answer, original); } else #endif @@ -1116,11 +1140,14 @@ answer_domain(struct nsd* nsd, struct qu assert(rrset->rr_count > 0); if (added) { /* only process first CNAME record */ - domain_type *closest_match = rdata_atom_domain(rrset->rrs[0].rdatas[0]); + domain_type *closest_match = rdata_domain_ref(rrset->rrs[0]); domain_type *closest_encloser = closest_match; zone_type* origzone = q->zone; ++q->cname_count; + if (q->cname_count >= MAX_CNAME_CHAIN) { + return; + } answer_lookup_zone(nsd, q, answer, closest_match->number, closest_match == closest_encloser, closest_match, closest_encloser, @@ -1185,7 +1212,7 @@ answer_authoritative(struct nsd *nsd, /* process DNAME */ const dname_type* name = qname; domain_type* src = closest_encloser; - domain_type *dest = rdata_atom_domain(rrset->rrs[0].rdatas[0]); + domain_type *dest = rdata_domain_ref(rrset->rrs[0]); const dname_type* newname; size_t newnum = 0; zone_type* origzone = q->zone; @@ -1215,11 +1242,12 @@ answer_authoritative(struct nsd *nsd, return; } DEBUG(DEBUG_QUERY,2, (LOG_INFO, "->result is %s", dname_to_string(newname, NULL))); + /* follow the DNAME */ (void)namedb_lookup(nsd->db, newname, &closest_match, &closest_encloser); /* synthesize CNAME record */ newnum = query_synthesize_cname(q, answer, name, newname, - src, closest_encloser, &closest_match, rrset->rrs[0].ttl); + src, closest_encloser, &closest_match, rrset->rrs[0]->ttl); if(!newnum) { /* could not synthesize the CNAME. */ /* return previous CNAMEs to make resolver recurse for us */ @@ -1231,6 +1259,9 @@ answer_authoritative(struct nsd *nsd, * of type CNAME */ return; } + if (q->cname_count >= MAX_CNAME_CHAIN) { + return; + } answer_lookup_zone(nsd, q, answer, newnum, closest_match == closest_encloser, @@ -1755,6 +1786,16 @@ query_process(query_type *q, nsd_type *n cookie_verify(q, nsd, now_p); query_prepare_response(q); + if(q->reserved_space + QHEADERSZ + (size_t)q->qname->name_size + + 2 /* qtype */ + 2 /* qclass */ > q->maxlen) { + /* Clear out some space, and return error, it does not fit. */ + q->edns.status = EDNS_NOT_PRESENT; + q->tsig.status = TSIG_NOT_PRESENT; + if(q->tcp) + return query_error(q, NSD_RC_SERVFAIL); + TC_SET(q->packet); + return query_error(q, NSD_RC_OK); + } if (q->qclass != CLASS_IN && q->qclass != CLASS_ANY) { if (q->qclass == CLASS_CH) { @@ -1811,14 +1852,28 @@ query_add_optional(query_type *q, nsd_ty if(q->edns.zoneversion && q->zone && q->zone->soa_rrset - && q->zone->soa_rrset->rrs - && q->zone->soa_rrset->rrs->rdata_count >= 3) + && q->zone->soa_rrset->rr_count >= 1 + && q->zone->soa_rrset->rrs[0]->rdlength >= 20 /* 5x 32bit numbers */ +2*sizeof(void*) /* two pointers to domain names */) q->edns.opt_reserved_space += sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t); - + if(q->edns.padding) { + size_t cur_sz = buffer_position(q->packet) + 2 + q->edns.opt_reserved_space; + size_t padded_sz = (((cur_sz - 1) / PADDING_BLOCK_SZ) + 1) * PADDING_BLOCK_SZ; + size_t to_padd = padded_sz - cur_sz; + /* Need 4 bytes for option code and length */ + q->edns.padding = to_padd >= 4 ? to_padd + : to_padd > 0 ? (PADDING_BLOCK_SZ + to_padd) + : 0; /* Multiple of PADDING_BLOCK_SZ, + * so no outgoing padding option */ + if(!buffer_available(q->packet, 2+q->edns.opt_reserved_space+q->edns.padding) || cur_sz + q->edns.padding > 65535) + q->edns.padding = 0; + if(q->edns.padding) { + q->edns.opt_reserved_space += q->edns.padding; + } + } if(q->edns.opt_reserved_space == 0 || !buffer_available( q->packet, 2+q->edns.opt_reserved_space)) { /* fill with NULLs */ @@ -1836,8 +1891,9 @@ query_add_optional(query_type *q, nsd_ty if(q->edns.zoneversion && q->zone && q->zone->soa_rrset - && q->zone->soa_rrset->rrs - && q->zone->soa_rrset->rrs->rdata_count >= 3) { + && q->zone->soa_rrset->rr_count >= 1 + && q->zone->soa_rrset->rrs[0]->rdlength >= 20+2*sizeof(void*) /* 5x4 bytes and 2 pointers to domains */ ) { + uint32_t serial = 0; buffer_write_u16(q->packet, ZONEVERSION_CODE); buffer_write_u16( q->packet , sizeof(uint8_t) @@ -1847,9 +1903,8 @@ query_add_optional(query_type *q, nsd_ty 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]))); + retrieve_soa_rdata_serial(q->zone->soa_rrset->rrs[0], &serial); + buffer_write_u32(q->packet, serial); } if(q->edns.cookie_status != COOKIE_NOT_PRESENT) { /* cookie opt header */ @@ -1873,6 +1928,12 @@ query_add_optional(query_type *q, nsd_ty buffer_write(q->packet, q->edns.ede_text, q->edns.ede_text_len); + } + if(q->edns.padding) { + assert(q->edns.padding >= 4); + buffer_write_u16(q->packet, PADDING_CODE); + buffer_write_u16(q->packet, q->edns.padding - 4); + buffer_fill(q->packet, 0, q->edns.padding - 4); } } ARCOUNT_SET(q->packet, ARCOUNT(q->packet) + 1); Index: usr.sbin/nsd/query.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/query.h,v diff -u -p -r1.10 query.h --- usr.sbin/nsd/query.h 6 Sep 2025 17:41:37 -0000 1.10 +++ usr.sbin/nsd/query.h 21 Sep 2026 16:29:07 -0000 @@ -47,6 +47,9 @@ struct query { /* if set, the request came through a proxy */ int is_proxied; + /* if set, the response may be padded */ + int may_pad; + /* the client address * the same as remote_addr if not proxied */ #ifdef INET6 Index: usr.sbin/nsd/rdata.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/rdata.c,v diff -u -p -r1.19 rdata.c --- usr.sbin/nsd/rdata.c 6 Sep 2025 17:41:37 -0000 1.19 +++ usr.sbin/nsd/rdata.c 21 Sep 2026 16:29:08 -0000 @@ -24,6 +24,8 @@ #include "rdata.h" #include "zonec.h" +#include "query.h" +#include "dname.h" /* Taken from RFC 4398, section 2.1. */ lookup_table_type dns_certificate_types[] = { @@ -67,150 +69,241 @@ lookup_table_type dns_algorithms[] = { { 0, NULL } }; -const char *svcparamkey_strs[] = { - "mandatory", "alpn", "no-default-alpn", "port", - "ipv4hint", "ech", "ipv6hint", "dohpath", "ohttp", - "tls-supported-groups" - }; - -typedef int (*rdata_to_string_type)(buffer_type *output, - rdata_atom_type rdata, - rr_type *rr); +/* Print svcparam key without a value */ +static int print_svcparam_no_value(struct buffer *output, uint16_t svcparamkey, + const uint8_t* data, uint16_t datalen); + +/* Print svcparam mandatory */ +static int print_svcparam_mandatory(struct buffer *output, + uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); + +/* Print svcparam alpn */ +static int print_svcparam_alpn(struct buffer *output, + uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); + +/* Print svcparam port */ +static int print_svcparam_port(struct buffer *output, + uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); + +/* Print svcparam ipv4hint */ +static int print_svcparam_ipv4hint(struct buffer *output, + uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); + +/* Print svcparam ech */ +static int print_svcparam_ech(struct buffer *output, + uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); + +/* Print svcparam ipv6hint */ +static int print_svcparam_ipv6hint(struct buffer *output, + uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); + +/* Print svcparam dohpath */ +static int print_svcparam_dohpath(struct buffer *output, + uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); + +/* Print svcparam tls-supported-groups */ +static int print_svcparam_tls_supported_groups(struct buffer *output, + uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); + +/* Print svcparam docpath */ +static int print_svcparam_docpath(struct buffer *output, + uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); + +/* Print svcparam oots */ +static int print_svcparam_oots(struct buffer *output, + uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); + +static const nsd_svcparam_descriptor_type svcparams[] = { + { SVCB_KEY_MANDATORY, "mandatory", print_svcparam_mandatory }, + { SVCB_KEY_ALPN, "alpn", print_svcparam_alpn }, + { SVCB_KEY_NO_DEFAULT_ALPN, "no-default-alpn", + print_svcparam_no_value }, + { SVCB_KEY_PORT, "port", print_svcparam_port }, + { SVCB_KEY_IPV4HINT, "ipv4hint", print_svcparam_ipv4hint }, + { SVCB_KEY_ECH, "ech", print_svcparam_ech }, + { SVCB_KEY_IPV6HINT, "ipv6hint", print_svcparam_ipv6hint }, + { SVCB_KEY_DOHPATH, "dohpath", print_svcparam_dohpath }, + { SVCB_KEY_OHTTP, "ohttp", print_svcparam_no_value }, + { SVCB_KEY_TLS_SUPPORTED_GROUPS, "tls-supported-groups", + print_svcparam_tls_supported_groups }, + { SVCB_KEY_DOCPATH, "docpath", print_svcparam_docpath}, + { SVCB_KEY_PVD, "pvd", print_svcparam_no_value }, + { SVCB_KEY_OOTS, "oots", print_svcparam_oots}, +}; +/* + * Print domain name, as example.com. with escapes. + * The domain name is wireformat in the rdata. That is a literal dname + * in the rdata. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_dname_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_name_literal(struct buffer *output, uint16_t rdlength, + const uint8_t *rdata, uint16_t *offset) { - buffer_printf(output, - "%s", - dname_to_string(domain_dname(rdata_atom_domain(rdata)), - NULL)); + const uint8_t *name, *label, *limit; + assert(rdlength >= *offset); + if (rdlength < *offset || rdlength - *offset == 0) + return 0; + + name = rdata + *offset; + label = name; + limit = rdata + rdlength; + + if(*label) { + do { + /* space for labellen, label and a next root label. */ + if (label - name > MAXDOMAINLEN-1 + || *label > MAXLABELLEN + || limit - label < 2 + *label) + return 0; + label += 1 + *label; + } while (*label); + } else { + /* root domain. */ + /* The label is within the rdlength by the checks at start of + * the function. */ + } + + buffer_printf(output, "%s", wiredname2str(name)); + *offset += (label - name) + 1 /* root label */; return 1; } +/* + * Print domain name, as example.com. with escapes. + * The domain must be a reference in the rdata. That is a stored pointer + * to struct domain. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_dns_name_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_domain(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, + uint16_t *offset) { - const uint8_t *data = rdata_atom_data(rdata); - size_t offset = 0; - uint8_t length = data[offset]; - size_t i; + const struct dname *dname; + struct domain *domain; + if(rdlength < *offset || rdlength - *offset < (uint16_t)sizeof(void*)) + return 0; + memcpy(&domain, rdata+*offset, sizeof(void*)); + dname = domain_dname(domain); + buffer_printf(output, "%s", dname_to_string(dname, NULL)); + *offset += sizeof(void*); + return 1; +} - while (length > 0) - { - if (offset) /* concat label */ - buffer_printf(output, "."); - - for (i = 1; i <= length; ++i) { - uint8_t ch = data[i+offset]; - - if (ch=='.' || ch==';' || ch=='(' || ch==')' || ch=='\\') { - buffer_printf(output, "\\%c", (char) ch); - } else if (!isgraph((unsigned char) ch)) { - buffer_printf(output, "\\%03u", (unsigned int) ch); - } else if (isprint((unsigned char) ch)) { - buffer_printf(output, "%c", (char) ch); - } else { - buffer_printf(output, "\\%03u", (unsigned int) ch); - } - } - /* next label */ - offset = offset+length+1; - length = data[offset]; - } +/* Return length of string or -1 on wireformat error. offset is moved +len. */ +static inline int32_t +skip_string(struct buffer* output, uint16_t rdlength, uint16_t* offset) +{ + int32_t length; + if (rdlength < *offset || rdlength - *offset < 1) + return -1; + length = buffer_read_u8(output); + if (length + 1 > rdlength - *offset) + return -1; + buffer_skip(output, length); + *offset += length + 1; + return length + 1; +} - /* root label */ - buffer_printf(output, "."); - return 1; +/* Return length of strings or -1 on wireformat error. offset is moved +len. */ +static inline int32_t +skip_strings(struct buffer* output, uint16_t rdlength, uint16_t* offset) +{ + int32_t olen = 0; + while(*offset < rdlength) { + int32_t slen = skip_string(output, rdlength, offset); + if(slen < 0) + return slen; + olen += slen; + } + return olen; } +/* + * Print string, as "string" with escapes. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_text_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_string(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, + uint16_t *offset) { - const uint8_t *data = rdata_atom_data(rdata); - uint8_t length = data[0]; - size_t i; - + size_t n; + if(rdlength - *offset < 1) + return 0; + n = rdata[*offset]; + if(rdlength < *offset || (size_t)rdlength - *offset < 1 + n) + return 0; buffer_printf(output, "\""); - for (i = 1; i <= length; ++i) { - char ch = (char) data[i]; + for (size_t i = 1; i <= n; i++) { + char ch = (char) rdata[*offset+i]; if (isprint((unsigned char)ch)) { if (ch == '"' || ch == '\\') { buffer_printf(output, "\\"); } buffer_printf(output, "%c", ch); } else { - buffer_printf(output, "\\%03u", (unsigned) data[i]); + buffer_printf(output, "\\%03u", + (unsigned) rdata[*offset+i]); } } buffer_printf(output, "\""); + *offset += 1; + *offset += n; return 1; } -static int -rdata_texts_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) { - buffer_printf(output, "\""); - for (i = 1; i <= data[pos]; ++i) { - char ch = (char) data[pos + i]; - if (isprint((unsigned char)ch)) { - if (ch == '"' || 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_long_text_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +static int32_t +print_text(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, + uint16_t *offset) { - const uint8_t *data = rdata_atom_data(rdata); - uint16_t length = rdata_atom_size(rdata); - size_t i; - buffer_printf(output, "\""); - for (i = 0; i < length; ++i) { - char ch = (char) data[i]; + for (size_t i = *offset; i < rdlength; ++i) { + char ch = (char) rdata[i]; if (isprint((unsigned char)ch)) { if (ch == '"' || ch == '\\') { buffer_printf(output, "\\"); } buffer_printf(output, "%c", ch); } else { - buffer_printf(output, "\\%03u", (unsigned) data[i]); + buffer_printf(output, "\\%03u", (unsigned) rdata[i]); } } buffer_printf(output, "\""); + *offset = rdlength; return 1; } static int -rdata_unquoted_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_unquoted(buffer_type *output, uint16_t rdlength, + const uint8_t* rdata, uint16_t* offset) { - const uint8_t *data = rdata_atom_data(rdata); - uint8_t length = data[0]; + uint8_t len; size_t i; - for (i = 1; i <= length; ++i) { - char ch = (char) data[i]; + if(rdlength < *offset || rdlength - *offset < 1) + return 0; + len = rdata[*offset]; + if(((size_t)len) + 1 > (size_t)rdlength - *offset) + return 0; + + for (i = 1; i <= (size_t)len; ++i) { + char ch = (char) rdata[*offset + i]; if (isprint((unsigned char)ch)) { if (ch == '"' || ch == '\\' || ch == '(' || ch == ')' || ch == '\'' || isspace((unsigned char)ch)) { @@ -218,267 +311,227 @@ rdata_unquoted_to_string(buffer_type *ou } 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]); - } + buffer_printf(output, "\\%03u", + (unsigned) rdata[*offset + i]); } - pos += data[pos]+1; - buffer_printf(output, pos < length?" ":""); } + *offset += 1; + *offset += len; return 1; } static int -rdata_tag_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_unquoteds(buffer_type *output, uint16_t rdlength, + const uint8_t* rdata, uint16_t* offset) { - 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 (isdigit((unsigned char)ch) || islower((unsigned char)ch)) - buffer_printf(output, "%c", ch); - else return 0; + while (*offset < rdlength) { + if(!print_unquoted(output, rdlength, rdata, offset)) + return 0; + if(*offset < rdlength) + buffer_printf(output, " "); } return 1; } +/* + * Print IP4 address. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_byte_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - uint8_t data = *rdata_atom_data(rdata); - buffer_printf(output, "%lu", (unsigned long) data); - return 1; -} - -static int -rdata_short_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - uint16_t data = read_uint16(rdata_atom_data(rdata)); - buffer_printf(output, "%lu", (unsigned long) data); - return 1; -} - -static int -rdata_long_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_ip4(struct buffer *output, size_t rdlength, const uint8_t *rdata, + uint16_t *offset) { - uint32_t data = read_uint32(rdata_atom_data(rdata)); - buffer_printf(output, "%lu", (unsigned long) data); + char str[INET_ADDRSTRLEN + 1]; + if(((size_t)*offset) + 4 > rdlength) + return 0; + if(!inet_ntop(AF_INET, rdata + *offset, str, sizeof(str))) + return 0; + buffer_printf(output, "%s", str); + *offset += 4; return 1; } +/* + * Print IP6 address. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_longlong_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_ip6(struct buffer *output, size_t rdlength, const uint8_t *rdata, + uint16_t *offset) { - uint64_t data = read_uint64(rdata_atom_data(rdata)); - buffer_printf(output, "%llu", (unsigned long long) data); + char str[INET6_ADDRSTRLEN + 1]; + if (rdlength < *offset || rdlength - *offset < 16) + return 0; + if (!inet_ntop(AF_INET6, rdata + *offset, str, sizeof(str))) + return 0; + buffer_printf(output, "%s", str); + *offset += 16; return 1; } +/* + * Print ilnp64 field. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_a_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - int result = 0; - char str[200]; - if (inet_ntop(AF_INET, rdata_atom_data(rdata), str, sizeof(str))) { - buffer_printf(output, "%s", str); - result = 1; - } - return result; -} - -static int -rdata_aaaa_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - int result = 0; - char str[200]; - if (inet_ntop(AF_INET6, rdata_atom_data(rdata), str, sizeof(str))) { - buffer_printf(output, "%s", str); - result = 1; - } - return result; -} - -static int -rdata_ilnp64_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_ilnp64(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, + uint16_t *offset) { - uint8_t* data = rdata_atom_data(rdata); - uint16_t a1 = read_uint16(data); - uint16_t a2 = read_uint16(data+2); - uint16_t a3 = read_uint16(data+4); - uint16_t a4 = read_uint16(data+6); + uint16_t a1, a2, a3, a4; + if (rdlength < *offset || rdlength - *offset < 8) + return 0; + a1 = read_uint16(rdata + *offset); + a2 = read_uint16(rdata + *offset + 2); + a3 = read_uint16(rdata + *offset + 4); + a4 = read_uint16(rdata + *offset + 6); buffer_printf(output, "%.4x:%.4x:%.4x:%.4x", a1, a2, a3, a4); + *offset += 8; return 1; } +/* + * Print certificate type. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_eui48_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - uint8_t* data = rdata_atom_data(rdata); - uint8_t a1 = data[0]; - uint8_t a2 = data[1]; - uint8_t a3 = data[2]; - uint8_t a4 = data[3]; - uint8_t a5 = data[4]; - uint8_t a6 = data[5]; - - buffer_printf(output, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", - a1, a2, a3, a4, a5, a6); - return 1; -} - -static int -rdata_eui64_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - uint8_t* data = rdata_atom_data(rdata); - uint8_t a1 = data[0]; - uint8_t a2 = data[1]; - uint8_t a3 = data[2]; - uint8_t a4 = data[3]; - uint8_t a5 = data[4]; - uint8_t a6 = data[5]; - uint8_t a7 = data[6]; - uint8_t a8 = data[7]; - - buffer_printf(output, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", - a1, a2, a3, a4, a5, a6, a7, a8); - return 1; -} - -static int -rdata_rrtype_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - uint16_t type = read_uint16(rdata_atom_data(rdata)); - buffer_printf(output, "%s", rrtype_to_string(type)); - return 1; -} - -static int -rdata_algorithm_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - uint8_t id = *rdata_atom_data(rdata); - buffer_printf(output, "%u", (unsigned) id); - return 1; -} - -static int -rdata_certificate_type_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_certificate_type(struct buffer *output, size_t rdlength, + const uint8_t *rdata, uint16_t *offset) { - uint16_t id = read_uint16(rdata_atom_data(rdata)); - lookup_table_type *type - = lookup_by_id(dns_certificate_types, id); - if (type) { + uint16_t id; + lookup_table_type* type; + if (rdlength < *offset || rdlength - *offset < 2) + return 0; + id = read_uint16(rdata + *offset); + type = lookup_by_id(dns_certificate_types, id); + if (type) buffer_printf(output, "%s", type->name); - } else { + else buffer_printf(output, "%u", (unsigned) id); - } - return 1; -} - -static int -rdata_period_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - uint32_t period = read_uint32(rdata_atom_data(rdata)); - buffer_printf(output, "%lu", (unsigned long) period); + *offset += 2; return 1; } +/* + * Print time field. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_time_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_time(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, + uint16_t *offset) { - int result = 0; - time_t time = (time_t) read_uint32(rdata_atom_data(rdata)); - struct tm *tm = gmtime(&time); + time_t time; + struct tm tmbuf; + struct tm* tm; char buf[15]; - if (strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", tm)) { - buffer_printf(output, "%s", buf); - result = 1; - } - return result; + + if (rdlength < *offset || rdlength - *offset < 4) + return 0; + time = (time_t)read_uint32(rdata + *offset); + tm = gmtime_r(&time, &tmbuf); + if (!strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", tm)) + return 0; + buffer_printf(output, "%s", buf); + *offset += 4; + return 1; } +/* + * Print base32 output for a b32 field length uint8_t, like for NSEC3. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_base32_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_base32(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, + uint16_t *offset) { + size_t size; int length; - size_t size = rdata_atom_size(rdata); - if(size == 0) { + if(rdlength < *offset || rdlength - *offset == 0) + return 0; + size = rdata[*offset]; + if (rdlength - ((size_t)*offset) < 1 + size) + return 0; + + if (size == 0) { buffer_write(output, "-", 1); + *offset += 1; return 1; } - size -= 1; /* remove length byte from count */ + buffer_reserve(output, size * 2 + 1); - length = b32_ntop(rdata_atom_data(rdata)+1, size, - (char *) buffer_current(output), size * 2); - if (length > 0) { - buffer_skip(output, length); - } - return length != -1; + length = b32_ntop(rdata + *offset + 1, size, + (char *)buffer_current(output), size * 2); + if (length == -1) + return 0; + buffer_skip(output, length); + *offset += 1 + size; + return 1; } +/* + * Print base64 output for the remainder of rdata. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_base64_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* rr) +print_base64(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, + uint16_t *offset) { int length; - size_t size = rdata_atom_size(rdata); + size_t size = rdlength - *offset; + if(rdlength < *offset) + return 0; if(size == 0) { /* single zero represents empty buffer */ - buffer_write(output, (rr->type == TYPE_DOA ? "-" : "0"), 1); + buffer_write(output, "0", 1); return 1; } buffer_reserve(output, size * 2 + 1); - length = __b64_ntop(rdata_atom_data(rdata), size, + length = __b64_ntop(rdata + *offset, size, (char *) buffer_current(output), size * 2); - if (length > 0) { - buffer_skip(output, length); - } - return length != -1; + if (length == -1) + return 0; + buffer_skip(output, length); + *offset += size; + return 1; } static void -hex_to_string(buffer_type *output, const uint8_t *data, size_t size) +buffer_print_hex(buffer_type *output, const uint8_t *data, size_t size) { static const char hexdigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', @@ -494,186 +547,126 @@ hex_to_string(buffer_type *output, const } } +/* + * Print base16 output for the remainder of rdata, in hex lowercase. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_hex_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_base16(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, + uint16_t *offset) { - if(rdata_atom_size(rdata) == 0) { + size_t size = rdlength - *offset; + if(rdlength < *offset) + return 0; + if(size == 0) { /* single zero represents empty buffer, such as CDS deletes */ - buffer_printf(output, "0"); - } else { - hex_to_string(output, rdata_atom_data(rdata), rdata_atom_size(rdata)); - } - return 1; -} - -static int -rdata_hexlen_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - if(rdata_atom_size(rdata) <= 1) { - /* NSEC3 salt hex can be empty */ - buffer_printf(output, "-"); + buffer_write(output, "0", 1); return 1; + } else { + buffer_print_hex(output, rdata+*offset, size); } - hex_to_string(output, rdata_atom_data(rdata)+1, rdata_atom_size(rdata)-1); - return 1; -} - -static int -rdata_nsap_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - buffer_printf(output, "0x"); - hex_to_string(output, rdata_atom_data(rdata), rdata_atom_size(rdata)); + *offset += size; return 1; } -static int -rdata_apl_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - int result = 0; - buffer_type packet; - - buffer_create_from( - &packet, rdata_atom_data(rdata), rdata_atom_size(rdata)); - - if (buffer_available(&packet, 4)) { - uint16_t address_family = buffer_read_u16(&packet); - uint8_t prefix = buffer_read_u8(&packet); - uint8_t length = buffer_read_u8(&packet); - int negated = length & APL_NEGATION_MASK; - int af = -1; - - length &= APL_LENGTH_MASK; - switch (address_family) { - case 1: af = AF_INET; break; - case 2: af = AF_INET6; break; - } - if (af != -1 && buffer_available(&packet, length)) { - char text_address[1000]; - uint8_t address[128]; - memset(address, 0, sizeof(address)); - buffer_read(&packet, address, length); - if (inet_ntop(af, address, text_address, sizeof(text_address))) { - buffer_printf(output, "%s%d:%s/%d", - negated ? "!" : "", - (int) address_family, - text_address, - (int) prefix); - result = 1; - } - } - } - 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). + * Print salt, for NSEC3, in hex lowercase. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. */ static int -rdata_services_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_salt(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, + uint16_t *offset) { - int result = 0; - buffer_type packet; - - buffer_create_from( - &packet, rdata_atom_data(rdata), rdata_atom_size(rdata)); - - if (buffer_available(&packet, 1)) { - uint8_t protocol_number = buffer_read_u8(&packet); - ssize_t bitmap_size = buffer_remaining(&packet); - uint8_t *bitmap = buffer_current(&packet); - - buffer_printf(output, "%" PRIu8, protocol_number); - - 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; - } - return result; -} + uint8_t length; + if (rdlength < *offset || rdlength - *offset == 0) + return 0; -static int -rdata_ipsecgateway_to_string(buffer_type *output, rdata_atom_type rdata, rr_type* rr) -{ - switch(rdata_atom_data(rr->rdatas[1])[0]) { - case IPSECKEY_NOGATEWAY: - buffer_printf(output, "."); - break; - case IPSECKEY_IP4: - return rdata_a_to_string(output, rdata, rr); - case IPSECKEY_IP6: - return rdata_aaaa_to_string(output, rdata, rr); - case IPSECKEY_DNAME: - return rdata_dns_name_to_string(output, rdata, rr); - default: + length = rdata[*offset]; + if (rdlength - *offset < 1 + length) return 0; - } + if (!length) + /* NSEC3 salt hex can be empty */ + buffer_printf(output, "-"); + else + buffer_print_hex(output, rdata + *offset + 1, length); + *offset += 1 + (uint16_t)length; return 1; } -static int -rdata_nxt_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) -{ - size_t i; - uint8_t *bitmap = rdata_atom_data(rdata); - size_t bitmap_size = rdata_atom_size(rdata); - - for (i = 0; i < bitmap_size * 8; ++i) { - if (get_bit(bitmap, i)) { - buffer_printf(output, "%s ", rrtype_to_string(i)); - } +/* Return length of nsec type bitmap or -1 on wireformat error. offset is + * moved +len. rdlength is the length of the rdata, offset is offset in it. */ +static inline int32_t +skip_nsec(struct buffer* packet, uint16_t rdlength, uint16_t *offset) +{ + uint16_t length = 0; + uint8_t last_window = 0; + + while (rdlength - *offset - length > 2) { + uint8_t window = buffer_read_u8(packet); + uint8_t blocks = buffer_read_u8(packet); + if (length > 0 && window <= last_window) + return -1; + if (!blocks || blocks > 32) + return -1; + if (rdlength - *offset - length < 2 + blocks) + return -1; + buffer_skip(packet, blocks); + length += 2 + blocks; + last_window = window; } - buffer_skip(output, -1); + *offset += length; + if (rdlength != *offset) + return -1; - return 1; + return length; } +/* + * Print nsec type bitmap, for the remainder of rdata. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ static int -rdata_nsec_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_nsec_bitmap(struct buffer *output, uint16_t rdlength, + const uint8_t *rdata, uint16_t *offset) { - size_t saved_position = buffer_position(output); - buffer_type packet; int insert_space = 0; - buffer_create_from( - &packet, rdata_atom_data(rdata), rdata_atom_size(rdata)); - - while (buffer_available(&packet, 2)) { - uint8_t window = buffer_read_u8(&packet); - uint8_t bitmap_size = buffer_read_u8(&packet); - uint8_t *bitmap = buffer_current(&packet); + if(rdlength < *offset) + return 0; + rdata += *offset; + while(rdlength - *offset > 0) { + uint8_t window, bitmap_size; + const uint8_t* bitmap; int i; - if (!buffer_available(&packet, bitmap_size)) { - buffer_set_position(output, saved_position); + if(rdlength - *offset < 2) return 0; - } + window = rdata[0]; + bitmap_size = rdata[1]; + *offset += 2; - for (i = 0; i < bitmap_size * 8; ++i) { + if(rdlength - *offset < bitmap_size) + return 0; + + bitmap = rdata + 2; + rdata += 2; + for(i=0; i<((int)bitmap_size)*8; i++) { if (get_bit(bitmap, i)) { buffer_printf(output, "%s%s", @@ -683,152 +676,139 @@ rdata_nsec_to_string(buffer_type *output insert_space = 1; } } - buffer_skip(&packet, bitmap_size); + rdata += bitmap_size; + *offset += bitmap_size; } - return 1; } +/* If an svcparam must have a value */ static int -rdata_loc_to_string(buffer_type *ATTR_UNUSED(output), - rdata_atom_type ATTR_UNUSED(rdata), - rr_type* ATTR_UNUSED(rr)) +svcparam_must_have_value(uint16_t svcparamkey) { - /* - * Returning 0 forces the record to be printed in unknown - * format. - */ - return 0; -} - -static void -buffer_print_svcparamkey(buffer_type *output, uint16_t svcparamkey) -{ - if (svcparamkey < SVCPARAMKEY_COUNT) - buffer_printf(output, "%s", svcparamkey_strs[svcparamkey]); - else - buffer_printf(output, "key%d", (int)svcparamkey); + switch (svcparamkey) { + case SVCB_KEY_ALPN: + case SVCB_KEY_PORT: + case SVCB_KEY_IPV4HINT: + case SVCB_KEY_IPV6HINT: + case SVCB_KEY_MANDATORY: + case SVCB_KEY_DOHPATH: + case SVCB_KEY_TLS_SUPPORTED_GROUPS: + case SVCB_KEY_OOTS: + return 1; + default: + break; + } + return 0; } +/* If an svcparam must not have a value */ static int -rdata_svcparam_port_to_string(buffer_type *output, uint16_t val_len, - uint16_t *data) +svcparam_must_not_have_value(uint16_t svcparamkey) { - if (val_len != 2) - return 0; /* wireformat error, a short is 2 bytes */ - buffer_printf(output, "=%d", (int)ntohs(data[0])); - return 1; + switch (svcparamkey) { + case SVCB_KEY_NO_DEFAULT_ALPN: + case SVCB_KEY_OHTTP: + case SVCB_KEY_PVD: + return 1; + default: + break; + } + return 0; } -static int -rdata_svcparam_ipv4hint_to_string(buffer_type *output, uint16_t val_len, - uint16_t *data) +/* + * Skip over the svcparams in the packet. Moves position. + * @param packet: wire packet, position at rdata fields of svcparams. + * @param rdlength: remaining rdata length in the packet. + * @return 0 on wireformat error. + */ +static inline int +skip_svcparams(struct buffer *packet, uint16_t rdlength) { - char ip_str[INET_ADDRSTRLEN + 1]; - - assert(val_len > 0); /* Guaranteed by rdata_svcparam_to_string */ - - if ((val_len % IP4ADDRLEN) == 0) { - if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) == NULL) - return 0; /* wireformat error, incorrect size or inet family */ - - buffer_printf(output, "=%s", ip_str); - data += IP4ADDRLEN / sizeof(uint16_t); - - while ((val_len -= IP4ADDRLEN) > 0) { - if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) == NULL) - return 0; /* wireformat error, incorrect size or inet family */ - - buffer_printf(output, ",%s", ip_str); - data += IP4ADDRLEN / sizeof(uint16_t); - } - return 1; - } else - return 0; + unsigned pos = 0; + uint16_t key, count; + while(pos < rdlength) { + if(pos+4 > (unsigned)rdlength) + return 0; + if(!buffer_available(packet, 4)) + return 0; + key = buffer_read_u16(packet); + count = buffer_read_u16(packet); + if(count == 0 && svcparam_must_have_value(key)) + return 0; + if(count != 0 && svcparam_must_not_have_value(key)) + return 0; + pos += 4; + if(pos+count > (unsigned)rdlength) + return 0; + if(!buffer_available(packet, count)) + return 0; + buffer_skip(packet, count); + pos += count; + } + return 1; } -static int -rdata_svcparam_ipv6hint_to_string(buffer_type *output, uint16_t val_len, - uint16_t *data) +/* + * Print svcparamkey name to the buffer, or unknown as key. + * @param output: printed to string. + * @param svcparamkey: the key to print. + */ +static void +buffer_print_svcparamkey(buffer_type *output, uint16_t svcparamkey) { - char ip_str[INET6_ADDRSTRLEN + 1]; - - assert(val_len > 0); /* Guaranteed by rdata_svcparam_to_string */ - - if ((val_len % IP6ADDRLEN) == 0) { - if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) == NULL) - return 0; /* wireformat error, incorrect size or inet family */ - - buffer_printf(output, "=%s", ip_str); - data += IP6ADDRLEN / sizeof(uint16_t); - - while ((val_len -= IP6ADDRLEN) > 0) { - if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) == NULL) - return 0; /* wireformat error, incorrect size or inet family */ - - buffer_printf(output, ",%s", ip_str); - data += IP6ADDRLEN / sizeof(uint16_t); - } - return 1; - } else - return 0; + if (svcparamkey < sizeof(svcparams)/sizeof(svcparams[0])) + buffer_printf(output, "%s", svcparams[svcparamkey].name); + else + buffer_printf(output, "key%" PRIu16, svcparamkey); } static int -rdata_svcparam_mandatory_to_string(buffer_type *output, uint16_t val_len, - uint16_t *data) +print_svcparam_no_value(struct buffer *output, uint16_t svcparamkey, + const uint8_t* ATTR_UNUSED(data), uint16_t ATTR_UNUSED(datalen)) { - assert(val_len > 0); /* Guaranteed by rdata_svcparam_to_string */ - - if (val_len % sizeof(uint16_t)) - return 0; /* wireformat error, val_len must be multiple of shorts */ - buffer_write_u8(output, '='); - buffer_print_svcparamkey(output, ntohs(*data)); - data += 1; - - while ((val_len -= sizeof(uint16_t))) { - buffer_write_u8(output, ','); - buffer_print_svcparamkey(output, ntohs(*data)); - data += 1; - } - + buffer_print_svcparamkey(output, svcparamkey); return 1; } static int -rdata_svcparam_ech_to_string(buffer_type *output, uint16_t val_len, - uint16_t *data) +print_svcparam_mandatory(struct buffer *output, uint16_t svcparamkey, + const uint8_t* data, uint16_t datalen) { - int length; - - assert(val_len > 0); /* Guaranteed by rdata_svcparam_to_string */ + assert(datalen > 0); /* Guaranteed by svcparam_print */ + if (datalen % sizeof(uint16_t)) + return 0; /* wireformat error, val_len must be multiple of shorts */ + buffer_print_svcparamkey(output, svcparamkey); buffer_write_u8(output, '='); + buffer_print_svcparamkey(output, read_uint16(data)); + data += 2; - buffer_reserve(output, val_len * 2 + 1); - length = __b64_ntop((uint8_t*) data, val_len, - (char *) buffer_current(output), val_len * 2); - if (length > 0) { - buffer_skip(output, length); + while ((datalen -= sizeof(uint16_t))) { + buffer_write_u8(output, ','); + buffer_print_svcparamkey(output, read_uint16(data)); + data += 2; } - return length != -1; + return 1; } static int -rdata_svcparam_alpn_to_string(buffer_type *output, uint16_t val_len, - uint16_t *data) +print_svcparam_alpn(struct buffer *output, uint16_t svcparamkey, + const uint8_t* data, uint16_t datalen) { uint8_t *dp = (void *)data; - assert(val_len > 0); /* Guaranteed by rdata_svcparam_to_string */ + assert(datalen > 0); /* Guaranteed by svcparam_print */ + buffer_print_svcparamkey(output, svcparamkey); buffer_write_u8(output, '='); buffer_write_u8(output, '"'); - while (val_len) { + while (datalen) { uint8_t i, str_len = *dp++; - if (str_len > --val_len) + if (str_len > --datalen) return 0; for (i = 0; i < str_len; i++) { @@ -845,7 +825,7 @@ rdata_svcparam_alpn_to_string(buffer_typ buffer_write_u8(output, dp[i]); } dp += str_len; - if ((val_len -= str_len)) + if ((datalen -= str_len)) buffer_write_u8(output, ','); } buffer_write_u8(output, '"'); @@ -853,507 +833,3525 @@ 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) +print_svcparam_port(struct buffer *output, uint16_t svcparamkey, + const uint8_t* data, uint16_t datalen) { - 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++)); + if (datalen != 2) + return 0; /* wireformat error, a short is 2 bytes */ + buffer_print_svcparamkey(output, svcparamkey); + buffer_printf(output, "=%d", (int)read_uint16(data)); return 1; } static int -rdata_svcparam_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_svcparam_ipv4hint(struct buffer *output, uint16_t svcparamkey, + const uint8_t* data, uint16_t datalen) { - uint16_t size = rdata_atom_size(rdata); - uint16_t* data = (uint16_t *)rdata_atom_data(rdata); - uint16_t svcparamkey, val_len; - uint8_t* dp; - size_t i; + char ip_str[INET_ADDRSTRLEN + 1]; - if (size < 4) - return 0; - svcparamkey = ntohs(data[0]); + assert(datalen > 0); /* Guaranteed by svcparam_print */ buffer_print_svcparamkey(output, svcparamkey); - val_len = ntohs(data[1]); - if (size != val_len + 4) - return 0; /* wireformat error */ - if (!val_len) { - /* Some SvcParams MUST have values */ - switch (svcparamkey) { - case SVCB_KEY_ALPN: - case SVCB_KEY_PORT: - case SVCB_KEY_IPV4HINT: - case SVCB_KEY_IPV6HINT: - case SVCB_KEY_MANDATORY: - case SVCB_KEY_DOHPATH: - case SVCB_KEY_TLS_SUPPORTED_GROUPS: - return 0; - default: - return 1; - } - } - switch (svcparamkey) { - case SVCB_KEY_PORT: - return rdata_svcparam_port_to_string(output, val_len, data+2); - case SVCB_KEY_IPV4HINT: - return rdata_svcparam_ipv4hint_to_string(output, val_len, data+2); - case SVCB_KEY_IPV6HINT: - return rdata_svcparam_ipv6hint_to_string(output, val_len, data+2); - case SVCB_KEY_MANDATORY: - return rdata_svcparam_mandatory_to_string(output, val_len, data+2); - case SVCB_KEY_NO_DEFAULT_ALPN: - return 0; /* wireformat error, should not have a value */ - case SVCB_KEY_ALPN: - 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: - buffer_write(output, "=\"", 2); - dp = (void*) (data + 2); + if ((datalen % IP4ADDRLEN) == 0) { + if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) == NULL) + return 0; /* wireformat error, incorrect size or inet family */ - for (i = 0; i < val_len; i++) { - if (dp[i] == '"' || dp[i] == '\\') - buffer_printf(output, "\\%c", dp[i]); + buffer_printf(output, "=%s", ip_str); + data += IP4ADDRLEN; - else if (!isprint(dp[i])) - buffer_printf(output, "\\%03u", (unsigned) dp[i]); + while ((datalen -= IP4ADDRLEN) > 0) { + if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) + == NULL) + return 0; /* wireformat error, incorrect size or inet family */ - else - buffer_write_u8(output, dp[i]); + buffer_printf(output, ",%s", ip_str); + data += IP4ADDRLEN; } - buffer_write_u8(output, '"'); - break; + return 1; } - return 1; + return 0; } static int -rdata_hip_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_svcparam_ech(struct buffer *output, uint16_t svcparamkey, + const uint8_t* data, uint16_t datalen) { - uint16_t size = rdata_atom_size(rdata); - uint8_t hit_length; - uint16_t pk_length; - int length = 0; + int length; - 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); + buffer_print_svcparamkey(output, svcparamkey); + if(datalen == 0) + return 1; + buffer_write_u8(output, '='); + + buffer_reserve(output, datalen * 2 + 1); + length = __b64_ntop(data, datalen, (char*)buffer_current(output), + datalen * 2); if (length > 0) { buffer_skip(output, length); } + return length != -1; } static int -rdata_atma_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_svcparam_ipv6hint(struct buffer *output, uint16_t svcparamkey, + const uint8_t* data, uint16_t datalen) { - uint16_t size = rdata_atom_size(rdata), i; + char ip_str[INET6_ADDRSTRLEN + 1]; - if(size < 2 || rdata_atom_data(rdata)[0] > 1) - return 0; - if(!rdata_atom_data(rdata)[0]) { - hex_to_string(output, rdata_atom_data(rdata) + 1, size - 1); + assert(datalen > 0); /* Guaranteed by svcparam_print */ + + buffer_print_svcparamkey(output, svcparamkey); + if ((datalen % IP6ADDRLEN) == 0) { + if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) == NULL) + return 0; /* wireformat error, incorrect size or inet family */ + + buffer_printf(output, "=%s", ip_str); + data += IP6ADDRLEN; + + while ((datalen -= IP6ADDRLEN) > 0) { + if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) + == NULL) + return 0; /* wireformat error, incorrect size or inet family */ + + buffer_printf(output, ",%s", ip_str); + data += IP6ADDRLEN; + } return 1; } - for(i = 1; i < size; i++) { - if(!isdigit(rdata_atom_data(rdata)[i])) - return 0; - } - buffer_write_u8(output, '+'); - buffer_write(output, rdata_atom_data(rdata) + 1, size - 1); - return 1; + return 0; } static int -rdata_amtrelay_d_type_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_svcparam_dohpath(struct buffer *output, uint16_t svcparamkey, + const uint8_t* data, uint16_t datalen) { - uint8_t data = *rdata_atom_data(rdata); - buffer_printf(output , "%c %lu", (data & 0x80 ? '1' : '0'), - ((unsigned long)data & 0x7f)); + const uint8_t* dp = data; + unsigned i; + + buffer_print_svcparamkey(output, svcparamkey); + buffer_write(output, "=\"", 2); + for (i = 0; i < datalen; i++) { + if (dp[i] == '"' || dp[i] == '\\') + buffer_printf(output, "\\%c", dp[i]); + + else if (!isprint(dp[i])) + buffer_printf(output, "\\%03u", (unsigned) dp[i]); + + else + buffer_write_u8(output, dp[i]); + } + buffer_write_u8(output, '"'); return 1; } static int -rdata_amtrelay_relay_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* rr) +print_svcparam_tls_supported_groups(struct buffer *output, + uint16_t svcparamkey, const uint8_t* data, uint16_t datalen) { - switch(rdata_atom_data(rr->rdatas[1])[0] & 0x7f) { - case AMTRELAY_NOGATEWAY: - break; - case AMTRELAY_IP4: - return rdata_a_to_string(output, rdata, rr); - case AMTRELAY_IP6: - return rdata_aaaa_to_string(output, rdata, rr); - case AMTRELAY_DNAME: - return rdata_dns_name_to_string(output, rdata, rr); - default: - return 0; + assert(datalen > 0); /* Guaranteed by svcparam_print */ + + if ((datalen % sizeof(uint16_t)) == 1) + return 0; /* A series of uint16_t is an even number of bytes */ + + buffer_print_svcparamkey(output, svcparamkey); + buffer_printf(output, "=%d", (int)read_uint16(data)); + data += 2; + while ((datalen -= sizeof(uint16_t)) > 0) { + buffer_printf(output, ",%d", (int)read_uint16(data)); + data += 2; } return 1; } static int -rdata_unknown_to_string(buffer_type *output, rdata_atom_type rdata, - rr_type* ATTR_UNUSED(rr)) +print_svcparam_docpath(struct buffer *output, uint16_t svcparamkey, + const uint8_t* data, uint16_t datalen) { - uint16_t size = rdata_atom_size(rdata); - buffer_printf(output, "\\# %lu ", (unsigned long) size); - hex_to_string(output, rdata_atom_data(rdata), size); + if(datalen > 0) + return print_svcparam_alpn(output, svcparamkey, data, datalen); + buffer_print_svcparamkey(output, svcparamkey); return 1; } -static rdata_to_string_type rdata_to_string_table[RDATA_ZF_UNKNOWN + 1] = { - rdata_dname_to_string, - rdata_dns_name_to_string, - rdata_text_to_string, - rdata_texts_to_string, - rdata_byte_to_string, - rdata_short_to_string, - rdata_long_to_string, - rdata_longlong_to_string, - rdata_a_to_string, - rdata_aaaa_to_string, - rdata_rrtype_to_string, - rdata_algorithm_to_string, - rdata_certificate_type_to_string, - rdata_period_to_string, - rdata_time_to_string, - rdata_base64_to_string, - rdata_base32_to_string, - rdata_hex_to_string, - rdata_hexlen_to_string, - rdata_nsap_to_string, - rdata_apl_to_string, - rdata_ipsecgateway_to_string, - rdata_services_to_string, - rdata_nxt_to_string, - rdata_nsec_to_string, - rdata_loc_to_string, - rdata_ilnp64_to_string, - 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_atma_to_string, - rdata_amtrelay_d_type_to_string, - rdata_amtrelay_relay_to_string, - rdata_unknown_to_string -}; - -int -rdata_atom_to_string(buffer_type *output, rdata_zoneformat_type type, - rdata_atom_type rdata, rr_type* record) +static int +print_svcparam_oots(struct buffer *output, uint16_t svcparamkey, + const uint8_t* data, uint16_t datalen) { - return rdata_to_string_table[type](output, rdata, record); + assert(datalen > 0); /* Guaranteed by svcparam_print */ + + buffer_print_svcparamkey(output, svcparamkey); + buffer_printf(output, "=\""); + while(((size_t)(*data)) + 2 <= (size_t)datalen) { + size_t transport_len = *data; + uint8_t percentage = data[transport_len + 1]; + size_t i; + + if(!transport_len || percentage > 100) + return 0; + + for(i=0; i < transport_len; i++) { + char ch = data[i + 1]; + if(!isgraph(ch) + || ch == '"' || ch == '\\' || ch == ',' || ch == ':') + return 0; + + buffer_write_u8(output, ch); + } + buffer_printf(output, ":%d", percentage); + data += transport_len + 2; + datalen -= transport_len + 2; + if(datalen) + buffer_write_u8(output, ','); + } + if(datalen) + return 0; + buffer_printf(output, "\""); + return 1; } -ssize_t -rdata_wireformat_to_rdata_atoms(region_type *region, - domain_table_type *owners, - uint16_t rrtype, - uint16_t data_size, - buffer_type *packet, - rdata_atom_type **rdatas) +/* + * Print svcparam. + * @param output: the string is output here. + * @param rdlength: length of rdata. + * @param rdata: the rdata. The rdata+*offset is where the field is. + * @param offset: the current position on input. The position is updated to + * be incremented with the length of rdata that was used. + * @return false on failure. + */ +static int +print_svcparam(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, + uint16_t *offset) { - size_t end = buffer_position(packet) + data_size; - size_t i; - rdata_atom_type temp_rdatas[MAXRDATALEN]; - rrtype_descriptor_type *descriptor = rrtype_descriptor_by_type(rrtype); - region_type *temp_region; + uint16_t key, length; + const uint8_t* dp; + unsigned i; + + if (rdlength < *offset || rdlength - *offset < 4) + return 0; - assert(descriptor->maximum <= MAXRDATALEN); + key = read_uint16(rdata + *offset); + length = read_uint16(rdata + *offset + 2); - if (!buffer_available(packet, data_size)) { - return -1; + if (rdlength - *offset < length + 4) + return 0; /* wireformat error */ + + if(length == 0 && svcparam_must_have_value(key)) + return 0; + if(length != 0 && svcparam_must_not_have_value(key)) + return 0; + if (key < sizeof(svcparams)/sizeof(svcparams[0])) { + if(!svcparams[key].print_rdata(output, key, rdata+*offset+4, length)) + return 0; + *offset += length+4; + return 1; } - temp_region = region_create(xalloc, free); + buffer_printf(output, "key%" PRIu16, key); + if (!length) { + *offset += 4; + return 1; + } - for (i = 0; i < descriptor->maximum; ++i) { - int is_domain = 0; - int is_normalized = 0; - int is_wirestore = 0; - size_t length = 0; - int required = i < descriptor->minimum; - - switch (rdata_atom_wireformat_type(rrtype, i)) { - case RDATA_WF_COMPRESSED_DNAME: - case RDATA_WF_UNCOMPRESSED_DNAME: - is_domain = 1; - is_normalized = 1; - break; - case RDATA_WF_LITERAL_DNAME: - is_domain = 1; - is_wirestore = 1; - break; - case RDATA_WF_BYTE: - length = sizeof(uint8_t); - break; - case RDATA_WF_SHORT: - length = sizeof(uint16_t); - break; - case RDATA_WF_LONG: - length = sizeof(uint32_t); - break; - case RDATA_WF_LONGLONG: - length = sizeof(uint64_t); - break; - case RDATA_WF_TEXTS: - case RDATA_WF_LONG_TEXT: - length = end - buffer_position(packet); - break; - case RDATA_WF_TEXT: - case RDATA_WF_BINARYWITHLENGTH: - /* Length is stored in the first byte. */ - length = 1; - if (buffer_position(packet) + length <= end) { - length += buffer_current(packet)[length - 1]; - } - break; - case RDATA_WF_A: - length = sizeof(in_addr_t); - break; - case RDATA_WF_AAAA: - length = IP6ADDRLEN; - break; - case RDATA_WF_ILNP64: - length = IP6ADDRLEN/2; - break; - case RDATA_WF_EUI48: - length = EUI48ADDRLEN; - break; - case RDATA_WF_EUI64: - length = EUI64ADDRLEN; - break; - case RDATA_WF_BINARY: - /* Remaining RDATA is binary. */ - length = end - buffer_position(packet); - break; - case RDATA_WF_APL: - length = (sizeof(uint16_t) /* address family */ - + sizeof(uint8_t) /* prefix */ - + sizeof(uint8_t)); /* length */ - if (buffer_position(packet) + length <= end) { - /* Mask out negation bit. */ - length += (buffer_current(packet)[length - 1] - & APL_LENGTH_MASK); - } - break; - case RDATA_WF_IPSECGATEWAY: - assert(i>1); /* we are past the gateway type */ - switch(rdata_atom_data(temp_rdatas[1])[0]) /* gateway type */ { - default: - case IPSECKEY_NOGATEWAY: - length = 0; - break; - case IPSECKEY_IP4: - length = IP4ADDRLEN; - break; - case IPSECKEY_IP6: - length = IP6ADDRLEN; - break; - case IPSECKEY_DNAME: - is_domain = 1; - is_normalized = 1; - is_wirestore = 1; - break; - } - break; - case RDATA_WF_SVCPARAM: - length = 4; - if (buffer_position(packet) + 4 <= end) { - length += - 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; - case RDATA_WF_AMTRELAY_RELAY: - assert(i>1); - switch(rdata_atom_data(temp_rdatas[1])[0] & 0x7f) /* relay type */ { - default: - case AMTRELAY_NOGATEWAY: - length = 0; - break; - case AMTRELAY_IP4: - length = IP4ADDRLEN; - break; - case AMTRELAY_IP6: - length = IP6ADDRLEN; - break; - case AMTRELAY_DNAME: - is_domain = 1; - is_normalized = 1; - is_wirestore = 1; - break; - } - break; - } - if (is_domain) { - const dname_type *dname; + buffer_write(output, "=\"", 2); + dp = rdata + *offset + 4; - if (!required && buffer_position(packet) == end) { - break; - } + for (i = 0; i < length; i++) { + if (dp[i] == '"' || dp[i] == '\\') + buffer_printf(output, "\\%c", dp[i]); - dname = dname_make_from_packet( - temp_region, packet, 1, is_normalized); - if (!dname || buffer_position(packet) > end) { - /* Error in domain name. */ - region_destroy(temp_region); - return -1; - } - if(is_wirestore) { - temp_rdatas[i].data = (uint16_t *) region_alloc( - region, sizeof(uint16_t) + ((size_t)dname->name_size)); - temp_rdatas[i].data[0] = dname->name_size; - memcpy(temp_rdatas[i].data+1, dname_name(dname), - dname->name_size); - } else { - temp_rdatas[i].domain - = domain_table_insert(owners, dname); - temp_rdatas[i].domain->usage ++; - } - } else { - if (buffer_position(packet) + length > end) { - if (required) { - /* Truncated RDATA. */ - region_destroy(temp_region); - return -1; - } else { - break; - } - } - if (!required && buffer_position(packet) == end) { - break; - } + else if (!isprint(dp[i])) + buffer_printf(output, "\\%03u", (unsigned) dp[i]); - temp_rdatas[i].data = (uint16_t *) region_alloc( - region, sizeof(uint16_t) + length); - temp_rdatas[i].data[0] = length; - buffer_read(packet, temp_rdatas[i].data + 1, length); - } + else + buffer_write_u8(output, dp[i]); } + buffer_write_u8(output, '"'); + *offset += length + 4; + return 1; +} - if (buffer_position(packet) < end) { - /* Trailing garbage. */ - region_destroy(temp_region); - return -1; - } +static inline int32_t +read_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + if (buffer_remaining(packet) < rdlength) + return MALFORMED; + if (!(*rr = region_alloc(domains->region, sizeof(**rr) + rdlength))) + return TRUNCATED; + if(rdlength != 0) + buffer_read(packet, (*rr)->rdata, rdlength); + (*rr)->rdlength = rdlength; + return rdlength; +} - *rdatas = (rdata_atom_type *) region_alloc_array_init( - region, temp_rdatas, i, sizeof(rdata_atom_type)); - region_destroy(temp_region); - return (ssize_t)i; +int32_t +read_generic_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + return read_rdata(domains, rdlength, packet, rr); } -size_t -rdata_maximum_wireformat_size(rrtype_descriptor_type *descriptor, - size_t rdata_count, - rdata_atom_type *rdatas) +void +write_generic_rdata(struct query *query, const struct rr *rr) { - size_t result = 0; - size_t i; - for (i = 0; i < rdata_count; ++i) { - if (rdata_atom_is_domain(descriptor->type, i)) { - result += domain_dname(rdata_atom_domain(rdatas[i]))->name_size; - } else { - result += rdata_atom_size(rdatas[i]); - } - } - return result; + if(rr->rdlength != 0) + buffer_write(query->packet, rr->rdata, rr->rdlength); } int -rdata_atoms_to_unknown_string(buffer_type *output, - rrtype_descriptor_type *descriptor, - size_t rdata_count, - rdata_atom_type *rdatas) +print_generic_rdata(struct buffer *output, const struct rr *rr) { - size_t i; - size_t size = - rdata_maximum_wireformat_size(descriptor, rdata_count, rdatas); - buffer_printf(output, " \\# %lu ", (unsigned long) size); - for (i = 0; i < rdata_count; ++i) { - if (rdata_atom_is_domain(descriptor->type, i)) { - const dname_type *dname = - domain_dname(rdata_atom_domain(rdatas[i])); - hex_to_string( - output, dname_name(dname), dname->name_size); - } else { - hex_to_string(output, rdata_atom_data(rdatas[i]), - rdata_atom_size(rdatas[i])); - } - } - return 1; + const nsd_type_descriptor_type* descriptor = + nsd_type_descriptor(rr->type); + return print_unknown_rdata_field(output, descriptor, rr); } int -print_rdata(buffer_type *output, rrtype_descriptor_type *descriptor, - rr_type *record) -{ +lookup_rdata_field_entry(const nsd_type_descriptor_type* descriptor, + size_t index, const rr_type* rr, uint16_t offset, uint16_t* field_len, + struct domain** domain) +{ + const nsd_rdata_descriptor_type* field = + &descriptor->rdata.fields[index]; + if(field->calculate_length) { + /* Call field length function. */ + int32_t l = field->calculate_length(rr->rdlength, rr->rdata, + offset, domain); + if(l < 0) + return 0; + *field_len = l; + } else if(field->length >= 0) { + *field_len = field->length; + *domain = NULL; + } else { + size_t dlen; + int32_t flen; + if(offset > rr->rdlength) + return 0; + /* Handle the specialized value cases. */ + switch(field->length) { + case RDATA_COMPRESSED_DNAME: + case RDATA_UNCOMPRESSED_DNAME: + if(rr->rdlength - offset < + (uint16_t)sizeof(void*)) + return 0; + *field_len = sizeof(void*); + memcpy(domain, rr->rdata+offset, sizeof(void*)); + break; + case RDATA_LITERAL_DNAME: + dlen = buf_dname_length(rr->rdata+offset, + rr->rdlength-offset); + if(dlen == 0) + return 0; + *field_len = dlen; + *domain = NULL; + break; + case RDATA_STRING: + case RDATA_BINARY: + if(rr->rdlength - offset < 1) + return 0; + flen = (rr->rdata+offset)[0]; + *field_len = flen+1; + *domain = NULL; + break; + case RDATA_IPSECGATEWAY: + case RDATA_AMTRELAY_RELAY: + return 0; /* Has a callback function. */ + case RDATA_REMAINDER: + *field_len = rr->rdlength - offset; + *domain = NULL; + break; + default: + /* Unknown specialized value. */ + return 0; + } + } + if(offset + *field_len > rr->rdlength) + return 0; /* The field does not fit in the rdata. */ + return 1; +} + +int +lookup_rdata_field_entry_uncompressed_wire( + const nsd_type_descriptor_type* descriptor, size_t index, + const uint8_t* rdata, uint16_t rdlength, uint16_t offset, + uint16_t* field_len, struct domain** domain) +{ + const nsd_rdata_descriptor_type* field = + &descriptor->rdata.fields[index]; + if(field->calculate_length) { + /* Call field length function. */ + int32_t l = field->calculate_length_uncompressed_wire(rdlength, + rdata, offset, domain); + if(l < 0) + return 0; + *field_len = l; + } else if(field->length >= 0) { + *field_len = field->length; + *domain = NULL; + } else { + size_t dlen; + int32_t flen; + if(offset > rdlength) + return 0; + /* Handle the specialized value cases. */ + switch(field->length) { + case RDATA_COMPRESSED_DNAME: + case RDATA_UNCOMPRESSED_DNAME: + /* In the case that the field type is of type dname, + * since this function deals with uncompressed + * wireformat in the rdata buffer, the dname is + * going to be there as an uncompressed wireformat + * dname, for RDATA_COMPRESSED_DNAME and + * RDATA_UNCOMPRESSED_DNAME. */ + case RDATA_LITERAL_DNAME: + dlen = buf_dname_length(rdata+offset, rdlength-offset); + if(dlen == 0) + return 0; + *field_len = dlen; + *domain = NULL; + break; + case RDATA_STRING: + case RDATA_BINARY: + if(rdlength - offset < 1) + return 0; + flen = (rdata+offset)[0]; + *field_len = flen+1; + *domain = NULL; + break; + case RDATA_IPSECGATEWAY: + case RDATA_AMTRELAY_RELAY: + return 0; /* Has a callback function. */ + case RDATA_REMAINDER: + *field_len = rdlength - offset; + *domain = NULL; + break; + default: + /* Unknown specialized value. */ + return 0; + } + } + if(offset + *field_len > rdlength) + return 0; /* The field does not fit in the rdata. */ + return 1; +} + +int32_t +rr_calculate_uncompressed_rdata_length(const rr_type* rr) +{ size_t i; - size_t saved_position = buffer_position(output); + const nsd_type_descriptor_type* descriptor = + nsd_type_descriptor(rr->type); + uint16_t offset = 0; + int32_t result = 0; + if(!descriptor->has_references) + return rr->rdlength; /* It does not really validate the + fields versus the rdlength. */ + for(i=0; i < descriptor->rdata.length; i++) { + uint16_t field_len; + struct domain* domain; + if(rr->rdlength == offset && + descriptor->rdata.fields[i].is_optional) + break; /* There are no more rdata fields. */ + if(!lookup_rdata_field_entry(descriptor, i, rr, offset, + &field_len, &domain)) + return -1; /* malformed rdata buffer */ + if(domain != NULL) { + /* Handle RDATA_COMPRESSED_DNAME and + * RDATA_UNCOMPRESSED_DNAME fields. */ + const struct dname* dname = domain_dname(domain); + result += dname->name_size; + } else { + result += field_len; + } + offset += field_len; + } + return result; +} - for (i = 0; i < record->rdata_count; ++i) { - if (i == 0) { - buffer_printf(output, "\t"); - } else if (descriptor->type == TYPE_SOA && i == 2) { - buffer_printf(output, " (\n\t\t"); +void +rr_write_uncompressed_rdata(const rr_type* rr, uint8_t* buf, size_t len) +{ + size_t i; + const nsd_type_descriptor_type* descriptor = + nsd_type_descriptor(rr->type); + uint16_t offset = 0; /* The offset in rr->rdatas. */ + size_t pos = 0; /* The position in buf. */ + if(!descriptor->has_references) { + /* It does not really validate the fields versus the + * rdlength. */ + if(rr->rdlength > len) + return; /* buffer too small */ + memcpy(buf, rr->rdata, rr->rdlength); + return; + } + for(i=0; i < descriptor->rdata.length; i++) { + uint16_t field_len; + struct domain* domain; + if(rr->rdlength == offset && + descriptor->rdata.fields[i].is_optional) + break; /* There are no more rdata fields. */ + if(!lookup_rdata_field_entry(descriptor, i, rr, offset, + &field_len, &domain)) + return; /* malformed rdata buffer */ + if(domain != NULL) { + /* Handle RDATA_COMPRESSED_DNAME and + * RDATA_UNCOMPRESSED_DNAME fields. */ + const struct dname* dname = domain_dname(domain); + if(pos + dname->name_size > len) + return; /* buffer too small */ + memcpy(buf+pos, dname_name(dname), dname->name_size); + pos += dname->name_size; } else { - buffer_printf(output, " "); + if(pos + field_len > len) + return; /* buffer too small */ + memcpy(buf+pos, rr->rdata+offset, field_len); + pos += field_len; + } + offset += field_len; + } +} + +int print_unknown_rdata_field(buffer_type *output, + const nsd_type_descriptor_type *descriptor, const rr_type *rr) +{ + size_t i; + int32_t size; + uint16_t length = 0; + + if(!descriptor->has_references) { + /* There are no references to the domain table, the + * rdata can be printed literally. */ + buffer_printf(output, "\\# %lu ", (unsigned long)rr->rdlength); + buffer_print_hex(output, rr->rdata, rr->rdlength); + return 1; + } + + size = rr_calculate_uncompressed_rdata_length(rr); + if(size < 0) + return 0; + buffer_printf(output, "\\# %lu ", (unsigned long) size); + + for(i=0; i < descriptor->rdata.length; i++) { + uint16_t field_len; + struct domain* domain; + const uint8_t* to_print; + size_t to_print_len; + if(rr->rdlength == length && + descriptor->rdata.fields[i].is_optional) + break; /* There are no more rdata fields. */ + if(!lookup_rdata_field_entry(descriptor, i, rr, length, + &field_len, &domain)) + return 0; /* malformed rdata buffer */ + if(domain != NULL) { + /* Handle RDATA_COMPRESSED_DNAME and + * RDATA_UNCOMPRESSED_DNAME fields. */ + const struct dname* dname = domain_dname(domain); + to_print = dname_name(dname); + to_print_len = dname->name_size; + } else { + to_print = rr->rdata+length; + to_print_len = field_len; } - if (!rdata_atom_to_string( - output, - (rdata_zoneformat_type) descriptor->zoneformat[i], - record->rdatas[i], record)) - { - buffer_set_position(output, saved_position); + buffer_print_hex(output, to_print, to_print_len); + length += field_len; + } + return 1; +} + +int print_unknown_rdata(buffer_type *output, + const nsd_type_descriptor_type *descriptor, const rr_type *rr) +{ + buffer_printf(output, "\t"); + return print_unknown_rdata_field(output, descriptor, rr); +} + +int32_t +read_compressed_name_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *domain; + struct dname_buffer dname; + size_t size; + const size_t mark = buffer_position(packet); + + if (!dname_make_from_packet_buffered(&dname, packet, 1, 1) || + rdlength != buffer_position(packet) - mark) + return MALFORMED; + size = sizeof(**rr) + sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + domain = domain_table_insert(domains, (void*)&dname); + domain->usage++; + memcpy((*rr)->rdata, &domain, sizeof(void*)); + (*rr)->rdlength = sizeof(void*); + return rdlength; +} + +int32_t +read_uncompressed_name_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *domain; + struct dname_buffer dname; + size_t size; + const size_t mark = buffer_position(packet); + + if (!dname_make_from_packet_buffered(&dname, packet, + 1 /* Lenient, allows pointers. */, 1) || + rdlength != buffer_position(packet)-mark) + return MALFORMED; + size = sizeof(**rr) + sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + domain = domain_table_insert(domains, (void*)&dname); + domain->usage++; + memcpy((*rr)->rdata, &domain, sizeof(void*)); + (*rr)->rdlength = sizeof(void*); + return rdlength; +} + +static void +encode_dname(query_type *q, domain_type *domain) +{ + while (domain->parent && query_get_dname_offset(q, domain) == 0) { + query_put_dname_offset(q, domain, buffer_position(q->packet)); + DEBUG(DEBUG_NAME_COMPRESSION, 2, + (LOG_INFO, "dname: %s, number: %lu, offset: %u\n", + domain_to_string(domain), + (unsigned long) domain->number, + query_get_dname_offset(q, domain))); + buffer_write(q->packet, dname_name(domain_dname(domain)), + label_length(dname_name(domain_dname(domain))) + 1U); + domain = domain->parent; + } + if (domain->parent) { + DEBUG(DEBUG_NAME_COMPRESSION, 2, + (LOG_INFO, "dname: %s, number: %lu, pointer: %u\n", + domain_to_string(domain), + (unsigned long) domain->number, + query_get_dname_offset(q, domain))); + assert(query_get_dname_offset(q, domain) <= MAX_COMPRESSION_OFFSET); + buffer_write_u16(q->packet, + 0xc000 | query_get_dname_offset(q, domain)); + } else { + buffer_write_u8(q->packet, 0); + } +} + +void +write_compressed_name_rdata(struct query *query, const struct rr *rr) +{ + struct domain *domain; + assert(rr->rdlength == sizeof(void*)); + memcpy(&domain, rr->rdata, sizeof(void*)); + encode_dname(query, domain); +} + +void +write_uncompressed_name_rdata(struct query *query, const struct rr *rr) +{ + const struct dname *dname; + struct domain *domain; + assert(rr->rdlength >= sizeof(void*)); + memcpy(&domain, rr->rdata, sizeof(void*)); + dname = domain_dname(domain); + buffer_write(query->packet, dname_name(dname), dname->name_size); +} + +int +print_name_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + assert(rr->rdlength == sizeof(void*)); + /* This prints a reference to a name stored as a pointer. */ + return print_domain(output, rr->rdlength, rr->rdata, &length); +} + +int32_t +read_a_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + if (rdlength != 4) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_a_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + assert(rr->rdlength == 4); + return print_ip4(output, rr->rdlength, rr->rdata, &length); +} + +int32_t +read_soa_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *primary_domain, *mailbox_domain; + struct dname_buffer primary, mailbox; + size_t size; + + /* domain + domain + uint32 + uint32 + uint32 + uint32 + uint32 */ + const size_t mark = buffer_position(packet); + if (!dname_make_from_packet_buffered(&primary, packet, 1, 1) || + !dname_make_from_packet_buffered(&mailbox, packet, 1, 1) || + rdlength != (buffer_position(packet) - mark) + 20) + return MALFORMED; + + size = sizeof(**rr) + 2 * sizeof(void*) + 20; + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + primary_domain = domain_table_insert(domains, (void*)&primary); + primary_domain->usage++; + mailbox_domain = domain_table_insert(domains, (void*)&mailbox); + mailbox_domain->usage++; + + memcpy((*rr)->rdata, &primary_domain, sizeof(void*)); + memcpy((*rr)->rdata + sizeof(void*), &mailbox_domain, sizeof(void*)); + buffer_read(packet, (*rr)->rdata + 2 * sizeof(void*), 20); + (*rr)->rdlength = 2 * sizeof(void*) + 20; + return rdlength; +} + +void +write_soa_rdata(struct query *query, const struct rr *rr) +{ + struct domain *primary, *mailbox; + /* domain + domain + uint32 + uint32 + uint32 + uint32 + uint32 */ + assert(rr->rdlength == 2 * sizeof(void*) + 20); + memcpy(&primary, rr->rdata, sizeof(void*)); + memcpy(&mailbox, rr->rdata + sizeof(void*), sizeof(void*)); + encode_dname(query, primary); + encode_dname(query, mailbox); + buffer_write(query->packet, rr->rdata + (2 * sizeof(void*)), 20); +} + +int +print_soa_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + uint32_t serial, refresh, retry, expire, minimum; + assert(rr->rdlength == 2 * sizeof(void*) + 20); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + + assert(length == 2 * sizeof(void*)); + serial = read_uint32(rr->rdata + length); + refresh = read_uint32(rr->rdata + length + 4); + retry = read_uint32(rr->rdata + length + 8); + expire = read_uint32(rr->rdata + length + 12); + minimum = read_uint32(rr->rdata + length + 16); + + buffer_printf( + output, " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32, + serial, refresh, retry, expire, minimum); + return 1; +} + +int +print_soa_rdata_twoline(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + uint32_t serial, refresh, retry, expire, minimum; + assert(rr->rdlength == 2 * sizeof(void*) + 20); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + + assert(length == 2 * sizeof(void*)); + serial = read_uint32(rr->rdata + length); + refresh = read_uint32(rr->rdata + length + 4); + retry = read_uint32(rr->rdata + length + 8); + expire = read_uint32(rr->rdata + length + 12); + minimum = read_uint32(rr->rdata + length + 16); + + buffer_printf( + output, " (\n\t\t%" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 " )", + serial, refresh, retry, expire, minimum); + return 1; +} + +int32_t +read_wks_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + if (rdlength < 5) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +/* + * 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). + */ +int +print_wks_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + uint8_t protocol; + int bits; + const uint8_t* bitmap; + + if(rr->rdlength < 5) + return 0; + if (!print_ip4(output, rr->rdlength, rr->rdata, &length)) + return 0; + + buffer_printf(output, " "); + protocol = rr->rdata[4]; + if(protocol == 6) + buffer_printf(output, "tcp"); + else if(protocol == 17) + buffer_printf(output, "udp"); + else + buffer_printf(output, "%" PRIu8, protocol); + + bits = (rr->rdlength - 5) * 8; + bitmap = rr->rdata + 5; + for (int service = 0; service < bits; service++) { + if (get_bit(bitmap, service)) + buffer_printf(output, " %d", service); + } + return 1; +} + +int32_t +read_hinfo_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + uint16_t length = 0; + const size_t mark = buffer_position(packet); + + if(!buffer_available(packet, rdlength)) + return MALFORMED; + if (skip_string(packet, rdlength, &length) < 0 + || skip_string(packet, rdlength, &length) < 0 + || rdlength != length) + return MALFORMED; + buffer_set_position(packet, mark); + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_hinfo_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if (!print_string(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_string(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_minfo_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *rmailbx_domain, *emailbx_domain; + struct dname_buffer rmailbx, emailbx; + size_t size; + const size_t mark = buffer_position(packet); + + if (buffer_remaining(packet) < rdlength || + !dname_make_from_packet_buffered(&rmailbx, packet, 1, 1) || + !dname_make_from_packet_buffered(&emailbx, packet, 1, 1) || + rdlength != buffer_position(packet)-mark) + return MALFORMED; + size = sizeof(**rr) + 2 * sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + rmailbx_domain = domain_table_insert(domains, (void*)&rmailbx); + rmailbx_domain->usage++; + emailbx_domain = domain_table_insert(domains, (void*)&emailbx); + emailbx_domain->usage++; + memcpy((*rr)->rdata, &rmailbx_domain, sizeof(void*)); + memcpy((*rr)->rdata + sizeof(void*), &emailbx_domain, sizeof(void*)); + (*rr)->rdlength = 2 * sizeof(void*); + return rdlength; +} + +void +write_minfo_rdata(struct query *query, const struct rr *rr) +{ + struct domain *rmailbx, *emailbx; + assert(rr->rdlength == 2 * sizeof(void*)); + memcpy(&rmailbx, rr->rdata, sizeof(void*)); + memcpy(&emailbx, rr->rdata + sizeof(void*), sizeof(void*)); + encode_dname(query, rmailbx); + encode_dname(query, emailbx); +} + +int +print_minfo_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + assert(rr->rdlength == 2 * sizeof(void*)); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + assert(rr->rdlength == length); + return 1; +} + +int32_t +read_mx_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *domain; + struct dname_buffer exchange; + size_t size; + + /* short + name */ + const size_t mark = buffer_position(packet); + if (buffer_remaining(packet) < rdlength || rdlength < 2) + return MALFORMED; + buffer_skip(packet, 2); + if (!dname_make_from_packet_buffered(&exchange, packet, 1, 1)) + return MALFORMED; + if (rdlength != buffer_position(packet)-mark) + return MALFORMED; + size = sizeof(**rr) + 2 + sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return -1; + domain = domain_table_insert(domains, (void*)&exchange); + domain->usage++; + buffer_read_at(packet, mark, (*rr)->rdata, 2); + memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); + (*rr)->rdlength = 2 + sizeof(void*); + return rdlength; +} + +void +write_mx_rdata(struct query *query, const struct rr *rr) +{ + struct domain *domain; + assert(rr->rdlength == 2 + sizeof(void*)); + memcpy(&domain, rr->rdata + 2, sizeof(void*)); + buffer_write(query->packet, rr->rdata, 2); + encode_dname(query, domain); +} + +int +print_mx_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 2; + assert(rr->rdlength > length); + buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + assert(rr->rdlength == length); + return 1; +} + +int32_t +read_txt_rdata(struct domain_table *owners, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + uint16_t length = 0; + const size_t mark = buffer_position(packet); + if(!buffer_available(packet, rdlength)) + return MALFORMED; + if (skip_strings(packet, rdlength, &length) < 0 || rdlength != length) + return MALFORMED; + buffer_set_position(packet, mark); + return read_rdata(owners, rdlength, packet, rr); +} + +int +print_txt_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if (length < rr->rdlength) { + if (!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; + while (length < rr->rdlength) { + buffer_printf(output, " "); + if (!print_string(output, rr->rdlength, rr->rdata, + &length)) + return 0; } } - if (descriptor->type == TYPE_SOA) { - buffer_printf(output, " )"); + return 1; +} + +int32_t +read_rp_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *mbox_domain, *txt_domain; + struct dname_buffer mbox, txt; + size_t size; + const size_t mark = buffer_position(packet); + + if (buffer_remaining(packet) < rdlength || + !dname_make_from_packet_buffered(&mbox, packet, 1 /*lenient*/, 1) || + !dname_make_from_packet_buffered(&txt, packet, 1 /*lenient*/, 1) || + rdlength != buffer_position(packet)-mark) + return MALFORMED; + size = sizeof(**rr) + 2 * sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + mbox_domain = domain_table_insert(domains, (void*)&mbox); + mbox_domain->usage++; + txt_domain = domain_table_insert(domains, (void*)&txt); + txt_domain->usage++; + memcpy((*rr)->rdata, &mbox_domain, sizeof(void*)); + memcpy((*rr)->rdata + sizeof(void*), &txt_domain, sizeof(void*)); + (*rr)->rdlength = 2 * sizeof(void*); + return rdlength; +} + +void +write_rp_rdata(struct query *query, const struct rr *rr) +{ + struct domain *mbox_domain, *txt_domain; + const struct dname *mbox, *txt; + + assert(rr->rdlength == 2 * sizeof(void*)); + memcpy(&mbox_domain, rr->rdata, sizeof(void*)); + memcpy(&txt_domain, rr->rdata + sizeof(void*), sizeof(void*)); + mbox = domain_dname(mbox_domain); + txt = domain_dname(txt_domain); + buffer_write(query->packet, dname_name(mbox), mbox->name_size); + buffer_write(query->packet, dname_name(txt), txt->name_size); +} + +int +print_rp_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if(!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if(!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + assert(rr->rdlength == length); + return 1; +} + +int32_t +read_afsdb_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *domain; + struct dname_buffer hostname; + size_t size; + /* short + uncompressed name */ + const size_t mark = buffer_position(packet); + + if (buffer_remaining(packet) < rdlength || rdlength < 2) + return MALFORMED; + buffer_skip(packet, 2); + if (!dname_make_from_packet_buffered(&hostname, packet, + 1 /*lenient*/, 1) || + rdlength != buffer_position(packet)-mark) + return MALFORMED; + size = sizeof(**rr) + 2 + sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + domain = domain_table_insert(domains, (void*)&hostname); + domain->usage++; + buffer_read_at(packet, mark, (*rr)->rdata, 2); + memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); + (*rr)->rdlength = 2 + sizeof(void*); + return rdlength; +} + +void +write_afsdb_rdata(struct query *query, const struct rr *rr) +{ + struct domain *domain; + const struct dname *dname; + + assert(rr->rdlength == 2 + sizeof(void*)); + memcpy(&domain, rr->rdata + 2, sizeof(void*)); + dname = domain_dname(domain); + buffer_write(query->packet, rr->rdata, 2); + buffer_write(query->packet, dname_name(dname), dname->name_size); +} + +int +print_afsdb_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t subtype, length=2; + assert(rr->rdlength == 2 + sizeof(void*)); + subtype = read_uint16(rr->rdata); + buffer_printf(output, "%" PRIu16 " ", subtype); + if(!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + return 1; +} + +int32_t +read_x25_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + uint16_t length = 0; + const size_t mark = buffer_position(packet); + if(!buffer_available(packet, rdlength)) + return MALFORMED; + if (skip_string(packet, rdlength, &length) < 0 || rdlength != length) + return MALFORMED; + buffer_set_position(packet, mark); + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_x25_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if (!print_string(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_isdn_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + uint16_t length = 0; + const size_t mark = buffer_position(packet); + + if(!buffer_available(packet, rdlength)) + return MALFORMED; + if (skip_string(packet, rdlength, &length) < 0) + return MALFORMED; + if(rdlength > length) { + /* Optional subaddress field is present. */ + if (skip_string(packet, rdlength, &length) < 0 + || rdlength != length) + return MALFORMED; + } + buffer_set_position(packet, mark); + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_isdn_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if (!print_string(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength > length) { + /* Optional subaddress field is present. */ + buffer_printf(output, " "); + if (!print_string(output, rr->rdlength, rr->rdata, &length)) + return 0; } + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_rt_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *domain; + struct dname_buffer dname; + size_t size; + const size_t mark = buffer_position(packet); + + if(!buffer_available(packet, rdlength)) + return MALFORMED; + if (rdlength < 2) + return MALFORMED; + buffer_skip(packet, 2); + if (!dname_make_from_packet_buffered(&dname, packet, 1 /*lenient*/, 1)) + return MALFORMED; + if (rdlength != buffer_position(packet)-mark) + return MALFORMED; + size = sizeof(**rr) + 2 + sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return -1; + domain = domain_table_insert(domains, (void*)&dname); + domain->usage++; + buffer_read_at(packet, mark, (*rr)->rdata, 2); + memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); + (*rr)->rdlength = 2 + sizeof(void*); + return rdlength; +} + +void +write_rt_rdata(struct query *query, const struct rr *rr) +{ + struct domain *domain; + const struct dname *dname; + + assert(rr->rdlength == 2 + sizeof(void*)); + memcpy(&domain, rr->rdata + 2, sizeof(void*)); + dname = domain_dname(domain); + buffer_write(query->packet, rr->rdata, 2); + buffer_write(query->packet, dname_name(dname), dname->name_size); +} + +int +print_nsap_rdata(struct buffer *output, const struct rr *rr) +{ + buffer_printf(output, "0x"); + buffer_print_hex(output, rr->rdata, rr->rdlength); + return 1; +} + +int +print_nsap_ptr_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if(!print_unquoted(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int +print_key_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 4; + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu16 " %" PRIu8 " %" PRIu8 " ", + read_uint16(rr->rdata), rr->rdata[2], rr->rdata[3]); + if (!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_px_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *map822_domain, *mapx400_domain; + struct dname_buffer map822, mapx400; + size_t size; + const size_t mark = buffer_position(packet); + + /* short + uncompressed name + uncompressed name */ + if (buffer_remaining(packet) < rdlength || + rdlength < 2) + return MALFORMED; + buffer_skip(packet, 2); + if(!dname_make_from_packet_buffered(&map822, packet, + 1 /*lenient*/, 1) || + !dname_make_from_packet_buffered(&mapx400, packet, + 1 /*lenient*/, 1) || + rdlength != buffer_position(packet) - mark) + return MALFORMED; + + size = sizeof(**rr) + 2 + 2*sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + map822_domain = domain_table_insert(domains, (void*)&map822); + map822_domain->usage++; + mapx400_domain = domain_table_insert(domains, (void*)&mapx400); + mapx400_domain->usage++; + + buffer_read_at(packet, mark, (*rr)->rdata, 2); + memcpy((*rr)->rdata+2, &map822_domain, sizeof(void*)); + memcpy((*rr)->rdata+2+sizeof(void*), &mapx400_domain, sizeof(void*)); + (*rr)->rdlength = 2 + 2*sizeof(void*); + return rdlength; +} + +void +write_px_rdata(struct query *query, const struct rr *rr) +{ + struct domain *map822_domain, *mapx400_domain; + const struct dname *map822, *mapx400; + + memcpy(&map822_domain, rr->rdata + 2, sizeof(void*)); + memcpy(&mapx400_domain, rr->rdata + 2 + sizeof(void*), sizeof(void*)); + map822 = domain_dname(map822_domain); + mapx400 = domain_dname(mapx400_domain); + buffer_write(query->packet, rr->rdata, 2); + buffer_write(query->packet, dname_name(map822), map822->name_size); + buffer_write(query->packet, dname_name(mapx400), mapx400->name_size); +} + +int +print_px_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 2; + assert(rr->rdlength > 3); + buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + assert(rr->rdlength == length); + return 1; +} +int +print_gpos_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if(!print_unquoted(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if(!print_unquoted(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if(!print_unquoted(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; return 1; } +int32_t +read_aaaa_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + if (rdlength != 16) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_aaaa_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + assert(rr->rdlength == 16); + return print_ip6(output, rr->rdlength, rr->rdata, &length); +} + +int32_t +read_loc_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + size_t mark; + uint8_t version; + uint16_t size_version_0; + + if(!buffer_available(packet, rdlength)) + return MALFORMED; + /* version (byte) */ + if (rdlength < 1) + return MALFORMED; + /* version (byte) + size (byte) + * + horizontal precision (byte) + vertical precision (byte) + * + latitude (uint32) + longitude (uint32) + altitude (uint32) */ + mark = buffer_position(packet); + version = buffer_read_u8_at(packet, mark + 0); + size_version_0 = 16u; + if (version == 0 && rdlength != size_version_0) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +/* Print the cm for LOC. */ +static void +loc_cm_print(struct buffer* output, uint8_t mantissa, uint8_t exponent) +{ + uint8_t i; + /* is it 0. ? */ + if(exponent < 2) { + if(exponent == 1) + mantissa *= 10; + buffer_printf(output, "0.%02ld", (long)mantissa); + return; + } + /* always */ + buffer_printf(output, "%d", (int)mantissa); + for(i=0; irdlength < 16) + return 0; + version = rr->rdata[0]; + if(version != 0) + return 0; + size = rr->rdata[1]; + horizontal_precision = rr->rdata[2]; + vertical_precision = rr->rdata[3]; + + latitude = read_uint32(rr->rdata+4); + longitude = read_uint32(rr->rdata+8); + altitude = read_uint32(rr->rdata+12); + + if (latitude > equator) { + northerness = 'N'; + latitude = latitude - equator; + } else { + northerness = 'S'; + latitude = equator - latitude; + } + h = latitude / (1000 * 60 * 60); + latitude = latitude % (1000 * 60 * 60); + m = latitude / (1000 * 60); + latitude = latitude % (1000 * 60); + s = (double) latitude / 1000.0; + buffer_printf(output, "%02u %02u %06.3f %c ", + h, m, s, northerness); + + if (longitude > equator) { + easterness = 'E'; + longitude = longitude - equator; + } else { + easterness = 'W'; + longitude = equator - longitude; + } + h = longitude / (1000 * 60 * 60); + longitude = longitude % (1000 * 60 * 60); + m = longitude / (1000 * 60); + longitude = longitude % (1000 * 60); + s = (double) longitude / (1000.0); + buffer_printf(output, "%02u %02u %06.3f %c ", + h, m, s, easterness); + + s = ((double) altitude) / 100; + s -= 100000; + + if(altitude%100 != 0) + buffer_printf(output, "%.2f", s); + else + buffer_printf(output, "%.0f", s); + buffer_printf(output, "m "); + + loc_cm_print(output, (size & 0xf0) >> 4, size & 0x0f); + buffer_printf(output, "m "); + + loc_cm_print(output, (horizontal_precision & 0xf0) >> 4, + horizontal_precision & 0x0f); + buffer_printf(output, "m "); + + loc_cm_print(output, (vertical_precision & 0xf0) >> 4, + vertical_precision & 0x0f); + buffer_printf(output, "m"); + + return 1; +} + +int32_t +read_nxt_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *domain; + struct dname_buffer dname; + size_t size; + uint16_t bitmap_size; + const size_t mark = buffer_position(packet); + + /* name + nxt */ + if(!buffer_available(packet, rdlength)) + return MALFORMED; + if (!dname_make_from_packet_buffered(&dname, packet, 1, 1)) + return MALFORMED; + if(rdlength < buffer_position(packet) - mark) + return MALFORMED; + bitmap_size = rdlength - (buffer_position(packet) - mark); + size = sizeof(**rr) + sizeof(void*) + bitmap_size; + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + domain = domain_table_insert(domains, (void*)&dname); + domain->usage++; + memcpy((*rr)->rdata, &domain, sizeof(void*)); + if(bitmap_size != 0) + buffer_read(packet, (*rr)->rdata + sizeof(void*), bitmap_size); + (*rr)->rdlength = sizeof(void*) + bitmap_size; + return rdlength; +} + +void +write_nxt_rdata(struct query *query, const struct rr *rr) +{ + struct domain *domain; + const struct dname *dname; + + assert(rr->rdlength >= sizeof(void*)); + memcpy(&domain, rr->rdata, sizeof(void*)); + dname = domain_dname(domain); + buffer_write(query->packet, dname_name(dname), dname->name_size); + if(rr->rdlength - sizeof(void*) != 0) + buffer_write(query->packet, rr->rdata + sizeof(void*), + rr->rdlength - sizeof(void*)); +} + +int +print_nxt_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + int bitmap_size; + const uint8_t* bitmap; + + assert(rr->rdlength >= sizeof(void*)); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + + bitmap_size = rr->rdlength - length; + bitmap = rr->rdata + length; + for (int type = 0; type < bitmap_size * 8; type++) { + if (get_bit(bitmap, type)) { + buffer_printf(output, " %s", rrtype_to_string(type)); + } + } + + return 1; +} + +int +print_eid_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if(!print_base16(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int +print_nimloc_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if(!print_base16(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_srv_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *domain; + struct dname_buffer dname; + size_t size; + const size_t mark = buffer_position(packet); + + /* short + short + short + name */ + if (buffer_remaining(packet) < rdlength || rdlength < 6) + return MALFORMED; + buffer_skip(packet, 6); + if (!dname_make_from_packet_buffered(&dname, packet, + 1 /* lenient */, 1) || + rdlength != buffer_position(packet)-mark) + return MALFORMED; + size = sizeof(**rr) + 6 + sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + domain = domain_table_insert(domains, (void*)&dname); + domain->usage++; + buffer_read_at(packet, mark, (*rr)->rdata, 6); + memcpy((*rr)->rdata + 6, &domain, sizeof(void*)); + (*rr)->rdlength = 6 + sizeof(void*); + return rdlength; +} + +void +write_srv_rdata(struct query *query, const struct rr *rr) +{ + struct domain *domain; + const struct dname *dname; + + assert(rr->rdlength == 6 + sizeof(void*)); + memcpy(&domain, rr->rdata + 6, sizeof(void*)); + dname = domain_dname(domain); + buffer_write(query->packet, rr->rdata, 6); + buffer_write(query->packet, dname_name(dname), dname->name_size); +} + +int +print_srv_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 6; + assert(rr->rdlength > length); + buffer_printf( + output, "%" PRIu16 " %" PRIu16 " %" PRIu16 " ", + read_uint16(rr->rdata), read_uint16(rr->rdata+2), + read_uint16(rr->rdata+4)); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + assert(rr->rdlength == length); + return 1; +} + +int +print_atma_rdata(struct buffer *output, const struct rr *rr) +{ + uint8_t format; + if(rr->rdlength < 1) + return 0; + format = rr->rdata[0]; + if(format == 0) { + /* AESA format (ATM End System Address). */ + uint16_t length = 1; + if (!print_base16(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; + } else if(format == 1) { + /* E.164 format. */ + /* '+' and then digits '0'-'9' from the rdata string. */ + buffer_printf(output, "+"); + for (size_t i = 1; i < rr->rdlength; i++) { + char ch = (char)rr->rdata[i]; + if(!isdigit((unsigned char)ch)) + return 0; + buffer_printf(output, "%c", ch); + } + return 1; + } + /* Unknown format. */ + return 0; +} + +int32_t +read_naptr_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *domain; + struct dname_buffer dname; + uint16_t length = 4; + size_t size; + const size_t mark = buffer_position(packet); + + /* short + short + text + text + text + name */ + if (buffer_remaining(packet) < rdlength || + rdlength < length) + return MALFORMED; + buffer_skip(packet, 4); + if(skip_string(packet, rdlength, &length) < 0 || + skip_string(packet, rdlength, &length) < 0 || + skip_string(packet, rdlength, &length) < 0 || + !dname_make_from_packet_buffered(&dname, packet, 1, 1) || + rdlength != buffer_position(packet) - mark) + return MALFORMED; + + size = sizeof(**rr) + length + sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + domain = domain_table_insert(domains, (void*)&dname); + domain->usage++; + buffer_read_at(packet, mark, (*rr)->rdata, length); + memcpy((*rr)->rdata + length, &domain, sizeof(void*)); + (*rr)->rdlength = length + sizeof(void*); + return rdlength; +} + +void +write_naptr_rdata(struct query *query, const struct rr *rr) +{ + struct domain *domain; + const struct dname *dname; + uint16_t length; + + /* short + short + string + string + string + uncompressed name */ + assert(rr->rdlength >= 7 + sizeof(void*)); + length = rr->rdlength - sizeof(void*); + memcpy(&domain, rr->rdata + length, sizeof(void*)); + dname = domain_dname(domain); + buffer_write(query->packet, rr->rdata, length); + buffer_write(query->packet, dname_name(dname), dname->name_size); +} + +int +print_naptr_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 4; + + assert(rr->rdlength >= 7 + sizeof(void*)); + buffer_printf( + output, "%" PRIu16 " %" PRIu16 " ", + read_uint16(rr->rdata), read_uint16(rr->rdata+2)); + if (!print_string(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_string(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_string(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + assert(rr->rdlength == length); + return 1; +} + +int32_t +read_kx_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *domain; + struct dname_buffer dname; + const size_t mark = buffer_position(packet); + size_t size; + + /* short + uncompressed name */ + if(buffer_remaining(packet) < rdlength || rdlength < 2) + return MALFORMED; + buffer_skip(packet, 2); + if(!dname_make_from_packet_buffered(&dname, packet, + 1 /* lenient */, 1) || + rdlength != buffer_position(packet) - mark) + return MALFORMED; + + size = sizeof(**rr) + 2 + sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + domain = domain_table_insert(domains, (void*)&dname); + domain->usage++; + buffer_read_at(packet, mark, (*rr)->rdata, 2); + memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); + (*rr)->rdlength = 2 + sizeof(void*); + return rdlength; +} + +void +write_kx_rdata(struct query *query, const struct rr *rr) +{ + struct domain *domain; + const struct dname *dname; + + /* short + uncompressed name */ + assert(rr->rdlength == 2 + sizeof(void*)); + memcpy(&domain, rr->rdata + 2, sizeof(void*)); + dname = domain_dname(domain); + buffer_write(query->packet, rr->rdata, 2); + buffer_write(query->packet, dname_name(dname), dname->name_size); +} + +int32_t +read_cert_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* short + short + byte + binary */ + if (rdlength < 5) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_cert_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if(rr->rdlength < 5) + return 0; + if(!print_certificate_type(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " %" PRIu16 " %" PRIu8 " ", + read_uint16(rr->rdata+2), rr->rdata[4]); + length += 3; + if (!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int +print_sink_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 2; + if(rr->rdlength < 2) + return 0; + buffer_printf(output, "%" PRIu8 " %" PRIu8 " ", + rr->rdata[0], rr->rdata[1]); + if (!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_apl_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + uint16_t length = 0; + const uint8_t *rdata = buffer_current(packet); + + if (buffer_remaining(packet) < rdlength) + return MALFORMED; + while (rdlength - length >= 4) { + uint8_t afdlength = rdata[length + 3] & APL_LENGTH_MASK; + uint16_t afam = read_uint16(rdata + length); + if (rdlength - (length + 4) < afdlength || + (afam == 1 && afdlength > 4) || + (afam == 2 && afdlength > 16)) + return MALFORMED; + length += 4 + afdlength; + } + + if (length != rdlength) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +/* + * Print one APL field. + * @param output: string is printed to the buffer. + * @param rdlength: length of rdata. + * @param rdata: the rdata + * @param offset: on input the current position, adjusted on output to + * increment for the rdata used. + * @return false on failure. + */ +static int +print_apl(struct buffer *output, size_t rdlength, const uint8_t *rdata, + uint16_t *offset) +{ + size_t size = rdlength - *offset; + uint16_t address_family; + uint8_t prefix, length, negated; + int af; + char text_address[INET6_ADDRSTRLEN + 1]; + uint8_t address[16]; + + if (rdlength < *offset || size < 4) + return 0; + + address_family = read_uint16(rdata + *offset); + prefix = rdata[*offset + 2]; + length = rdata[*offset + 3] & APL_LENGTH_MASK; + negated = rdata[*offset + 3] & APL_NEGATION_MASK; + af = -1; + + switch (address_family) { + case 1: af = AF_INET; + if(length > 4) + return 0; + break; + case 2: af = AF_INET6; + if(length > 16) + return 0; + break; + } + + if (af == -1 || size - 4 < length) + return 0; + + memset(address, 0, sizeof(address)); + if(length > sizeof(address)) + return 0; + memmove(address, rdata + *offset + 4, length); + + if (!inet_ntop(af, address, text_address, sizeof(text_address))) + return 0; + + buffer_printf( + output, "%s%" PRIu16 ":%s/%" PRIu8, + (negated ? "!" : ""), address_family, text_address, prefix); + *offset += 4 + length; + return 1; +} + +int +print_apl_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + + while (length < rr->rdlength) { + if(length != 0) + buffer_printf(output, " "); + if (!print_apl(output, rr->rdlength, rr->rdata, &length)) + return 0; + } + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_ds_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* short + byte + byte + binary */ + if (rdlength < 4) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_ds_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 4; + + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu16 " %" PRIu8 " %" PRIu8 " ", + read_uint16(rr->rdata), rr->rdata[2], rr->rdata[3]); + if (!print_base16(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_sshfp_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* byte + byte + binary */ + if (rdlength < 2) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_sshfp_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 2; + uint8_t algorithm, ftype; + + if(rr->rdlength < length) + return 0; + algorithm = rr->rdata[0]; + ftype = rr->rdata[1]; + + buffer_printf(output, "%" PRIu8 " %" PRIu8 " ", algorithm, ftype); + if (!print_base16(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_ipseckey_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct dname_buffer gateway; + const uint8_t *gateway_rdata; + uint8_t gateway_length = 0; + const size_t mark = buffer_position(packet); + uint16_t keylen; + + /* byte + byte + byte + gateway + binary */ + if (buffer_remaining(packet) < rdlength || rdlength < 3) + return MALFORMED; + + buffer_skip(packet, 3); + + switch (buffer_read_u8_at(packet, mark + 1)) { + case IPSECKEY_NOGATEWAY: + gateway_length = 0; + gateway_rdata = NULL; + break; + case IPSECKEY_IP4: + gateway_length = 4; + gateway_rdata = buffer_current(packet); + if (rdlength < 3 + gateway_length) + return MALFORMED; + buffer_skip(packet, gateway_length); + break; + case IPSECKEY_IP6: + gateway_length = 16; + gateway_rdata = buffer_current(packet); + if (rdlength < 3 + gateway_length) + return MALFORMED; + buffer_skip(packet, gateway_length); + break; + case IPSECKEY_DNAME: + /* The dname is stored as literal dname. On the wire + * skip possibly compressed format, in the rdata there + * is an uncompressed wire format. */ + if (!dname_make_from_packet_buffered(&gateway, packet, + 1 /* lenient */, 0)) + return MALFORMED; + if(rdlength < buffer_position(packet) - mark) + return MALFORMED; + gateway_length = gateway.dname.name_size; + gateway_rdata = dname_name((void*)&gateway); + break; + default: + return MALFORMED; + } + keylen = rdlength - (buffer_position(packet)-mark); + + if (!(*rr = region_alloc(domains->region, + sizeof(**rr) + 3 + gateway_length + keylen))) + return TRUNCATED; + + buffer_read_at(packet, mark, (*rr)->rdata, 3); + if(gateway_rdata) + memcpy((*rr)->rdata + 3, gateway_rdata, gateway_length); + if(keylen != 0) + buffer_read(packet, (*rr)->rdata + 3 + gateway_length, keylen); + (*rr)->rdlength = 3 + gateway_length + keylen; + return rdlength; +} + +int +print_ipseckey_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 3; + uint8_t gateway_type; + + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu8 " %" PRIu8 " %" PRIu8 " ", + rr->rdata[0], rr->rdata[1], rr->rdata[2]); + gateway_type = rr->rdata[1]; + switch (gateway_type) { + case IPSECKEY_NOGATEWAY: + buffer_printf(output, "."); + break; + case IPSECKEY_IP4: + if (!print_ip4(output, rr->rdlength, rr->rdata, &length)) + return 0; + break; + case IPSECKEY_IP6: + if (!print_ip6(output, rr->rdlength, rr->rdata, &length)) + return 0; + break; + case IPSECKEY_DNAME: + if (!print_name_literal(output, rr->rdlength, rr->rdata, + &length)) + return 0; + break; + default: + return 0; + } + + if(rr->rdlength > length) { + /* Print key field in base64. */ + buffer_printf(output, " "); + if (!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + } + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +ipseckey_gateway_length(uint16_t rdlength, const uint8_t *rdata, + uint16_t offset, struct domain** domain) +{ + /* The ipseckey gateway length depends only on earlier bytes, so both + * the in-memory and uncompressed wireformat refer to the same + * earlier bytes. Also the domain name is stored literally, so it + * does not need to return a reference. */ + uint8_t gateway_type; + *domain = NULL; + /* Calculate the gateway length, based on the gateway type. + * That is stored in an earlier field. */ + if(rdlength < 3 || offset < 3) + return -1; /* too short */ + gateway_type = rdata[1]; + switch(gateway_type) { + case IPSECKEY_NOGATEWAY: + return 0; + case IPSECKEY_IP4: + return 4; + case IPSECKEY_IP6: + return 16; + case IPSECKEY_DNAME: + return buf_dname_length(rdata+offset, rdlength-offset); + default: + /* Unknown gateway type. */ + break; + } + return -1; +} + +int32_t +read_rrsig_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct dname_buffer signer; + const size_t mark = buffer_position(packet); + uint16_t memrdlen, b64len; + + /* short + byte + byte + uint32 + uint32 + uint32 + short */ + if (buffer_remaining(packet) < rdlength || rdlength < 18) + return MALFORMED; + buffer_skip(packet, 18); + if (!dname_make_from_packet_buffered(&signer, packet, + 1 /* lenient */, 0)) + return MALFORMED; + if (rdlength < buffer_position(packet) - mark) + return MALFORMED; + memrdlen = 18 + signer.dname.name_size; + b64len = rdlength - (buffer_position(packet) - mark); + if (!(*rr = region_alloc(domains->region, sizeof(**rr) + memrdlen + b64len))) + return TRUNCATED; + buffer_read_at(packet, mark, (*rr)->rdata, 18); + memcpy((*rr)->rdata + 18, dname_name((void*)&signer), + signer.dname.name_size); + if(b64len != 0) + buffer_read(packet, (*rr)->rdata+memrdlen, b64len); + (*rr)->rdlength = memrdlen + b64len; + return rdlength; +} + +int +print_rrsig_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 8; + + if(rr->rdlength < 18) + return 0; + buffer_printf( + output, "%s %" PRIu8 " %" PRIu8 " %" PRIu32 " ", + rrtype_to_string(read_uint16(rr->rdata)), rr->rdata[2], + rr->rdata[3], read_uint32(rr->rdata+4)); + if (!print_time(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_time(output, rr->rdlength, rr->rdata, &length)) + return 0; + + buffer_printf(output, " %" PRIu16 " ", read_uint16(rr->rdata+length)); + length += 2; + + if (!print_name_literal(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_nsec_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct dname_buffer next; + uint16_t length, memrdlen, bitmaplen; + size_t bitmapmark; + const size_t mark = buffer_position(packet); + + /* uncompressed name + nsec */ + if(buffer_remaining(packet) < rdlength || + !dname_make_from_packet_buffered(&next, packet, + 1 /* lenient */, 0 /* The NSEC record can have a non normalized + dname as the next owner name. And if so this should be + preserved, so that the RRSIG verifies. */ ) || + rdlength < buffer_position(packet) - mark) + return MALFORMED; + + bitmapmark = buffer_position(packet); + length = bitmapmark - mark; + if (skip_nsec(packet, rdlength, &length) < 0 + || rdlength != length) + return MALFORMED; + bitmaplen = buffer_position(packet) - bitmapmark; + memrdlen = next.dname.name_size + bitmaplen; + if (!(*rr = region_alloc(domains->region, sizeof(**rr) + memrdlen))) + return TRUNCATED; + memcpy((*rr)->rdata, dname_name((void*)&next), next.dname.name_size); + if(bitmaplen != 0) + buffer_read_at(packet, bitmapmark, + (*rr)->rdata + next.dname.name_size, bitmaplen); + (*rr)->rdlength = memrdlen; + return rdlength; +} + +int +print_nsec_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + + if (!print_name_literal(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_nsec_bitmap(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_dnskey_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* short + byte + byte + binary of remainder */ + if (rdlength < 4) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_dnskey_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 4; + + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu16 " %" PRIu8 " %" PRIu8 " ", + read_uint16(rr->rdata), rr->rdata[2], rr->rdata[3]); + if (!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_dhcid_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* short + byte + digest */ + if (rdlength < 3) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_dhcid_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + + if (!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_nsec3_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + uint16_t length = 4; + /* byte + byte + short + string + string + binary */ + const size_t mark = buffer_position(packet); + + if (buffer_remaining(packet) < rdlength || rdlength < length) + return MALFORMED; + buffer_skip(packet, length); + if (skip_string(packet, rdlength, &length) < 0 || + skip_string(packet, rdlength, &length) < 0 || + skip_nsec(packet, rdlength, &length) < 0 || + rdlength != length) + return MALFORMED; + buffer_set_position(packet, mark); + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_nsec3_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 4; + + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu8 " %" PRIu8 " %" PRIu16 " ", + rr->rdata[0], rr->rdata[1], read_uint16(rr->rdata + 2)); + if (!print_salt(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_base32(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if (!print_nsec_bitmap(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_nsec3param_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + uint16_t length = 4; + /* byte + byte + short + string */ + const size_t mark = buffer_position(packet); + + if (buffer_remaining(packet) < rdlength || rdlength < length) + return MALFORMED; + buffer_skip(packet, length); + if (skip_string(packet, rdlength, &length) < 0 || + rdlength != length) + return MALFORMED; + buffer_set_position(packet, mark); + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_nsec3param_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 4; + + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu8 " %" PRIu8 " %" PRIu16 " ", + rr->rdata[0], rr->rdata[1], read_uint16(rr->rdata + 2)); + + if (!print_salt(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_tlsa_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* byte + byte + byte + binary */ + if (rdlength < 3) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_tlsa_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 3; + + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu8 " %" PRIu8 " %" PRIu8 " ", + rr->rdata[0], rr->rdata[1], rr->rdata[2]); + + if (!print_base16(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_hip_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* byte (hit length) + byte (PK algorithm) + short (PK length) + + * HIT(hex) + pubkey(base64) + rendezvous servers(literal dnames) */ + if (rdlength < 4) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_hip_rdata(struct buffer *output, const struct rr *rr) +{ + /* byte (hit length) + byte (PK algorithm) + short (PK length) + + * HIT(hex) + pubkey(base64) + rendezvous servers(literal dnames) */ + uint8_t hit_length, pk_algorithm; + uint16_t pk_length; + uint16_t length = 4; + + if(rr->rdlength < length) + return 0; + hit_length = rr->rdata[0]; + pk_algorithm = rr->rdata[1]; + pk_length = read_uint16(rr->rdata+2); + if(4 + (uint32_t)hit_length + (uint32_t)pk_length > (uint32_t)rr->rdlength) + return 0; + buffer_printf(output, "%" PRIu8 " ", pk_algorithm); + if(!print_base16(output, length+hit_length, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if(!print_base64(output, length+pk_length, rr->rdata, &length)) + return 0; + while(length < rr->rdlength) { + buffer_printf(output, " "); + if(!print_name_literal(output, rr->rdlength, rr->rdata, + &length)) + return 0; + } + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_rkey_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* short + byte + byte + binary of remainder */ + if (rdlength < 4) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_rkey_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 4; + + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu16 " %" PRIu8 " %" PRIu8 " ", + read_uint16(rr->rdata), rr->rdata[2], rr->rdata[3]); + if (!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_talink_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct dname_buffer prev, next; + const size_t mark = buffer_position(packet); + if(!buffer_available(packet, rdlength)) + return MALFORMED; + if(!dname_make_from_packet_buffered(&prev, packet, + 1 /* lenient */, 0)) + return MALFORMED; + if(buffer_position(packet)-mark > rdlength) + return MALFORMED; + if(!dname_make_from_packet_buffered(&next, packet, + 1 /* lenient */, 0)) + return MALFORMED; + if(buffer_position(packet)-mark != rdlength) + return MALFORMED; + if (!(*rr = region_alloc(domains->region, + sizeof(**rr) + prev.dname.name_size + next.dname.name_size))) + return TRUNCATED; + memcpy((*rr)->rdata, dname_name((void*)&prev), prev.dname.name_size); + memcpy((*rr)->rdata + prev.dname.name_size, dname_name((void*)&next), + next.dname.name_size); + (*rr)->rdlength = prev.dname.name_size + next.dname.name_size; + return rdlength; +} + +int +print_talink_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if(!print_name_literal(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if(!print_name_literal(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int +print_openpgpkey_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + + if (!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_csync_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* long + short + binary bitmap in remainder */ + if (rdlength < 6) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_csync_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 6; + + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu32 " %" PRIu16 " ", + read_uint32(rr->rdata), read_uint16(rr->rdata + 4)); + if (!print_nsec_bitmap(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_zonemd_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* long + byte + byte + binary */ + if (rdlength < 6) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_zonemd_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 6; + + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu32 " %" PRIu8 " %" PRIu8 " ", + read_uint32(rr->rdata), rr->rdata[4], rr->rdata[5]); + if (!print_base16(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_svcb_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *domain; + struct dname_buffer target; + uint16_t length = 2, svcparams_length = 0; + size_t size; + const size_t mark = buffer_position(packet); + + /* short + name + svc_params */ + if (buffer_remaining(packet) < rdlength || rdlength < length) + return MALFORMED; + buffer_skip(packet, length); + if (!dname_make_from_packet_buffered(&target, packet, + 1 /* lenient */, 1)) + return MALFORMED; + if(rdlength < buffer_position(packet) - mark) + return MALFORMED; + length = buffer_position(packet)-mark; + /* For secondary, the server should accept the wireformat more + * leniently. So this check is skipped. */ + /* + if(!skip_svcparams(packet, rdlength-length)) + return MALFORMED; + if(rdlength < buffer_position(packet) - mark) + return MALFORMED; + */ + svcparams_length = rdlength - length; + buffer_skip(packet, svcparams_length); + + size = sizeof(**rr) + 2 + sizeof(void*) + svcparams_length; + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + domain = domain_table_insert(domains, (void*)&target); + domain->usage++; + buffer_read_at(packet, mark, (*rr)->rdata, 2); + memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); + if(svcparams_length != 0) + buffer_read_at(packet, mark + length, + (*rr)->rdata + 2 + sizeof(void*), svcparams_length); + (*rr)->rdlength = 2 + sizeof(void*) + svcparams_length; + return rdlength; +} + +void +write_svcb_rdata(struct query *query, const struct rr *rr) +{ + struct domain *domain; + const struct dname *target; + uint8_t length; + + assert(rr->rdlength >= 2 + sizeof(void*)); + memcpy(&domain, rr->rdata + 2, sizeof(void*)); + target = domain_dname(domain); + buffer_write(query->packet, rr->rdata, 2); + buffer_write(query->packet, dname_name(target), target->name_size); + length = 2 + sizeof(void*); + if(rr->rdlength > length) + buffer_write(query->packet, rr->rdata + length, + rr->rdlength - length); +} + +int +print_svcb_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 2; + + assert(rr->rdlength > length); /* It has 2+sizeof(void*) at least. */ + buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + while (length < rr->rdlength) { + buffer_printf(output, " "); + if (!print_svcparam(output, rr->rdlength, rr->rdata, &length)) + return 0; + } + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_dsync_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct dname_buffer target; + uint16_t length = 5; + const size_t mark = buffer_position(packet); + + /* type + byte + short + literaldname */ + if (buffer_remaining(packet) < rdlength || rdlength < length) + return MALFORMED; + buffer_skip(packet, length); + if(!dname_make_from_packet_buffered(&target, packet, + 1 /* lenient */, 0) || + rdlength != buffer_position(packet) - mark) + return MALFORMED; + length += target.dname.name_size; + + if (!(*rr = region_alloc(domains->region, sizeof(**rr)+length))) + return TRUNCATED; + buffer_read_at(packet, mark, (*rr)->rdata, 5); + memcpy((*rr)->rdata + 5, dname_name((void*)&target), + target.dname.name_size); + (*rr)->rdlength = length; + return rdlength; +} + +int +print_dsync_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 5; + if(rr->rdlength < length) + return 0; + buffer_printf(output, "%s %" PRIu8 " %" PRIu16 " ", + rrtype_to_string(read_uint16(rr->rdata)), rr->rdata[2], + read_uint16(rr->rdata+3)); + if(!print_name_literal(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_hhit_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* A CBOR blob has at least 1 byte */ + if (rdlength < 1) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_hhit_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + + if (!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_brid_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* A CBOR blob has at least 1 byte */ + if (rdlength < 1) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_brid_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + + if (!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_nid_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + if (rdlength != 10) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_nid_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 2; + + if(rr->rdlength != 10) + return 0; + buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); + if (!print_ilnp64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_l32_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + if (rdlength != 6) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_l32_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 2; + + if(rr->rdlength != 6) + return 0; + buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); + if (!print_ip4(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_l64_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + if (rdlength != 10) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_l64_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 2; + + if(rr->rdlength != 10) + return 0; + buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); + if (!print_ilnp64(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_lp_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct domain *domain; + struct dname_buffer target; + size_t size; + /* short + name */ + const size_t mark = buffer_position(packet); + + if (buffer_remaining(packet) < rdlength || rdlength < 2) + return MALFORMED; + buffer_skip(packet, 2); + if (!dname_make_from_packet_buffered(&target, packet, + 1 /* lenient */, 1) || + rdlength != buffer_position(packet) - mark) + return MALFORMED; + size = sizeof(**rr) + 2 + sizeof(void*); + if (!(*rr = region_alloc(domains->region, size))) + return TRUNCATED; + domain = domain_table_insert(domains, (void*)&target); + domain->usage++; + buffer_read_at(packet, mark, (*rr)->rdata, 2); + memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); + (*rr)->rdlength = 2 + sizeof(void*); + return rdlength; +} + +void +write_lp_rdata(struct query *query, const struct rr *rr) +{ + struct domain *domain; + const struct dname *dname; + + /* short + uncompressed name */ + assert(rr->rdlength == 2 + sizeof(void*)); + memcpy(&domain, rr->rdata + 2, sizeof(void*)); + dname = domain_dname(domain); + buffer_write(query->packet, rr->rdata, 2); + buffer_write(query->packet, dname_name(dname), dname->name_size); +} + +int +print_lp_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 2; + + assert(rr->rdlength == 2 + sizeof(void*)); + buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); + if (!print_domain(output, rr->rdlength, rr->rdata, &length)) + return 0; + assert(rr->rdlength == length); + return 1; +} + +int32_t +read_eui48_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + if (rdlength != 6) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_eui48_rdata(struct buffer *output, const struct rr *rr) +{ + const uint8_t *x = rr->rdata; + if(rr->rdlength != 6) + return 0; + buffer_printf(output, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", + x[0], x[1], x[2], x[3], x[4], x[5]); + return 1; +} + +int32_t +read_eui64_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + if (rdlength != 8) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_eui64_rdata(struct buffer *output, const struct rr *rr) +{ + const uint8_t *x = rr->rdata; + if(rr->rdlength != 8) + return 0; + buffer_printf(output, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", + x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]); + return 1; +} + +int32_t +read_uri_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* short + short + long string */ + if (rdlength < 4) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_uri_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 4; + + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu16 " %" PRIu16 " ", + read_uint16(rr->rdata), read_uint16(rr->rdata + 2)); + if(!print_text(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int +print_resinfo_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 0; + if(!print_unquoteds(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_caa_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + const size_t mark = buffer_position(packet); + uint16_t length = 1; + + /* byte + string + long string */ + if (buffer_remaining(packet) < rdlength || rdlength < 2) + return MALFORMED; + buffer_skip(packet, 1); + if (skip_string(packet, rdlength, &length) < 0 || rdlength < length) + return MALFORMED; + buffer_set_position(packet, mark); + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_caa_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 1; + + if(rr->rdlength < 2) + return 0; + buffer_printf(output, "%" PRIu8 " ", rr->rdata[0]); + + length = 2 + rr->rdata[1]; + if (rr->rdlength < length) + return 0; + + for (uint16_t i = 2; i < length; ++i) { + char ch = (char) rr->rdata[i]; + if (isdigit((unsigned char)ch) || islower((unsigned char)ch)) + buffer_printf(output, "%c", ch); + else return 0; + } + + buffer_printf(output, " "); + if (!print_text(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int +print_doa_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 9; + if(rr->rdlength < length) + return 0; + buffer_printf(output, "%" PRIu32 " %" PRIu32 " %" PRIu8 " ", + read_uint32(rr->rdata), read_uint32(rr->rdata+4), + rr->rdata[8]); + if(!print_string(output, rr->rdlength, rr->rdata, &length)) + return 0; + buffer_printf(output, " "); + if(rr->rdlength == length) { + /* The base64 string is empty, and DOA uses '-' for that. */ + buffer_printf(output, "-"); + } else { + if(!print_base64(output, rr->rdlength, rr->rdata, &length)) + return 0; + } + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +read_amtrelay_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + struct dname_buffer relay; + const uint8_t *relay_rdata; + uint8_t relay_length = 0; + const size_t mark = buffer_position(packet); + + /* byte + byte + relay */ + if (buffer_remaining(packet) < rdlength || rdlength < 2) + return MALFORMED; + + buffer_skip(packet, 2); + + switch (buffer_read_u8_at(packet, mark + 1)&AMTRELAY_TYPE_MASK) { + case AMTRELAY_NOGATEWAY: + relay_length = 0; + relay_rdata = NULL; + break; + case AMTRELAY_IP4: + relay_length = 4; + relay_rdata = buffer_current(packet); + if (rdlength < 2 + relay_length) + return MALFORMED; + buffer_skip(packet, relay_length); + break; + case AMTRELAY_IP6: + relay_length = 16; + relay_rdata = buffer_current(packet); + if (rdlength < 2 + relay_length) + return MALFORMED; + buffer_skip(packet, relay_length); + break; + case AMTRELAY_DNAME: + /* The dname is stored as literal dname. On the wire + * skip possibly compressed format, in the rdata there + * is an uncompressed wire format. */ + if(!dname_make_from_packet_buffered(&relay, packet, + 1 /* lenient */, 0)) + return MALFORMED; + if(rdlength < buffer_position(packet) - mark) + return MALFORMED; + relay_length = relay.dname.name_size; + relay_rdata = dname_name((void*)&relay); + break; + default: + return MALFORMED; + } + if(rdlength != buffer_position(packet) - mark) + return MALFORMED; /* trailing bytes */ + + if (!(*rr = region_alloc(domains->region, + sizeof(**rr) + 2 + relay_length))) + return TRUNCATED; + + buffer_read_at(packet, mark, (*rr)->rdata, 2); + if(relay_rdata) + memcpy((*rr)->rdata + 2, relay_rdata, relay_length); + (*rr)->rdlength = 2 + relay_length; + return rdlength; +} + +int +print_amtrelay_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 2; + uint8_t relay_type; + + if(rr->rdlength < length) + return 0; + relay_type = rr->rdata[1]&AMTRELAY_TYPE_MASK; + buffer_printf(output, "%" PRIu8 " %c %" PRIu8, + rr->rdata[0], + (rr->rdata[1] & AMTRELAY_DISCOVERY_OPTIONAL_MASK ? '1' : '0'), + relay_type); + switch(relay_type) { + case AMTRELAY_NOGATEWAY: + buffer_printf(output, " ."); + break; + case AMTRELAY_IP4: + buffer_printf(output, " "); + if(!print_ip4(output, rr->rdlength, rr->rdata, &length)) + return 0; + break; + case AMTRELAY_IP6: + buffer_printf(output, " "); + if(!print_ip6(output, rr->rdlength, rr->rdata, &length)) + return 0; + break; + case AMTRELAY_DNAME: + buffer_printf(output, " "); + if(!print_name_literal(output, rr->rdlength, rr->rdata, + &length)) + return 0; + break; + default: + return 0; + } + + if(rr->rdlength != length) + return 0; + return 1; +} + +int32_t +amtrelay_relay_length(uint16_t rdlength, const uint8_t *rdata, uint16_t offset, + struct domain** domain) +{ + /* The amtrelay relay length depends only on earlier bytes, so both + * the in-memory and uncompressed wireformat refer to the same + * earlier bytes. Also the domain name is stored literally, so it + * does not need to return a reference. */ + uint8_t relay_type; + *domain = NULL; + /* Calculate the relay length, based on the relay type. + * That is stored in an earlier field. */ + if(rdlength < 2 || offset < 2) + return -1; /* too short */ + relay_type = rdata[1]&AMTRELAY_TYPE_MASK; + switch(relay_type) { + case AMTRELAY_NOGATEWAY: + return 0; + case AMTRELAY_IP4: + return 4; + case AMTRELAY_IP6: + return 16; + case AMTRELAY_DNAME: + return buf_dname_length(rdata+offset, rdlength-offset); + default: + /* Unknown relay type. */ + break; + } + return -1; +} + +int +print_ipn_rdata(struct buffer *output, const struct rr *rr) +{ + uint64_t data; + + if(rr->rdlength < sizeof(data)) + return 0; + data = read_uint64(rr->rdata); + buffer_printf(output, "%llu", (unsigned long long) data); + return 1; +} + +int32_t +read_dlv_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr) +{ + /* short + byte + byte + binary */ + if (rdlength < 4) + return MALFORMED; + return read_rdata(domains, rdlength, packet, rr); +} + +int +print_dlv_rdata(struct buffer *output, const struct rr *rr) +{ + uint16_t length = 4; + + if(rr->rdlength < length) + return 0; + buffer_printf( + output, "%" PRIu16 " %" PRIu8 " %" PRIu8 " ", + read_uint16(rr->rdata), rr->rdata[2], rr->rdata[3]); + if (!print_base16(output, rr->rdlength, rr->rdata, &length)) + return 0; + if(rr->rdlength != length) + return 0; + return 1; +} + +int +print_rdata(buffer_type *output, const nsd_type_descriptor_type *descriptor, + const rr_type *rr) +{ + size_t saved_position = buffer_position(output); + /* If print_rdata is going to print "", omit the tab printout. */ + if(!(rr->type == TYPE_APL && rr->rdlength == 0)) + buffer_printf(output, "\t"); + if(!descriptor->print_rdata(output, rr)) { + buffer_set_position(output, saved_position); + return 0; + } + return 1; +} + +/* + * Compare two wireformat byte strings. In canonical order for this comparison. + * Sorts equal as equal, and if there is a prefix match, the shorter one is + * before the longer one; so it sorts like normalized domain names, + * ab ac acd ace ad ae af. And ab < abc , abc < ac . + * @param b1: byte string1. + * @param len1: length of b1. + * @param b2: byte string2. + * @param len2: length of b2 + * @return comparison, -1, 0, 1. + */ +static int +compare_bytestring(const uint8_t* b1, uint16_t len1, const uint8_t* b2, + uint16_t len2) +{ + uint16_t blen; + int res; + + if(len1 == 0 && len2 == 0) + return 0; + if(len1 == 0 && len2 != 0) + return -1; + if(len1 != 0 && len2 == 0) + return 1; + blen = len1; + if(len2 < blen) + blen = len2; + res = memcmp(b1, b2, blen); + if(res != 0) + return res; + if(len1 < len2) + return -1; + if(len1 > len2) + return 1; + /* len1 == len2 and equal. */ + return 0; +} + +int +equal_rr_rdata(const nsd_type_descriptor_type *descriptor, + const struct rr *rr1, const struct rr *rr2) +{ + size_t i; + uint16_t offset = 0; + int res; + + if(!descriptor->has_references) { + /* Compare the wireformat of the rdata. */ + res = compare_bytestring(rr1->rdata, rr1->rdlength, + rr2->rdata, rr2->rdlength); + if(res != 0) + return 0; + return 1; + } + + for(i=0; i < descriptor->rdata.length; i++) { + uint16_t field_len1, field_len2; + struct domain* domain1, *domain2; + int malf1 = 0, malf2 = 0; + /* The fields are equal up to this point. */ + if((rr1->rdlength == offset || rr2->rdlength == offset) && + descriptor->rdata.fields[i].is_optional) { + /* There are no more rdata fields. */ + /* Check lengths. */ + if(rr1->rdlength == rr2->rdlength) + return 1; + else + return 0; + } + if(!lookup_rdata_field_entry(descriptor, i, rr1, offset, + &field_len1, &domain1)) + malf1 = 1; /* malformed rdata buffer */ + if(!lookup_rdata_field_entry(descriptor, i, rr2, offset, + &field_len2, &domain2)) + malf2 = 1; /* malformed rdata buffer */ + if(malf1 || malf2) { + /* Malformed entries sort last, and are sorted + * equal with other malformed entries. */ + if(malf1 && malf2) + return 1; + else + return 0; + } + /* Compare the two fields. */ + /* If they have a different type field, they are not the + * same. */ + if(domain1 && !domain2) + return 0; + if(!domain1 && domain2) + return 0; + if(domain1 && domain2) { + /* Handle RDATA_COMPRESSED_DNAME and + * RDATA_UNCOMPRESSED_DNAME fields. */ + res = dname_compare(domain_dname(domain1), + domain_dname(domain2)); + if(res != 0) + return 0; + } else { + res = compare_bytestring(rr1->rdata + offset, + field_len1, rr2->rdata + offset, field_len2); + if(res != 0) + return 0; + } + /* The fields are equal, field_len1 == field_len2. */ + offset += field_len1; + } + return 1; +} + +int +equal_rr_rdata_uncompressed_wire(const nsd_type_descriptor_type *descriptor, + const struct rr *rr1, const uint8_t* rr2_rdata, uint16_t rr2_rdlen) +{ + size_t i; + uint16_t offset1 = 0, offset2 = 0; + int res; + + if(!descriptor->has_references) { + /* Compare the wireformat of the rdata. */ + res = compare_bytestring(rr1->rdata, rr1->rdlength, + rr2_rdata, rr2_rdlen); + if(res != 0) + return 0; + return 1; + } + + for(i=0; i < descriptor->rdata.length; i++) { + uint16_t field_len1, field_len2; + struct domain* domain1, *domain2; + int malf1 = 0, malf2 = 0; + /* The fields are equal up to this point. */ + if((rr1->rdlength == offset1 || rr2_rdlen == offset2) && + descriptor->rdata.fields[i].is_optional) { + /* There are no more rdata fields. */ + /* Check lengths. */ + int remain1 = rr1->rdlength - offset1; + int remain2 = rr2_rdlen - offset2; + if(remain1 < remain2) + return 0; + if(remain1 > remain2) + return 0; + /* It is equal. */ + return 1; + } + if(!lookup_rdata_field_entry(descriptor, i, rr1, offset1, + &field_len1, &domain1)) + malf1 = 1; /* malformed rdata buffer */ + if(!lookup_rdata_field_entry_uncompressed_wire(descriptor, i, + rr2_rdata, rr2_rdlen, offset2, &field_len2, &domain2)) + malf2 = 1; /* malformed rdata buffer */ + if(malf1 || malf2) { + /* Malformed entries sort last, and are sorted + * equal with other malformed entries. */ + if(!malf1 && malf2) + return 0; + if(malf1 && !malf2) + return 0; + return 1; + } + /* Compare the two fields. */ + /* If they have a different type field, they are not the + * same. */ + if(domain1) { + if(domain2) { + /* Handle RDATA_COMPRESSED_DNAME and + * RDATA_UNCOMPRESSED_DNAME fields. */ + res = dname_compare(domain_dname(domain1), + domain_dname(domain2)); + if(res != 0) + return 0; + } else { + uint16_t dname_len2 = buf_dname_length( + rr2_rdata+offset2, + rr2_rdlen-offset2); + if(domain_dname(domain1)->name_size != + dname_len2) { + /* not the same length dnames. */ + return 0; + } + if(!dname_equal_nocase( + (uint8_t*)dname_name(domain_dname( + domain1)), + (uint8_t*)rr2_rdata+offset2, + dname_len2)) { + /* name comparison not equal. */ + return 0; + } + } + } else { + if(domain2) { + uint16_t dname_len1 = buf_dname_length( + rr1->rdata + offset1, + rr1->rdlength-offset1); + if(dname_len1 != + domain_dname(domain2)->name_size) { + /* not the same length dnames. */ + return 0; + } + if(!dname_equal_nocase( + (uint8_t*)rr1->rdata+offset1, + (uint8_t*)dname_name(domain_dname( + domain2)), + dname_len1)) { + /* name comparison not equal. */ + return 0; + } + } else { + res = compare_bytestring(rr1->rdata + offset1, + field_len1, rr2_rdata + offset2, + field_len2); + if(res != 0) + return 0; + } + } + offset1 += field_len1; + offset2 += field_len2; + } + return 1; +} + +struct domain* +retrieve_rdata_ref_domain_offset(const struct rr* rr, uint16_t offset) +{ + struct domain *domain; + if(rr->rdlength < offset+sizeof(void*)) + return NULL; + memcpy(&domain, rr->rdata+offset, sizeof(void*)); + return domain; +} + +struct domain* +retrieve_rdata_ref_domain(const struct rr* rr) +{ + struct domain *domain; + if(rr->rdlength < sizeof(void*)) + return NULL; + memcpy(&domain, rr->rdata, sizeof(void*)); + return domain; +} + +struct domain* +retrieve_ns_ref_domain(const struct rr* rr) +{ + assert(rr->type == TYPE_NS); + return retrieve_rdata_ref_domain(rr); +} + +struct domain* +retrieve_cname_ref_domain(const struct rr* rr) +{ + assert(rr->type == TYPE_CNAME); + return retrieve_rdata_ref_domain(rr); +} + +struct domain* +retrieve_dname_ref_domain(const struct rr* rr) +{ + assert(rr->type == TYPE_DNAME); + return retrieve_rdata_ref_domain(rr); +} + +struct domain* +retrieve_mb_ref_domain(const struct rr* rr) +{ + assert(rr->type == TYPE_MB); + return retrieve_rdata_ref_domain(rr); +} + +struct domain* +retrieve_mx_ref_domain(const struct rr* rr) +{ + assert(rr->type == TYPE_MX); + return retrieve_rdata_ref_domain_offset(rr, 2); +} + +struct domain* +retrieve_kx_ref_domain(const struct rr* rr) +{ + assert(rr->type == TYPE_KX); + return retrieve_rdata_ref_domain_offset(rr, 2); +} + +struct domain* +retrieve_rt_ref_domain(const struct rr* rr) +{ + assert(rr->type == TYPE_RT); + return retrieve_rdata_ref_domain_offset(rr, 2); +} + +struct domain* +retrieve_srv_ref_domain(const struct rr* rr) +{ + assert(rr->type == TYPE_SRV); + return retrieve_rdata_ref_domain_offset(rr, 6); +} + +struct domain* +retrieve_ptr_ref_domain(const struct rr* rr) +{ + assert(rr->type == TYPE_PTR); + return retrieve_rdata_ref_domain(rr); +} + +int +retrieve_soa_rdata_serial(const struct rr* rr, uint32_t* serial) +{ + assert(rr->type == TYPE_SOA); + if(rr->rdlength < 20 + 2*sizeof(void*)) + return 0; + /* primary mail serial[4] refresh[4] retry[4] expire[4] minimum[4] */ + *serial = read_uint32(rr->rdata+2*sizeof(void*)); + return 1; +} + +int +retrieve_soa_rdata_minttl(const struct rr* rr, uint32_t* minttl) +{ + assert(rr->type == TYPE_SOA); + if(rr->rdlength < 20 + 2*sizeof(void*)) + return 0; + /* primary mail serial[4] refresh[4] retry[4] expire[4] minimum[4] */ + *minttl = read_uint32(rr->rdata+2*sizeof(void*)+16); + return 1; +} + +struct dname* retrieve_cname_ref_dname(const struct rr* rr) +{ + struct domain* domain; + assert(rr->type == TYPE_CNAME); + domain = retrieve_rdata_ref_domain(rr); + if(!domain) + return NULL; + return domain_dname(domain); +} + +void +rr_lower_usage(namedb_type* db, rr_type* rr) +{ + const nsd_type_descriptor_type *descriptor = + nsd_type_descriptor(rr->type); + uint16_t offset = 0; + size_t i; + for(i=0; irdata.length; i++) { + uint16_t field_len; + struct domain* domain; + if(rr->rdlength == offset && + descriptor->rdata.fields[i].is_optional) + break; + if(!lookup_rdata_field_entry(descriptor, i, rr, offset, + &field_len, &domain)) + break; + if(domain) { + assert(domain->usage > 0); + domain->usage --; + if(domain->usage == 0) + domain_table_deldomain(db, + domain); + } + offset += field_len; + } +} + +const char* +read_rdata_fail_str(int32_t code) +{ + switch(code) { + case TRUNCATED: + return "out of memory"; + case MALFORMED: + return "malformed rdata fields"; + default: + break; + } + return "failed to read rdata fields"; +} Index: usr.sbin/nsd/rdata.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/rdata.h,v diff -u -p -r1.3 rdata.h --- usr.sbin/nsd/rdata.h 29 Jun 2023 19:38:50 -0000 1.3 +++ usr.sbin/nsd/rdata.h 21 Sep 2026 16:29:08 -0000 @@ -17,45 +17,682 @@ #define APL_NEGATION_MASK 0x80U #define APL_LENGTH_MASK (~APL_NEGATION_MASK) +/* High bit of the AMTRELAY Type byte in rdata[1] is the Discovery Optional + * flag, the D-bit. */ +#define AMTRELAY_DISCOVERY_OPTIONAL_MASK 0x80U +#define AMTRELAY_TYPE_MASK 0x7fU + extern lookup_table_type dns_certificate_types[]; extern lookup_table_type dns_algorithms[]; -extern const char *svcparamkey_strs[]; - -int rdata_atom_to_string(buffer_type *output, rdata_zoneformat_type type, - rdata_atom_type rdata, rr_type *rr); /* - * Split the wireformat RDATA into an array of rdata atoms. Domain - * names are inserted into the OWNERS table. The number of rdata atoms - * is returned and the array itself is allocated in REGION and stored - * in RDATAS. - * - * Returns -1 on failure. + * Function signature for svcparam print. Input offset is at key uint16_t + * in rdata. + * @param output: the string is printed to the buffer. + * @param svcparamkey: the key that is printed. + * @param data: the data for the svcparam, from rdata. + * @param datalen: length of data in bytes. + * @return false on failure. */ -ssize_t rdata_wireformat_to_rdata_atoms(region_type *region, - domain_table_type *owners, - uint16_t rrtype, - uint16_t rdata_size, - buffer_type *packet, - rdata_atom_type **rdatas); - -/* - * Calculate the maximum size of the rdata assuming domain names are - * not compressed. - */ -size_t rdata_maximum_wireformat_size(rrtype_descriptor_type *descriptor, - size_t rdata_count, - rdata_atom_type *rdatas); - -int rdata_atoms_to_unknown_string(buffer_type *out, - rrtype_descriptor_type *descriptor, - size_t rdata_count, - rdata_atom_type *rdatas); +typedef int(*nsd_print_svcparam_rdata_type)( + struct buffer* output, + uint16_t svcparamkey, + const uint8_t* data, + uint16_t datalen); + +typedef struct nsd_svcparam_descriptor nsd_svcparam_descriptor_type; + +/* Descriptor for svcparam rdata fields. With type, name and print func. */ +struct nsd_svcparam_descriptor { + /* The svc param key */ + uint16_t key; + /* The name of the key */ + const char *name; + /* Print function that prints the key, from rdata. */ + nsd_print_svcparam_rdata_type print_rdata; +}; + +int print_unknown_rdata_field(buffer_type *output, + const nsd_type_descriptor_type *descriptor, const rr_type *rr); +int print_unknown_rdata(buffer_type *output, + const nsd_type_descriptor_type *descriptor, const rr_type *rr); /* print rdata to a text string (as for a zone file) returns 0 on a failure (bufpos is reset to original position). returns 1 on success, bufpos is moved. */ -int print_rdata(buffer_type *output, rrtype_descriptor_type *descriptor, - rr_type *record); +int print_rdata(buffer_type *output, const nsd_type_descriptor_type *descriptor, + const rr_type *rr); + +/* Read rdata for an unknown RR type. */ +int32_t read_generic_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for an unknown RR type. */ +void write_generic_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for an unknown RR type. */ +int print_generic_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for an RR type with one compressed dname. */ +int32_t read_compressed_name_rdata(struct domain_table *domains, + uint16_t rdlength, struct buffer *packet, struct rr **rr); + +/* Write rdata for an RR type with one compressed dname. */ +void write_compressed_name_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for an RR type with one compressed or uncompressed dname. + * But not a dname type literal. */ +int print_name_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for an RR type with one uncompressed dname. */ +int32_t read_uncompressed_name_rdata(struct domain_table *domains, + uint16_t rdlength, struct buffer *packet, struct rr **rr); + +/* Write rdata for an RR type with one uncompressed dname. */ +void write_uncompressed_name_rdata(struct query *query, const struct rr *rr); + +/* Read rdata for type A. */ +int32_t read_a_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type A. */ +int print_a_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type SOA. */ +int32_t read_soa_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type SOA. */ +void write_soa_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type SOA. */ +int print_soa_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type SOA, on two lines, with parentheses. */ +int print_soa_rdata_twoline(struct buffer *output, const struct rr *rr); + +/* Read rdata for type WKS. */ +int32_t read_wks_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type WKS. */ +int print_wks_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type HINFO. */ +int32_t read_hinfo_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type HINFO. */ +int print_hinfo_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type MINFO. */ +int32_t read_minfo_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type MINFO. */ +void write_minfo_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type MINFO. */ +int print_minfo_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type MX. */ +int32_t read_mx_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type MX. */ +void write_mx_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type MX. */ +int print_mx_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type TXT. */ +int32_t read_txt_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type TXT. */ +int print_txt_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type RP. */ +int32_t read_rp_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type RP. */ +void write_rp_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type RP. */ +int print_rp_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type AFSDB. */ +int32_t read_afsdb_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type AFSDB. */ +void write_afsdb_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type AFSDB. */ +int print_afsdb_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type X25. */ +int32_t read_x25_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type X25. */ +int print_x25_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type ISDN. */ +int32_t read_isdn_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type ISDN. */ +int print_isdn_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type RT. */ +int32_t read_rt_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type RT. */ +void write_rt_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type NSAP. */ +int print_nsap_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type NSAP-PTR. */ +int print_nsap_ptr_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type KEY. */ +int print_key_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type PX. */ +int32_t read_px_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type PX. */ +void write_px_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type PX. */ +int print_px_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type GPOS. */ +int print_gpos_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type AAAA. */ +int32_t read_aaaa_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type AAAA. */ +int print_aaaa_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type LOC. */ +int32_t read_loc_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type LOC. */ +int print_loc_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type NXT. */ +int32_t read_nxt_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type NXT. */ +void write_nxt_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type NXT. */ +int print_nxt_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type EID. */ +int print_eid_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type NIMLOC. */ +int print_nimloc_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type SRV. */ +int32_t read_srv_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type SRV. */ +void write_srv_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type SRV. */ +int print_srv_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type ATMA. */ +int print_atma_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type NAPTR. */ +int32_t read_naptr_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type NAPTR. */ +void write_naptr_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type NAPTR. */ +int print_naptr_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type KX. */ +int32_t read_kx_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type KX. */ +void write_kx_rdata(struct query *query, const struct rr *rr); + +/* Read rdata for type CERT. */ +int32_t read_cert_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type CERT. */ +int print_cert_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type SINK. */ +int print_sink_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type APL. */ +int32_t read_apl_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type APL. */ +int print_apl_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type DS. */ +int32_t read_ds_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type DS. */ +int print_ds_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type SSHFP. */ +int32_t read_sshfp_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type SSHFP. */ +int print_sshfp_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type IPSECKEY. */ +int32_t read_ipseckey_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type IPSECKEY. */ +int print_ipseckey_rdata(struct buffer *output, const struct rr *rr); + +/* Determine length of IPSECKEY gateway field. */ +int32_t ipseckey_gateway_length(uint16_t rdlength, const uint8_t *rdata, + uint16_t offset, struct domain** domain); + +/* Read rdata for type RRSIG. */ +int32_t read_rrsig_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type RRSIG. */ +int print_rrsig_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type NSEC. */ +int32_t read_nsec_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type NSEC. */ +int print_nsec_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type DNSKEY. */ +int32_t read_dnskey_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type DNSKEY. */ +int print_dnskey_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type DHCID. */ +int32_t read_dhcid_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type DHCID. */ +int print_dhcid_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type NSEC3. */ +int32_t read_nsec3_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type NSEC3. */ +int print_nsec3_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type NSEC3PARAM. */ +int32_t read_nsec3param_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type NSEC3PARAM. */ +int print_nsec3param_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type TLSA. */ +int32_t read_tlsa_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type TLSA. */ +int print_tlsa_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type HIP. */ +int32_t read_hip_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type HIP. */ +int print_hip_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type RKEY. */ +int32_t read_rkey_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type RKEY. */ +int print_rkey_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type TALINK. */ +int32_t read_talink_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type TALINK. */ +int print_talink_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type OPENPGPKEY. */ +int print_openpgpkey_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type CSYNC. */ +int32_t read_csync_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type CSYNC. */ +int print_csync_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type ZONEMD. */ +int32_t read_zonemd_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type ZONEMD. */ +int print_zonemd_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type SVCB. */ +int32_t read_svcb_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type SVCB. */ +void write_svcb_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type SVCB. */ +int print_svcb_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type DSYNC. */ +int32_t read_dsync_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type DSYNC. */ +int print_dsync_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type HHIT. */ +int32_t read_hhit_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type HHIT. */ +int print_hhit_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type BRID. */ +int32_t read_brid_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type BRID. */ +int print_brid_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type NID. */ +int32_t read_nid_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type NID. */ +int print_nid_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type L32. */ +int32_t read_l32_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type L32. */ +int print_l32_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type L64. */ +int32_t read_l64_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type L64. */ +int print_l64_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type LP. */ +int32_t read_lp_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Write rdata for type LP. */ +void write_lp_rdata(struct query *query, const struct rr *rr); + +/* Print rdata for type LP. */ +int print_lp_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type EUI48. */ +int32_t read_eui48_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type EUI48. */ +int print_eui48_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type EUI64. */ +int32_t read_eui64_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type EUI64. */ +int print_eui64_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type URI. */ +int32_t read_uri_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type URI. */ +int print_uri_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type resinfo. */ +int print_resinfo_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type CAA. */ +int32_t read_caa_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type CAA. */ +int print_caa_rdata(struct buffer *output, const struct rr *rr); + +/* Print rdata for type DOA. */ +int print_doa_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type AMTRELAY. */ +int32_t read_amtrelay_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type AMTRELAY. */ +int print_amtrelay_rdata(struct buffer *output, const struct rr *rr); + +/* Determine length of AMTRELAY relay field. */ +int32_t amtrelay_relay_length(uint16_t rdlength, const uint8_t *rdata, + uint16_t offset, struct domain** domain); + +/* Print rdata for type IPN. */ +int print_ipn_rdata(struct buffer *output, const struct rr *rr); + +/* Read rdata for type DLV. */ +int32_t read_dlv_rdata(struct domain_table *domains, uint16_t rdlength, + struct buffer *packet, struct rr **rr); + +/* Print rdata for type DLV. */ +int print_dlv_rdata(struct buffer *output, const struct rr *rr); + +/* + * Look up the uncompressed wireformat length of the rdata. + * The pointer references in it are taking up the length of their uncompressed + * domain names. The length is without the RR's rdatalength uint16 preceding. + * @param rr: the rr, the RR type and rdata are used. + * @result -1 on failure, otherwise length in bytes. + */ +int32_t rr_calculate_uncompressed_rdata_length(const rr_type* rr); + +/* + * Write uncompressed wireformat rdata to buffer. The pointer references + * and domains are uncompressed wireformat domain names. The uint16 rdlength + * is not written before it. + * @param rr: the rr, with RR type and rdata. + * @param buf: destination. + * @param len: length of buffer. + */ +void rr_write_uncompressed_rdata(const rr_type* rr, uint8_t* buf, size_t len); + +/* + * Look up the field length. The field length is returned as a length + * in the rdata that is stored. For a reference, the pointer is returned too. + * Before calling it check if the field is_optional, and rdlength is + * reached by offset, then there are no more rdata fields. + * Also if the index has reached the rdata field length count, fields end. + * It checks if the field fits in the rdata buffer, failure if not. + * Then check for domain ptr or not, and handle the field at rr->rdata+offset. + * Continue the loop by incrementing offset with field_len, and index++. + * + * @param descriptor: type descriptor. + * @param index: field index. + * @param rr: the rr with the rdata. + * @param offset: current position in the rdata. + * It is not updated, because the caller has to do that. + * @param field_len: the field length is returned. + * @param domain: the pointer is returned when the field is a reference. + * @return false on failure, when the rdata stored is badly formatted, like + * the rdata buffer is too short. + */ +int lookup_rdata_field_entry(const nsd_type_descriptor_type* descriptor, + size_t index, const rr_type* rr, uint16_t offset, uint16_t* field_len, + struct domain** domain); + +/* Look up the field length. Same as lookup_rdata_field_entry, but the rdata + * is uncompressed wireformat. The length returned skips the field in the + * uncompressed wireformat. */ +int lookup_rdata_field_entry_uncompressed_wire( + const nsd_type_descriptor_type* descriptor, size_t index, + const uint8_t* rdata, uint16_t rdlength, uint16_t offset, + uint16_t* field_len, struct domain** domain); + +/* + * Compare rdata for equality. This is easier than the sorted compare, + * it treats field types as a difference too, so a reference instead of + * a wireformat field makes for a different RR. + * The RRs have to be the same type already. + * It iterates over the RR type fields. The RRs and the rdatas are the + * namedb format, that is with references stored as pointers. + * @param rr1: RR to compare rdata 1. The rdata can contain pointers. + * @param rr2: RR to compare rdata 2. The rdata can contain pointers. + * @return true if rdata is equal. + */ +int equal_rr_rdata(const nsd_type_descriptor_type *descriptor, + const struct rr *rr1, const struct rr *rr2); + +/* + * Compare rdata for equality. Same as equal_rr_rdata, but the second + * rdata is passed as uncompressed wireformat, the first has the in-memory + * rdata format. + */ +int equal_rr_rdata_uncompressed_wire(const nsd_type_descriptor_type *descriptor, + const struct rr *rr1, const uint8_t* rr2_rdata, uint16_t rr2_rdlen); + +/* + * Retrieve domain ref at an offset in the rdata. + * @param rr: the RR to retrieve it for. + * @param offset: where in the rdata the reference pointer is. + * @return domain ptr. + */ +struct domain* retrieve_rdata_ref_domain_offset(const struct rr* rr, + uint16_t offset); + +/* + * Retrieve domain ref from rdata. No offset, rdata starts with ref. + * @param rr: the RR to retrieve it for. + * @return domain ptr. + */ +struct domain* retrieve_rdata_ref_domain(const struct rr* rr); + +/* + * Accessor function to the domain in the rdata of the type. + * The RR must be of the type. The type must have references. + * @param rr: the rr with rdata + * @return domain pointer. + */ +typedef struct domain*(*nsd_rdata_ref_domain_type)( + const struct rr* rr); + +/* Access the domain reference for type NS */ +struct domain* retrieve_ns_ref_domain(const struct rr* rr); + +/* Access the domain reference for type CNAME */ +struct domain* retrieve_cname_ref_domain(const struct rr* rr); + +/* Access the domain reference for type DNAME */ +struct domain* retrieve_dname_ref_domain(const struct rr* rr); + +/* Access the domain reference for type MB */ +struct domain* retrieve_mb_ref_domain(const struct rr* rr); + +/* Access the domain reference for type MX */ +struct domain* retrieve_mx_ref_domain(const struct rr* rr); + +/* Access the domain reference for type KX */ +struct domain* retrieve_kx_ref_domain(const struct rr* rr); + +/* Access the domain reference for type RT */ +struct domain* retrieve_rt_ref_domain(const struct rr* rr); + +/* Access the domain reference for type SRV */ +struct domain* retrieve_srv_ref_domain(const struct rr* rr); + +/* Access the domain reference for type PTR */ +struct domain* retrieve_ptr_ref_domain(const struct rr* rr); + +/* Access the serial number for type SOA, false if malformed. */ +int retrieve_soa_rdata_serial(const struct rr* rr, uint32_t* serial); + +/* Access the minimum ttl for type SOA, false if malformed. */ +int retrieve_soa_rdata_minttl(const struct rr* rr, uint32_t* minttl); + +/* Access the dname reference for type CNAME */ +struct dname* retrieve_cname_ref_dname(const struct rr* rr); + +/* + * Access the domain name reference, that is stored as COMPRESSED_DNAME, + * or UNCOMPRESSED DNAME, at the start of the rdata, or only part of the rdata. + * Not for literal DNAMEs. This is similar to a pointer reference, + * but it may be stored unaligned. + * @param rr: the resource record. + * @return domain pointer. + */ +static inline struct domain* rdata_domain_ref(const struct rr* rr) { + struct domain* domain; + assert(rr->rdlength >= (uint16_t)sizeof(void*)); + memcpy(&domain, rr->rdata, sizeof(void*)); + return domain; +} + +/* + * Access the domain name reference, that is stored as COMPRESSED_DNAME, + * or UNCOMPRESSED DNAME, the reference is at an offset in the rdata. + * Not for literal DNAMEs. This is similar to a pointer reference, + * but it may be stored unaligned. + * @param rr: the resource record + * @param offset: where the reference is found in the rdata. Pass like 2, + * for type MX, or sizeof(void*) to access the second domain pointer + * of SOA. + * @return domain pointer. + */ +static inline struct domain* rdata_domain_ref_offset(const struct rr* rr, + uint16_t offset) { + struct domain* domain; + assert(rr->rdlength >= offset+(uint16_t)sizeof(void*)); + memcpy(&domain, rr->rdata+offset, sizeof(void*)); + return domain; +} + +/* fixup usage lower for domain names in the rdata */ +void rr_lower_usage(namedb_type* db, rr_type* rr); + +/* return error string for read_rdata return code that is < 0 */ +const char* read_rdata_fail_str(int32_t code); #endif /* RDATA_H */ Index: usr.sbin/nsd/remote.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/remote.c,v diff -u -p -r1.29 remote.c --- usr.sbin/nsd/remote.c 6 Sep 2025 17:41:37 -0000 1.29 +++ usr.sbin/nsd/remote.c 21 Sep 2026 16:29:08 -0000 @@ -81,6 +81,7 @@ #include "difffile.h" #include "ipc.h" #include "remote.h" +#include "rdata.h" #ifdef USE_METRICS #include "metrics.h" @@ -981,6 +982,10 @@ force_transfer_zone(xfrd_zone_type* zone xfrd_tcp_release(xfrd->tcp_set, zone); else if(zone->zone_handler.ev_fd != -1) xfrd_udp_release(zone); + if(zone->udp_waiting) + udp_zone_waiting_list_remove(zone); + if(zone->tcp_waiting) + tcp_zone_waiting_list_remove(zone); /* pretend we not longer have it and force any * zone to be downloaded (even same serial, w AXFR) */ zone->soa_disk_acquired = 0; @@ -1042,8 +1047,11 @@ print_zonestatus(RES* ssl, xfrd_state_ty return 0; } if(zone_is_catalog_consumer(zo)) { - zone_type* zone = namedb_find_zone(xfrd->nsd->db, - (const dname_type*)zo->node.key); + uint32_t serial = 0; + zone_type* zone = xfrd->nsd->db + ? namedb_find_zone(xfrd->nsd->db, + (const dname_type*)zo->node.key) + : NULL; struct xfrd_catalog_consumer_zone* consumer_zone = (struct xfrd_catalog_consumer_zone*) rbtree_search( xfrd->catalog_consumer_zones @@ -1051,13 +1059,14 @@ print_zonestatus(RES* ssl, xfrd_state_ty if(!ssl_printf(ssl, " catalog: consumer")) return 0; - if(zone && zone->soa_rrset && zone->soa_rrset->rrs - && zone->soa_rrset->rrs[0].rdata_count > 2 - && rdata_atom_size(zone->soa_rrset->rrs[0].rdatas[2]) == - sizeof(uint32_t)) { + if(zone && zone->soa_rrset +#ifndef PACKED_STRUCTS + && zone->soa_rrset->rrs +#endif + && retrieve_soa_rdata_serial(zone->soa_rrset->rrs[0], + &serial)) { if(!ssl_printf(ssl, " (serial: %u, # members: %zu)\n", - read_uint32(rdata_atom_data( - zone->soa_rrset->rrs[0].rdatas[2])), + serial, consumer_zone ? consumer_zone->member_ids.count : 0)) return 0; @@ -1881,6 +1890,18 @@ repat_interrupt_zones(xfrd_state_type* x xfrd_udp_release(xz); xfrd_set_refresh_now(xz); } + if(xz->udp_waiting) { + log_msg(LOG_INFO, "udp_waiting for %s", + xz->apex_str); + udp_zone_waiting_list_remove(xz); + xfrd_set_refresh_now(xz); + } + if(xz->tcp_waiting) { + log_msg(LOG_INFO, "tcp_waiting for %s", + xz->apex_str); + tcp_zone_waiting_list_remove(xz); + xfrd_set_refresh_now(xz); + } xz->master = 0; xz->master_num = 0; xz->next_master = -1; @@ -2404,6 +2425,7 @@ do_assoc_tsig(RES* ssl, xfrd_state_type* char* arg2 = NULL; struct zone_options* zone; struct key_options* key_opt; + struct xfrd_zone* xzone; if(*arg == '\0') { (void)ssl_printf(ssl, "error: missing argument (zonename)\n"); @@ -2426,6 +2448,23 @@ do_assoc_tsig(RES* ssl, xfrd_state_type* return; } + /* Abort any in-flight transfer for this zone before mutating its ACL + * so that xfr->tsig stays consistent with zone->master->key_options. */ + xzone = xfrd_find_zone(xfrd, (dname_type*)zone->node.key); + if(xzone && xzone->tcp_conn != -1) { + xfrd_tcp_release(xfrd->tcp_set, xzone); + /* probe the current target again, if possible. */ + if(xzone->next_master == -1) + xzone->next_master = xzone->master_num; + xfrd_set_refresh_now(xzone); + } else if(xzone && xzone->zone_handler.ev_fd != -1) { + /* Also UDP can use the TSIG for signature. */ + xfrd_udp_release(xzone); + if(xzone->next_master == -1) + xzone->next_master = xzone->master_num; + xfrd_set_refresh_now(xzone); + } + zopt_set_acl_to_tsig(zone->pattern->allow_notify, region, arg2, key_opt); zopt_set_acl_to_tsig(zone->pattern->notify, region, arg2, key_opt); @@ -2473,6 +2512,7 @@ do_del_tsig(RES* ssl, xfrd_state_type* x } RBTREE_FOR(zone, struct zone_options*, xfrd->nsd->options->zone_options) { + xfrd_zone_type* xzone; if(acl_contains_tsig_key(zone->pattern->allow_notify, arg) || acl_contains_tsig_key(zone->pattern->notify, arg) || acl_contains_tsig_key(zone->pattern->request_xfr, arg) || @@ -2484,6 +2524,15 @@ do_del_tsig(RES* ssl, xfrd_state_type* x used_key = 1; break; } + xzone = xfrd_find_zone(xfrd, (dname_type*)zone->node.key); + if(xzone && xzone->latest_xfr && + xzone->latest_xfr->tsig.key == key_opt->tsig_key) { + if(!ssl_printf(ssl, "zone %s uses key %s\n", + zone->name, arg)) + return; + used_key = 1; + break; + } } if(used_key) { @@ -2835,8 +2884,17 @@ handle_req(struct daemon_remote* rc, str (void)ssl_printf(res, "error version mismatch\n"); return; } - /* always log control commands */ - VERBOSITY(0, (LOG_INFO, "control cmd: %s", buf)); + /* always log control command 'verbosity', others at 2, so + * they can be squelched if needed. */ + if(verbosity < 2) { + /* only the 'verbosity' command. */ + if(strncmp(skipwhite(buf), "verbosity", 9) == 0) { + VERBOSITY(0, (LOG_INFO, "control cmd: %s", buf)); + } + } else { + /* always log every control command. */ + VERBOSITY(2, (LOG_INFO, "control cmd: %s", buf)); + } /* figure out what to do */ execute_cmd(rc, res, buf); @@ -3423,7 +3481,7 @@ create_local_accept_sock(const char *pat goto err; } - if (listen(s, TCP_BACKLOG) == -1) { + if (listen(s, nsd.options->tcp_listen_queue) == -1) { log_msg(LOG_ERR, "can't listen: %s", strerror(errno)); goto err; } Index: usr.sbin/nsd/server.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/server.c,v diff -u -p -r1.55 server.c --- usr.sbin/nsd/server.c 6 Sep 2025 17:47:20 -0000 1.55 +++ usr.sbin/nsd/server.c 21 Sep 2026 16:29:08 -0000 @@ -158,6 +158,8 @@ struct udp_handler_data struct event event; /* if set, PROXYv2 is expected on this connection */ int pp2_enabled; + /* if set, padding is allowed on this connection */ + int may_pad; }; struct tcp_accept_handler_data { @@ -895,10 +897,14 @@ set_rcvbuf(struct nsd_socket *sock, int return 1; } if(errno == EPERM || errno == ENOBUFS) { + if(errno == ENOBUFS) { + VERBOSITY(2, (LOG_INFO, "setsockopt(..., SO_RCVBUFFORCE, %d) was not granted: %s", + rcv, strerror(errno))); + } return 0; } - log_msg(LOG_ERR, "setsockopt(..., SO_RCVBUFFORCE, ...) failed: %s", - strerror(errno)); + log_msg(LOG_ERR, "setsockopt(..., SO_RCVBUFFORCE, %d) failed: %s", + rcv, strerror(errno)); return -1; #else /* !SO_RCVBUFFORCE */ if (0 == setsockopt( @@ -907,10 +913,14 @@ set_rcvbuf(struct nsd_socket *sock, int return 1; } if(errno == ENOSYS || errno == ENOBUFS) { + if(errno == ENOBUFS) { + VERBOSITY(2, (LOG_INFO, "setsockopt(..., SO_RCVBUF, %d) was not granted: %s", + rcv, strerror(errno))); + } return 0; } - log_msg(LOG_ERR, "setsockopt(..., SO_RCVBUF, ...) failed: %s", - strerror(errno)); + log_msg(LOG_ERR, "setsockopt(..., SO_RCVBUF, %d) failed: %s", + rcv, strerror(errno)); return -1; #endif /* SO_RCVBUFFORCE */ #endif /* SO_RCVBUF */ @@ -929,10 +939,14 @@ set_sndbuf(struct nsd_socket *sock, int return 1; } if(errno == EPERM || errno == ENOBUFS) { + if(errno == ENOBUFS) { + VERBOSITY(2, (LOG_INFO, "setsockopt(..., SO_SNDBUFFORCE, %d) was not granted: %s", + snd, strerror(errno))); + } return 0; } - log_msg(LOG_ERR, "setsockopt(..., SO_SNDBUFFORCE, ...) failed: %s", - strerror(errno)); + log_msg(LOG_ERR, "setsockopt(..., SO_SNDBUFFORCE, %d) failed: %s", + snd, strerror(errno)); return -1; #else /* !SO_SNDBUFFORCE */ if(0 == setsockopt( @@ -941,10 +955,14 @@ set_sndbuf(struct nsd_socket *sock, int return 1; } if(errno == ENOSYS || errno == ENOBUFS) { + if(errno == ENOBUFS) { + VERBOSITY(2, (LOG_INFO, "setsockopt(..., SO_SNDBUF, %d) was not granted: %s", + snd, strerror(errno))); + } return 0; } - log_msg(LOG_ERR, "setsockopt(..., SO_SNDBUF, ...) failed: %s", - strerror(errno)); + log_msg(LOG_ERR, "setsockopt(..., SO_SNDBUF, %d) failed: %s", + snd, strerror(errno)); return -1; #endif /* SO_SNDBUFFORCE */ #endif /* SO_SNDBUF */ @@ -1277,7 +1295,8 @@ set_setfib(struct nsd_socket *sock) static int open_udp_socket(struct nsd *nsd, struct nsd_socket *sock, int *reuseport_works) { - int rcv = 1*1024*1024, snd = 4*1024*1024; + int rcv = nsd->options->receive_buffer_size; + int snd = nsd->options->send_buffer_size; if(-1 == (sock->s = socket( sock->addr.ai_family, sock->addr.ai_socktype, 0))) @@ -1301,13 +1320,9 @@ open_udp_socket(struct nsd *nsd, struct if(nsd->reuseport && reuseport_works && *reuseport_works) *reuseport_works = (set_reuseport(sock) == 1); - if(nsd->options->receive_buffer_size > 0) - rcv = nsd->options->receive_buffer_size; if(set_rcvbuf(sock, rcv) == -1) return -1; - if(nsd->options->send_buffer_size > 0) - snd = nsd->options->send_buffer_size; if(set_sndbuf(sock, snd) == -1) return -1; #ifdef INET6 @@ -1415,7 +1430,7 @@ open_tcp_socket(struct nsd *nsd, struct (void)set_tcp_fastopen(sock); #endif - if(listen(sock->s, TCP_BACKLOG) == -1) { + if(listen(sock->s, nsd->options->tcp_listen_queue) == -1) { log_msg(LOG_ERR, "can't listen: %s", strerror(errno)); return -1; } @@ -3208,7 +3223,9 @@ server_main(struct nsd *nsd) } #ifdef USE_XDP - xdp_server_cleanup(&nsd->xdp.xdp_server); + if (nsd->options->xdp_interface) { + xdp_server_cleanup(&nsd->xdp.xdp_server); + } #endif #ifdef MEMCLEAN /* OS collects memory pages */ @@ -3322,11 +3339,14 @@ add_udp_handler( data->nsd = nsd; data->socket = sock; - if(nsd->options->proxy_protocol_port && - sockaddr_uses_proxy_protocol_port(nsd->options, - (struct sockaddr *)&sock->addr.ai_addr)) { + if(sockaddr_uses_port((struct sockaddr *)&sock->addr.ai_addr, + nsd->options->proxy_protocol_port)) { data->pp2_enabled = 1; } + if(sockaddr_uses_port((struct sockaddr *)&sock->addr.ai_addr, + nsd->options->udp_padding_port)) { + data->may_pad = 1; + } memset(handler, 0, sizeof(*handler)); event_set(handler, sock->s, EV_PERSIST|EV_READ, handle_udp, data); @@ -3347,9 +3367,8 @@ add_tcp_handler( data->nsd = nsd; data->socket = sock; - if(nsd->options->proxy_protocol_port && - sockaddr_uses_proxy_protocol_port(nsd->options, - (struct sockaddr *)&sock->addr.ai_addr)) { + if(sockaddr_uses_port((struct sockaddr *)&sock->addr.ai_addr, + nsd->options->proxy_protocol_port)) { data->pp2_enabled = 1; } @@ -3398,13 +3417,14 @@ add_xdp_handler(struct nsd *nsd, struct xdp_server *xdp, struct xdp_handler_data *data) { + int sock; struct event *handler = &data->event; data->nsd = nsd; data->server = xdp; memset(handler, 0, sizeof(*handler)); - int sock = xsk_socket__fd(xdp->xsks[xdp->queue_index].xsk); + sock = xsk_socket__fd(xdp->xsks[xdp->queue_index].xsk); if (sock < 0) { log_msg(LOG_ERR, "xdp: xsk socket file descriptor is invalid: %s", strerror(errno)); @@ -3704,13 +3724,18 @@ server_child(struct nsd *nsd) #ifdef USE_XDP if (nsd->options->xdp_interface) { /* don't try to bind more sockets than there are queues available */ - if (nsd->xdp.xdp_server.queue_count <= nsd->this_child->child_num) { + if ((int)nsd->xdp.xdp_server.queue_count <= nsd->this_child->child_num) { log_msg(LOG_WARNING, "xdp: server-count exceeds available queues (%d) on " "interface %s, skipping xdp in this process", nsd->xdp.xdp_server.queue_count, nsd->xdp.xdp_server.interface_name); } else { + struct xdp_handler_data *data; + const int scratch_data_len = 1; + void *scratch_data = region_alloc_zero(nsd->server_region, + scratch_data_len); + nsd->xdp.xdp_server.queue_index = nsd->this_child->child_num; nsd->xdp.xdp_server.queries = xdp_queries; @@ -3719,13 +3744,9 @@ server_child(struct nsd *nsd) nsd->xdp.xdp_server.queue_index, nsd->xdp.xdp_server.interface_name); - struct xdp_handler_data *data; data = region_alloc_zero(nsd->server_region, sizeof(*data)); add_xdp_handler(nsd, &nsd->xdp.xdp_server, data); - const int scratch_data_len = 1; - void *scratch_data = region_alloc_zero(nsd->server_region, - scratch_data_len); for (i = 0; i < XDP_RX_BATCH_SIZE; i++) { /* Be aware that the buffer is initialized with scratch data * and will be filled by the xdp handle and receive function @@ -4043,6 +4064,35 @@ port_is_zero( #endif } +/* Check if proxy is allowed */ +static int +pp2_is_allowed(struct query* q) +{ + if(nsd.options->allow_proxy) { + struct acl_options* why = NULL; + if(acl_check_incoming_proxy(nsd.options->allow_proxy, q, + &why) == -1) { + if(verbosity >= 2) { + char proxy[128]; + addr2str(&q->remote_addr, proxy, sizeof(proxy)); + VERBOSITY(2, (LOG_INFO, "proxy-protocol: %s is not " + "in allow-proxy list", proxy)); + } + return 0; + } +#ifndef NDEBUG + /* It was allowed from acl 'why'. */ + if(why) { + char proxy[128]; + addr2str(&q->remote_addr, proxy, sizeof(proxy)); + DEBUG(DEBUG_QUERY,1, (LOG_INFO, "proxy %s passed acl %s", + proxy, why->ip_address_spec)); + } +#endif /* NDEBUG */ + } + return 1; +} + /* Parses the PROXYv2 header from buf and updates the struct. * Returns 1 on success, 0 on failure. */ static int @@ -4081,6 +4131,10 @@ consume_pp2_header(struct buffer* buf, s { struct sockaddr_in* addr = (struct sockaddr_in*)&q->client_addr; + if(ntohs(header->len) < PP2_HEADER_LEN_INET) { + VERBOSITY(4, (LOG_ERR, "proxy_protocol: header too short for IPv4 address")); + return 0; + } addr->sin_family = AF_INET; memmove(&addr->sin_addr.s_addr, &header->addr.addr4.src_addr, 4); @@ -4096,6 +4150,10 @@ consume_pp2_header(struct buffer* buf, s { struct sockaddr_in6* addr = (struct sockaddr_in6*)&q->client_addr; + if(ntohs(header->len) < PP2_HEADER_LEN_INET6) { + VERBOSITY(4, (LOG_ERR, "proxy_protocol: header too short for IPv6 address")); + return 0; + } memset(addr, 0, sizeof(*addr)); addr->sin6_family = AF_INET6; memmove(&addr->sin6_addr, @@ -4154,6 +4212,7 @@ handle_udp(int fd, short event, void* ar queries[i]->remote_addrlen = msgs[i].msg_hdr.msg_namelen; queries[i]->client_addrlen = (socklen_t)sizeof(queries[i]->client_addr); queries[i]->is_proxied = 0; + queries[i]->may_pad = data->may_pad; q = queries[i]; if (received == -1) { log_msg(LOG_ERR, "recvmmsg %d failed %s", i, strerror( @@ -4182,10 +4241,15 @@ handle_udp(int fd, short event, void* ar buffer_skip(q->packet, received); buffer_flip(q->packet); - if(data->pp2_enabled && !consume_pp2_header(q->packet, q, 0)) { - VERBOSITY(2, (LOG_ERR, "proxy-protocol: could not " - "consume PROXYv2 header")); - goto swap_drop; + if(data->pp2_enabled) { + if(!pp2_is_allowed(q)) + goto swap_drop; + if(!consume_pp2_header(q->packet, q, 0)) { + VERBOSITY(6, (LOG_ERR, "proxy-protocol: could not " + "consume PROXYv2 header")); + query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0); + goto swap_drop; + } } if(!q->is_proxied) { q->client_addrlen = q->remote_addrlen; @@ -4545,6 +4609,10 @@ handle_tcp_reading(int fd, short event, return; } buffer_flip(data->query->packet); + if(!pp2_is_allowed(data->query)) { + cleanup_tcp_handler(data); + return; + } if(!consume_pp2_header(data->query->packet, data->query, 1)) { VERBOSITY(6, (LOG_ERR, "proxy-protocol: could not consume PROXYv2 header")); @@ -4794,7 +4862,8 @@ handle_tcp_writing(int fd, short event, } #ifdef HAVE_WRITEV - sent -= sizeof(n_tcplen); + /* The number of bytes transmitted for the message content. */ + sent = data->bytes_transmitted - sizeof(n_tcplen); /* handle potential 'packet done' code */ goto packet_could_be_done; #endif @@ -5159,6 +5228,10 @@ handle_tls_reading(int fd, short event, return; } buffer_flip(data->query->packet); + if(!pp2_is_allowed(data->query)) { + cleanup_tcp_handler(data); + return; + } if(!consume_pp2_header(data->query->packet, data->query, 1)) { VERBOSITY(6, (LOG_ERR, "proxy-protocol: could not consume PROXYv2 header")); cleanup_tcp_handler(data); @@ -5400,7 +5473,6 @@ handle_tls_writing(int fd, short event, data->shake_state = tls_hs_read_event; tcp_handler_setup_event(data, handle_tls_reading, fd, EV_PERSIST | EV_READ | EV_TIMEOUT); } else if(want != SSL_ERROR_WANT_WRITE) { - cleanup_tcp_handler(data); { char client_ip[128], e[188]; if(data->query) { @@ -5412,6 +5484,7 @@ handle_tls_writing(int fd, short event, client_ip, "SSL_write error"); log_crypto_err(e); } + cleanup_tcp_handler(data); } return; } @@ -5710,6 +5783,7 @@ static void handle_xdp(int fd, short eve if ((event & EV_READ)) xdp_handle_recv_and_send(data->server); + (void)fd; } #endif Index: usr.sbin/nsd/udb.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/udb.h,v diff -u -p -r1.6 udb.h --- usr.sbin/nsd/udb.h 20 Dec 2023 17:29:02 -0000 1.6 +++ usr.sbin/nsd/udb.h 21 Sep 2026 16:29:08 -0000 @@ -213,7 +213,6 @@ struct udb_base { int useful_compact; }; -typedef enum udb_chunk_type udb_chunk_type; /** chunk type enum, setting these types help recovery and debug */ enum udb_chunk_type { udb_chunk_type_free = 0, @@ -221,6 +220,7 @@ enum udb_chunk_type { udb_chunk_type_task, udb_chunk_type_internal }; +typedef enum udb_chunk_type udb_chunk_type; typedef struct udb_chunk_d udb_chunk_d; /** @@ -242,7 +242,7 @@ struct udb_chunk_d { * In the free chunk this is the previous pointer. */ udb_void ptrlist; /* user data space starts here, 64-bit aligned */ - uint8_t data[0]; + uint8_t data[]; /* last octet: exp of chunk */ }; @@ -287,7 +287,7 @@ struct udb_xl_chunk_d { /** size of this chunk in bytes */ uint64_t size; /** data of the XL chunk */ - uint8_t data[0]; + uint8_t data[]; /* uint64_t endsize: before last octet the size again. */ /* uint8_t pad[7]: padding to make last octet last. */ /* last octet: exp of chunk: special XL value */ Index: usr.sbin/nsd/util.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/util.c,v diff -u -p -r1.31 util.c --- usr.sbin/nsd/util.c 6 Sep 2025 17:41:37 -0000 1.31 +++ usr.sbin/nsd/util.c 21 Sep 2026 16:29:08 -0000 @@ -273,7 +273,7 @@ clear_bit(uint8_t bits[], size_t index) } int -get_bit(uint8_t bits[], size_t index) +get_bit(const uint8_t bits[], size_t index) { /* * The bits are counted from left to right, so bit #0 is the @@ -910,8 +910,8 @@ print_rr(FILE *out, region_type* rr_region, buffer_type* output) { - rrtype_descriptor_type *descriptor - = rrtype_descriptor_by_type(record->type); + const nsd_type_descriptor_type *descriptor = + nsd_type_descriptor(record->type); int result; const dname_type *owner = domain_dname(record->owner); buffer_clear(output); @@ -943,14 +943,18 @@ print_rr(FILE *out, rrclass_to_string(record->klass), rrtype_to_string(record->type)); - result = print_rdata(output, descriptor, record); + if(record->type == TYPE_SOA) { + buffer_printf(output, "\t"); + result = print_soa_rdata_twoline(output, record); + } else { + result = print_rdata(output, descriptor, record); + } if (!result) { /* * Some RDATA failed to print, so print the record's * RDATA in unknown format. */ - result = rdata_atoms_to_unknown_string(output, - descriptor, record->rdata_count, record->rdatas); + result = print_unknown_rdata(output, descriptor, record); } if (result) { Index: usr.sbin/nsd/util.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/util.h,v diff -u -p -r1.23 util.h --- usr.sbin/nsd/util.h 6 Sep 2025 17:41:37 -0000 1.23 +++ usr.sbin/nsd/util.h 21 Sep 2026 16:29:08 -0000 @@ -131,7 +131,7 @@ void clear_bit(uint8_t bits[], size_t in /* * Return the value of the INDEXth bit of BITS. */ -int get_bit(uint8_t bits[], size_t index); +int get_bit(const uint8_t bits[], size_t index); /* A general purpose lookup table */ typedef struct lookup_table lookup_table_type; Index: usr.sbin/nsd/verify.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/verify.c,v diff -u -p -r1.5 verify.c --- usr.sbin/nsd/verify.c 6 Sep 2025 17:41:37 -0000 1.5 +++ usr.sbin/nsd/verify.c 21 Sep 2026 16:29:08 -0000 @@ -371,11 +371,13 @@ verify_handle_command(int fd, short even sig_atomic_t mode; assert(nsd != NULL); - assert((event & (EV_READ + { + short ev_read = EV_READ; #ifdef EV_CLOSED - | EV_CLOSED + ev_read |= EV_CLOSED; #endif - ))); + assert((event & ev_read)); + } if((len = read(fd, &mode, sizeof(mode))) == -1) { log_msg(LOG_ERR, "verify: verify_handle_command: read: %s", Index: usr.sbin/nsd/xdp-server.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/xdp-server.c,v diff -u -p -r1.1.1.1 xdp-server.c --- usr.sbin/nsd/xdp-server.c 6 Sep 2025 17:38:33 -0000 1.1.1.1 +++ usr.sbin/nsd/xdp-server.c 21 Sep 2026 16:29:08 -0000 @@ -211,7 +211,7 @@ static uint64_t xsk_umem_free_frames(str } static void xsk_free_umem_frame(struct xsk_socket_info *xsk, uint64_t frame) { - assert(xsk->umem_frame_free < XDP_NUM_FRAMES); + assert(xsk->umem->umem_frame_free < XDP_NUM_FRAMES); xsk->umem->umem_frame_addr[xsk->umem->umem_frame_free++] = frame; } @@ -343,19 +343,21 @@ xsk_configure_umem(struct xsk_umem_info static int xsk_configure_socket(struct xdp_server *xdp, struct xsk_socket_info *xsk_info, struct xsk_umem_info *umem, uint32_t queue_index) { - uint16_t xsk_bind_flags = XDP_USE_NEED_WAKEUP; - if (xdp->force_copy) { - xsk_bind_flags |= XDP_COPY; - } + struct xsk_socket_config xsk_cfg; + uint32_t idx, reserved; + int ret; + struct xdp_config cfg = { .xdp_flags = 0, - .xsk_bind_flags = xsk_bind_flags, + .xsk_bind_flags = 0, .libxdp_flags = XSK_LIBXDP_FLAGS__INHIBIT_PROG_LOAD, }; - struct xsk_socket_config xsk_cfg; - uint32_t idx, reserved; - int ret; + uint16_t xsk_bind_flags = XDP_USE_NEED_WAKEUP; + if (xdp->force_copy) { + xsk_bind_flags |= XDP_COPY; + } + cfg.xsk_bind_flags = xsk_bind_flags; xsk_info->umem = umem; xsk_cfg.rx_size = XSK_RING_CONS__NUM_DESCS; @@ -843,8 +845,8 @@ process_packet(struct xdp_server *xdp, u swap_udp(udp); if (ipv4) { - swap_ipv4(ipv4); __be16 ipv4_old_len = ipv4->tot_len; + swap_ipv4(ipv4); ipv4->tot_len = htons(sizeof(*ipv4)) + udp->len; csum16_replace(&ipv4->check, ipv4_old_len, ipv4->tot_len); udp->check = calc_csum_udp4(udp, ipv4); @@ -867,6 +869,7 @@ void xdp_handle_recv_and_send(struct xdp struct xsk_socket_info *xsk = &xdp->xsks[xdp->queue_index]; unsigned int recvd, i, reserved, to_send = 0; uint32_t idx_rx = 0; + uint32_t tx_idx = 0; int ret; recvd = xsk_ring_cons__peek(&xsk->rx, XDP_RX_BATCH_SIZE, &idx_rx); @@ -906,8 +909,6 @@ void xdp_handle_recv_and_send(struct xdp /* xsk->stats.rx_packets += rcvd; */ /* Process sending packets */ - - uint32_t tx_idx = 0; /* TODO: at least send as many packets as slots are available */ reserved = xsk_ring_prod__reserve(&xsk->tx, to_send, &tx_idx); Index: usr.sbin/nsd/xfrd-catalog-zones.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/xfrd-catalog-zones.c,v diff -u -p -r1.3 xfrd-catalog-zones.c --- usr.sbin/nsd/xfrd-catalog-zones.c 6 Sep 2025 17:41:37 -0000 1.3 +++ usr.sbin/nsd/xfrd-catalog-zones.c 21 Sep 2026 16:29:08 -0000 @@ -9,6 +9,7 @@ #include "difffile.h" #include "nsd.h" #include "packet.h" +#include "rdata.h" #include "xfrd-catalog-zones.h" #include "xfrd-notify.h" @@ -545,11 +546,9 @@ xfrd_process_catalog_consumer_zone( } version_2_found = 0; for (i = 0; i < rrset->rr_count; i++) { - if (rrset->rrs[i].rdata_count != 1) - continue; - if (rrset->rrs[i].rdatas[0].data[0] == 2 - && ((uint8_t*)(rrset->rrs[i].rdatas[0].data + 1))[0] == 1 - && ((uint8_t*)(rrset->rrs[i].rdatas[0].data + 1))[1] == '2') { + if(rrset->rrs[i]->rdlength == 2 && + rrset->rrs[i]->rdata[0] == 1 /* TXT string length */ && + rrset->rrs[i]->rdata[1] == '2') { version_2_found = 1; break; } @@ -583,6 +582,7 @@ retry_adding: int valid_group_values; struct pattern_options *pattern = NULL; struct catalog_member_zone* to_add; + const dname_type* group_dname; if (domain_dname(member_id)->label_count > dname->label_count+2 || !(rrset = domain_find_rrset(member_id, zone, TYPE_PTR))) @@ -602,17 +602,21 @@ retry_adding: return; } /* A PTR rr always has 1 rdata element which is a dname */ - if (rrset->rrs[0].rdata_count != 1) + member_domain = rdata_domain_ref(rrset->rrs[0]); + if(!member_domain) continue; - member_domain = rrset->rrs[0].rdatas[0].domain; domain_to_string_buf(member_domain, member_domain_str); /* remove trailing dot */ member_domain_str[strlen(member_domain_str) - 1] = 0; valid_group_values = 0; /* Lookup group. TXT for matching patterns */ - if(!namedb_lookup(xfrd->nsd->db, label_plus_dname("group", - domain_dname(member_id)), + group_dname = label_plus_dname("group", + domain_dname(member_id)); + /* If group_dname is NULL, then the group name is too long, + * and group properties are skipped for this member. */ + if(!group_dname + || !namedb_lookup(xfrd->nsd->db, group_dname, &group, &closest_encloser) || !(rrset = domain_find_rrset(group, zone, TYPE_TXT))) { ; /* pass */ @@ -622,22 +626,20 @@ retry_adding: char group_value[256]; /* Looking for a single TXT rdata field */ - if (rrset->rrs[i].rdata_count != 1 + if(rrset->rrs[i]->rdlength < 1 + || rrset->rrs[i]->rdlength != + ((uint16_t)rrset->rrs[i]->rdata[0])+1 /* rdata field should be at least 1 char */ - || rrset->rrs[i].rdatas[0].data[0] < 2 - - /* single rdata atom with single TXT rdata field */ - || (uint16_t)(((uint8_t*)(rrset->rrs[i].rdatas[0].data + 1))[0]) - != (uint16_t) (rrset->rrs[i].rdatas[0].data[0]-1)) + || rrset->rrs[i]->rdata[0] < 1) continue; memcpy( group_value - , (uint8_t*)(rrset->rrs[i].rdatas[0].data+1) + 1 - ,((uint8_t*)(rrset->rrs[i].rdatas[0].data+1))[0] + , rrset->rrs[i]->rdata+1 + , rrset->rrs[i]->rdata[0] ); group_value[ - ((uint8_t*)(rrset->rrs[i].rdatas[0].data+1))[0] + rrset->rrs[i]->rdata[0] ] = 0; if ((pattern = pattern_options_find( xfrd->nsd->options, group_value))) @@ -715,13 +717,14 @@ retry_adding: member_id_to_delete_str)); catalog_del_consumer_member_zone( consumer_zone, to_delete); - if(cursor != RBTREE_NULL) + if(cursor != RBTREE_NULL) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Comparing %s with %s", member_id_str, dname_to_string( cursor_member_id(cursor), NULL))); + } } if (cursor != RBTREE_NULL && cmp == 0) { /* member_id is also in an current catalog @@ -975,13 +978,26 @@ xfrd_add_catalog_producer_member(struct producer_name = producer_zone->node.key; while(!cmz->member_id) { /* Make new member_id with this catalog producer */ + const dname_type* zones_name; char id_label[sizeof(uint32_t)*2+1]; uint32_t new_id = (uint32_t)random_generate(0x7fffffff); hex_ntop((void*)&new_id, sizeof(uint32_t), id_label, sizeof(id_label)); id_label[sizeof(uint32_t)*2] = 0; - cmz->member_id = label_plus_dname(id_label, - label_plus_dname("zones", producer_name)); + zones_name = label_plus_dname("zones", producer_name); + if(!zones_name) { + log_msg(LOG_ERR, "catalog producer: producer zone name " + "is too long, to add zones label. " + "Skipping addition of member zone"); + return; + } + cmz->member_id = label_plus_dname(id_label, zones_name); + if(!cmz->member_id) { + log_msg(LOG_ERR, "catalog producer: producer zone name " + "is too long, for member_id.zones labels. " + "Skipping addition of member zone"); + return; + } DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "does member_id %s exist?", dname_to_string(cmz->member_id, NULL))); if (!rbtree_search(&producer_zone->member_ids, cmz)) { @@ -1187,6 +1203,19 @@ xfrd_process_catalog_producer_zone( return; /* No changes */ producer_name = (dname_type*)producer_zone->node.key; + if (producer_name->name_size > 234) { + /* For "group".<8char member id>."zones".apex. + * that is (1+5)+(1+8)+(1+5)+apex. + * 255 - (6+9+6) = 234 */ + log_msg(LOG_ERR, "catalog producer: name too long of " + "producer zone %s", dname_to_string(producer_name,0)); + return; + } + if (producer_name->label_count > 124) { + log_msg(LOG_ERR, "catalog producer: too many labels in name of " + "producer zone %s", dname_to_string(producer_name,0)); + return; + } xfr_writer_init(&xw, producer_zone); xfr_writer_add_SOA(&xw, producer_name, xw.new_serial); @@ -1203,6 +1232,7 @@ xfrd_process_catalog_producer_zone( /* IXFR */ xfr_writer_add_SOA(&xw, producer_name, xw.old_serial); while(producer_zone->to_delete) { + const dname_type* group_owner; struct xfrd_producer_member* to_delete = producer_zone->to_delete; @@ -1210,14 +1240,45 @@ xfrd_process_catalog_producer_zone( producer_zone->to_delete = to_delete->next; to_delete->next = NULL; + if((size_t)to_delete->member_id->name_size > 249) { + log_msg(LOG_ERR, "catalog producer: member_id " + "name_size %d too long for group property " + "owner; skipping delete of member zone %s", + (int)to_delete->member_id->name_size, + dname_to_string(to_delete->member_zone_name,0)); + region_recycle( xfrd->nsd->options->region + , (void *)to_delete->member_id + , dname_total_size(to_delete->member_id)); + region_recycle( xfrd->region /* allocated in perform_delzone */ + , (void *)to_delete->member_zone_name + , dname_total_size(to_delete->member_zone_name)); + region_recycle( xfrd->region, to_delete, sizeof(*to_delete)); + continue; + } + /* Write PTR */ xfr_writer_add_PTR(&xw, to_delete->member_id , to_delete->member_zone_name); /* Write group. TXT */ + group_owner = label_plus_dname("group", to_delete->member_id); + if(!group_owner) { + log_msg(LOG_ERR, "catalog producer: member_id " + "name_size %d too long for group property " + "owner; skipping delete of member zone %s", + (int)to_delete->member_id->name_size, + dname_to_string(to_delete->member_zone_name,0)); + region_recycle( xfrd->nsd->options->region + , (void *)to_delete->member_id + , dname_total_size(to_delete->member_id)); + region_recycle( xfrd->region /* allocated in perform_delzone */ + , (void *)to_delete->member_zone_name + , dname_total_size(to_delete->member_zone_name)); + region_recycle( xfrd->region, to_delete, sizeof(*to_delete)); + continue; + } xfr_writer_add_TXT( &xw - , label_plus_dname("group" - , to_delete->member_id) + , group_owner , to_delete->group_name); region_recycle( xfrd->nsd->options->region @@ -1234,19 +1295,38 @@ xfrd_process_catalog_producer_zone( add_member_zones: while(producer_zone->to_add) { struct xfrd_producer_member* to_add = producer_zone->to_add; + const dname_type* group_owner; /* Pop to_add from stack */ producer_zone->to_add = to_add->next; to_add->next = NULL; + if((size_t)to_add->member_id->name_size > 249) { + log_msg(LOG_ERR, "catalog producer: member_id " + "name_size %d too long for group property " + "owner; skipping member zone %s", + (int)to_add->member_id->name_size, + dname_to_string(to_add->member_zone_name,0)); + region_recycle(xfrd->region, to_add, sizeof(*to_add)); + continue; + } + /* Write PTR */ xfr_writer_add_PTR(&xw, to_add->member_id, to_add->member_zone_name); /* Write group. TXT */ + group_owner = label_plus_dname("group", to_add->member_id); + if(!group_owner) { + log_msg(LOG_ERR, "catalog producer: member_id too long " + "to construct group owner (name_size=%d); " + "skipping group property for member zone", + (int)to_add->member_id->name_size); + region_recycle(xfrd->region, to_add, sizeof(*to_add)); + continue; + } xfr_writer_add_TXT( &xw - , label_plus_dname("group" - , to_add->member_id) + , group_owner , to_add->group_name); /* Don't recycle any of the struct attributes as they come Index: usr.sbin/nsd/xfrd-disk.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/xfrd-disk.c,v diff -u -p -r1.16 xfrd-disk.c --- usr.sbin/nsd/xfrd-disk.c 12 Apr 2024 15:53:34 -0000 1.16 +++ usr.sbin/nsd/xfrd-disk.c 21 Sep 2026 16:29:08 -0000 @@ -91,6 +91,7 @@ xfrd_read_state_soa(FILE* in, const char const char* id, xfrd_soa_type* soa, time_t* soatime) { char *p; + uint16_t rdata_count = 0; if(!xfrd_read_check_str(in, id_acquired) || !xfrd_read_time_t(in, soatime)) { @@ -104,7 +105,7 @@ xfrd_read_state_soa(FILE* in, const char !xfrd_read_i16(in, &soa->type) || !xfrd_read_i16(in, &soa->klass) || !xfrd_read_i32(in, &soa->ttl) || - !xfrd_read_i16(in, &soa->rdata_count)) + !xfrd_read_i16(in, &rdata_count)) { return 0; } @@ -112,7 +113,6 @@ xfrd_read_state_soa(FILE* in, const char soa->type = htons(soa->type); soa->klass = htons(soa->klass); soa->ttl = htonl(soa->ttl); - soa->rdata_count = htons(soa->rdata_count); if(!(p=xfrd_read_token(in)) || !(soa->prim_ns[0] = dname_parse_wire(soa->prim_ns+1, p))) @@ -428,7 +428,11 @@ xfrd_write_state_soa(FILE* out, const ch fprintf(out, "\t%s: %u %u %u %u", id, (unsigned)ntohs(soa->type), (unsigned)ntohs(soa->klass), - (unsigned)ntohl(soa->ttl), (unsigned)ntohs(soa->rdata_count)); + (unsigned)ntohl(soa->ttl), + /* This is the old rdata_count, and is printed for + * compatibility. Otherwise, if it is not printed, change + * the xfrd state file version number. */ + (unsigned)7); fprintf(out, " "); xfrd_write_dname(out, soa->prim_ns); fprintf(out, " "); Index: usr.sbin/nsd/xfrd-tcp.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/xfrd-tcp.c,v diff -u -p -r1.32 xfrd-tcp.c --- usr.sbin/nsd/xfrd-tcp.c 6 Sep 2025 17:41:37 -0000 1.32 +++ usr.sbin/nsd/xfrd-tcp.c 21 Sep 2026 16:29:08 -0000 @@ -321,7 +321,7 @@ void pick_id_values(uint16_t* array, int for(j = max-num; jtcp_waiting) { + /* delete from tcp waiting list */ + if(z->tcp_waiting_prev) + z->tcp_waiting_prev->tcp_waiting_next = + z->tcp_waiting_next; + else xfrd->tcp_set->tcp_waiting_first = z->tcp_waiting_next; + if(z->tcp_waiting_next) + z->tcp_waiting_next->tcp_waiting_prev = + z->tcp_waiting_prev; + else xfrd->tcp_set->tcp_waiting_last = z->tcp_waiting_prev; + z->tcp_waiting = 0; + } +} + /* remove zone from tcp waiting list */ static void tcp_zone_waiting_list_popfirst(struct xfrd_tcp_set* set, xfrd_zone_type* zone) @@ -1100,6 +1117,8 @@ xfrd_tcp_setup_write_packet(struct xfrd_ struct xfrd_tcp* tcp = tp->tcp_w; assert(zone->tcp_conn != -1); assert(zone->tcp_waiting == 0); + /* make sure we have a master to query the xfr request to */ + assert(zone->master); /* start AXFR or IXFR for the zone */ if(zone->soa_disk_acquired == 0 || zone->master->use_axfr_only || zone->master->ixfr_disabled || @@ -1252,8 +1271,8 @@ int conn_write(struct xfrd_tcp* tcp) } tcp->total_bytes += sent; - if(sent > (ssize_t)sizeof(tcp->msglen)) - buffer_skip(tcp->packet, sent-sizeof(tcp->msglen)); + if(tcp->total_bytes > (ssize_t)sizeof(tcp->msglen)) + buffer_skip(tcp->packet, tcp->total_bytes-sizeof(tcp->msglen)); if(tcp->total_bytes < sizeof(tcp->msglen)) { /* incomplete write, resume later */ return 0; Index: usr.sbin/nsd/xfrd-tcp.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/xfrd-tcp.h,v diff -u -p -r1.6 xfrd-tcp.h --- usr.sbin/nsd/xfrd-tcp.h 6 Sep 2025 17:41:37 -0000 1.6 +++ usr.sbin/nsd/xfrd-tcp.h 21 Sep 2026 16:29:08 -0000 @@ -187,6 +187,8 @@ struct xfrd_tcp* xfrd_tcp_create(struct void xfrd_tcp_obtain(struct xfrd_tcp_set* set, struct xfrd_zone* zone); /* release tcp connection for a zone (starts waiting) */ void xfrd_tcp_release(struct xfrd_tcp_set* set, struct xfrd_zone* zone); +/* remove zone from tcp waiting list */ +void tcp_zone_waiting_list_remove(struct xfrd_zone* z); /* release tcp pipe entirely (does not stop the zones inside it) */ void xfrd_tcp_pipe_release(struct xfrd_tcp_set* set, struct xfrd_tcp_pipeline* tp, int conn); Index: usr.sbin/nsd/xfrd.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/xfrd.c,v diff -u -p -r1.35 xfrd.c --- usr.sbin/nsd/xfrd.c 6 Sep 2025 17:41:37 -0000 1.35 +++ usr.sbin/nsd/xfrd.c 21 Sep 2026 16:29:09 -0000 @@ -627,13 +627,16 @@ apply_xfrs_to_consumer_zone(struct xfrd_ FILE* df; if(xfr->msg_is_ixfr) { - uint32_t soa_serial; + uint32_t soa_serial=0, after_serial=0; xfrd_xfr_type* prev; - if(dbzone->soa_rrset == NULL || dbzone->soa_rrset->rrs == NULL - || dbzone->soa_rrset->rrs[0].rdata_count <= 2 - || rdata_atom_size(dbzone->soa_rrset->rrs[0].rdatas[2]) - != sizeof(uint32_t)) { + if(dbzone->soa_rrset == NULL +#ifndef PACKED_STRUCTS + || dbzone->soa_rrset->rrs == NULL +#endif + || dbzone->soa_rrset->rrs[0]->rdlength < 20+2*sizeof(void*) + || !retrieve_soa_rdata_serial(dbzone->soa_rrset->rrs[0], + &soa_serial)) { make_catalog_consumer_invalid(consumer_zone, "could not apply ixfr on catalog consumer zone " @@ -641,8 +644,6 @@ apply_xfrs_to_consumer_zone(struct xfrd_ consumer_zone->options->name); return; } - soa_serial = read_uint32(rdata_atom_data( - dbzone->soa_rrset->rrs[0].rdatas[2])); if(soa_serial == xfr->msg_old_serial) goto apply_xfr; for(prev = xfr->prev; prev; prev = prev->prev) { @@ -653,8 +654,10 @@ apply_xfrs_to_consumer_zone(struct xfrd_ apply_xfrs_to_consumer_zone(consumer_zone, dbzone, prev); break; } - if(!prev || xfr->msg_old_serial != read_uint32(rdata_atom_data( - dbzone->soa_rrset->rrs[0].rdatas[2]))){ + if(!prev + || !retrieve_soa_rdata_serial(dbzone->soa_rrset->rrs[0], + &after_serial) + || xfr->msg_old_serial != after_serial) { make_catalog_consumer_invalid(consumer_zone, "could not find and/or apply xfrs for catalog " "consumer zone \'%s\': to update to serial %u", @@ -700,6 +703,7 @@ xfrd_process_soa_info_task(struct task_l zone_type* dbzone = NULL; xfrd_xfr_type* xfr; xfrd_xfr_type* prev_xfr; + int xfr_was_ixfr = 0; enum soainfo_hint hint; #ifndef NDEBUG time_t before; @@ -729,7 +733,6 @@ xfrd_process_soa_info_task(struct task_l soa.klass = htons(CLASS_IN); memmove(&soa.ttl, p, sizeof(uint32_t)); p += sizeof(uint32_t); - soa.rdata_count = htons(7); memmove(soa.prim_ns, p, sizeof(uint8_t)); p += sizeof(uint8_t); memmove(soa.prim_ns+1, p, soa.prim_ns[0]); @@ -823,6 +826,10 @@ xfrd_process_soa_info_task(struct task_l dbzone = namedb_find_or_create_zone( xfrd->nsd->db, task->zname , consumer_zone->options); } + if(zone->latest_xfr) { + xfr_was_ixfr = (zone->latest_xfr->query_type == TYPE_IXFR); + } + /* soainfo_gone and soainfo_bad are straightforward, delete all updates that were transfered, i.e. acquired != 0. soainfo_ok is more complicated as it is possible that there are subsequent corrupt or @@ -911,6 +918,28 @@ xfrd_process_soa_info_task(struct task_l break; /* fall through */ case soainfo_gone: + if(hint == soainfo_gone) { + /* "rollback" on-disk soa information */ + zone->soa_disk_acquired = zone->soa_nsd_acquired; + zone->soa_disk = zone->soa_nsd; + } + if(hint == soainfo_gone && !soa_ptr) { + if(xfr_was_ixfr) { + /* Attempt without IXFR, maybe AXFR works. */ + xfrd_disable_ixfr(zone); + xfrd_set_zone_state(zone, xfrd_zone_refreshing); + xfrd_set_refresh_now(zone); + break; + } + /* The zone transfer update failed to apply. + * Okay to fallback from IXFR to AXFR, but after failed + * AXFR, wait for retry instead of immediate fetch */ + VERBOSITY(2, (LOG_INFO, "xfrd: zone %s transfer " + "failed to apply, waiting for retry", + zone->apex_str)); + xfrd_set_timer_retry(zone); + break; + } xfrd_handle_incoming_soa(zone, soa_ptr, acquired); break; } @@ -1017,24 +1046,8 @@ xfrd_deactivate_zone(xfrd_zone_type* z) } void -xfrd_del_slave_zone(xfrd_state_type* xfrd, const dname_type* dname) +udp_zone_waiting_list_remove(xfrd_zone_type* z) { - xfrd_zone_type* z = (xfrd_zone_type*)rbtree_delete(xfrd->zones, dname); - if(!z) return; - - /* io */ - if(z->tcp_waiting) { - /* delete from tcp waiting list */ - if(z->tcp_waiting_prev) - z->tcp_waiting_prev->tcp_waiting_next = - z->tcp_waiting_next; - else xfrd->tcp_set->tcp_waiting_first = z->tcp_waiting_next; - if(z->tcp_waiting_next) - z->tcp_waiting_next->tcp_waiting_prev = - z->tcp_waiting_prev; - else xfrd->tcp_set->tcp_waiting_last = z->tcp_waiting_prev; - z->tcp_waiting = 0; - } if(z->udp_waiting) { /* delete from udp waiting list */ if(z->udp_waiting_prev) @@ -1047,6 +1060,23 @@ xfrd_del_slave_zone(xfrd_state_type* xfr else xfrd->udp_waiting_last = z->udp_waiting_prev; z->udp_waiting = 0; } +} + +void +xfrd_del_slave_zone(xfrd_state_type* xfrd, const dname_type* dname) +{ + xfrd_zone_type* z = (xfrd_zone_type*)rbtree_delete(xfrd->zones, dname); + if(!z) return; + + /* io */ + if(z->tcp_waiting) { + /* delete from tcp waiting list */ + tcp_zone_waiting_list_remove(z); + } + if(z->udp_waiting) { + /* delete from udp waiting list */ + udp_zone_waiting_list_remove(z); + } xfrd_deactivate_zone(z); if(z->tcp_conn != -1) { xfrd_tcp_release(xfrd->tcp_set, z); @@ -1249,6 +1279,16 @@ xfrd_make_request(xfrd_zone_type* zone) if(zone->round_num >= XFRD_MAX_ROUNDS) { /* tried all servers that many times, wait */ zone->round_num = -1; + /* Discard NOTIFY serial hint. After one round of + * searching for it, at the upstream primaries, the + * notify hint need no longer be used. The retry + * timer is used, for one, earlier re-attempt. This + * is useful if the upstream is in-progress of loading + * the zone information. After the brief wait it may + * have completed that task. And this may catch the + * case where it notify is sent before the load + * activity has completed at the primary. */ + zone->soa_notified_acquired = 0; xfrd_set_timer_retry(zone); DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd zone %s makereq wait_retry, rd %d mr %d nx %d", @@ -1377,22 +1417,24 @@ xfrd_time() void xfrd_copy_soa(xfrd_soa_type* soa, rr_type* rr) { - const uint8_t* rr_ns_wire = dname_name(domain_dname(rdata_atom_domain(rr->rdatas[0]))); - uint8_t rr_ns_len = domain_dname(rdata_atom_domain(rr->rdatas[0]))->name_size; - const uint8_t* rr_em_wire = dname_name(domain_dname(rdata_atom_domain(rr->rdatas[1]))); - uint8_t rr_em_len = domain_dname(rdata_atom_domain(rr->rdatas[1]))->name_size; + const uint8_t* rr_ns_wire = dname_name(domain_dname(rdata_domain_ref(rr))); + uint8_t rr_ns_len = domain_dname(rdata_domain_ref(rr))->name_size; + const uint8_t* rr_em_wire = dname_name(domain_dname( + rdata_domain_ref_offset(rr, sizeof(void*)))); + uint8_t rr_em_len = domain_dname(rdata_domain_ref_offset(rr, + sizeof(void*)))->name_size; + uint8_t* p; - if(rr->type != TYPE_SOA || rr->rdata_count != 7) { + if(rr->type != TYPE_SOA || rr->rdlength != 20+2*sizeof(void*)) { log_msg(LOG_ERR, "xfrd: copy_soa called with bad rr, type %d rrs %u.", - rr->type, rr->rdata_count); + rr->type, (unsigned)rr->rdlength); return; } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: copy_soa rr, type %d rrs %u, ttl %u.", - (int)rr->type, (unsigned)rr->rdata_count, (unsigned)rr->ttl)); + (int)rr->type, (unsigned)rr->rdlength, (unsigned)rr->ttl)); soa->type = htons(rr->type); soa->klass = htons(rr->klass); soa->ttl = htonl(rr->ttl); - soa->rdata_count = htons(rr->rdata_count); /* copy dnames */ soa->prim_ns[0] = rr_ns_len; @@ -1401,11 +1443,12 @@ xfrd_copy_soa(xfrd_soa_type* soa, rr_typ memcpy(soa->email+1, rr_em_wire, rr_em_len); /* already in network format */ - memcpy(&soa->serial, rdata_atom_data(rr->rdatas[2]), sizeof(uint32_t)); - memcpy(&soa->refresh, rdata_atom_data(rr->rdatas[3]), sizeof(uint32_t)); - memcpy(&soa->retry, rdata_atom_data(rr->rdatas[4]), sizeof(uint32_t)); - memcpy(&soa->expire, rdata_atom_data(rr->rdatas[5]), sizeof(uint32_t)); - memcpy(&soa->minimum, rdata_atom_data(rr->rdatas[6]), sizeof(uint32_t)); + p = rr->rdata + 2*sizeof(void*); + memcpy(&soa->serial, p, sizeof(uint32_t)); + memcpy(&soa->refresh, p+4, sizeof(uint32_t)); + memcpy(&soa->retry, p+8, sizeof(uint32_t)); + memcpy(&soa->expire, p+12, sizeof(uint32_t)); + memcpy(&soa->minimum, p+16, sizeof(uint32_t)); DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: copy_soa rr, serial %u refresh %u retry %u expire %u", (unsigned)ntohl(soa->serial), (unsigned)ntohl(soa->refresh), @@ -1624,6 +1667,9 @@ xfrd_udp_read_packet(buffer_type* packet log_msg(LOG_ERR, "xfrd: recvfrom failed: %s", strerror(errno)); return 0; + } else if(received < QHEADERSZ) { + log_msg(LOG_ERR, "xfrd: UDP packet too small"); + return 0; } buffer_set_limit(packet, received); return 1; @@ -1979,7 +2025,8 @@ static int xfrd_parse_soa_info(buffer_ty { return 0; } - soa->rdata_count = 7; /* rdata in SOA */ + if(!buffer_available(packet, 20)) + return 0; soa->serial = htonl(buffer_read_u32(packet)); soa->refresh = htonl(buffer_read_u32(packet)); soa->retry = htonl(buffer_read_u32(packet)); @@ -2002,12 +2049,15 @@ xfrd_xfr_check_rrs(xfrd_zone_type* zone, int *done, xfrd_soa_type* soa, region_type* temp) { /* first RR has already been checked */ + const struct nsd_type_descriptor *descriptor; uint32_t tmp_serial = 0; - uint16_t type, rrlen; + uint16_t type, klass, rrlen; size_t i, soapos, mempos; const dname_type* dname; + struct rr* rr; + int32_t code; domain_table_type* owners; - rdata_atom_type* rdatas; + enum { DELETING_RRs = 0, ADDING_RRs } ixfr_state = DELETING_RRs; for(i=0; ilatest_xfr->msg_rr_count) { @@ -2032,7 +2082,15 @@ xfrd_xfr_check_rrs(xfrd_zone_type* zone, } soapos = buffer_position(packet); type = buffer_read_u16(packet); - (void)buffer_read_u16(packet); /* class */ + klass = buffer_read_u16(packet); + if(klass != CLASS_IN && type != TYPE_OPT) { + log_msg(LOG_ERR, "xfrd: zone %s xfr " + "non-IN-class RR (%s type=%s class=%s), rejected", + zone->apex_str, dname_to_string(dname,0), + rrtype_to_string(type), + rrclass_to_string(klass)); + return 0; + } (void)buffer_read_u32(packet); /* ttl */ rrlen = buffer_read_u16(packet); if(!buffer_available(packet, rrlen)) { @@ -2041,10 +2099,15 @@ xfrd_xfr_check_rrs(xfrd_zone_type* zone, return 0; } mempos = buffer_position(packet); - if(rdata_wireformat_to_rdata_atoms(temp, owners, type, rrlen, - packet, &rdatas) == -1) { + + descriptor = nsd_type_descriptor(type); + code = descriptor->read_rdata(owners, rrlen, packet, &rr); + if(code < 0) { DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr unable " - "to parse rdata", zone->apex_str)); + "to parse rdata %s %s %s", zone->apex_str, + dname_to_string(dname,0), + rrtype_to_string(type), + read_rdata_fail_str(code))); return 0; } if(type == TYPE_SOA) { @@ -2071,6 +2134,7 @@ xfrd_xfr_check_rrs(xfrd_zone_type* zone, } zone->latest_xfr->msg_old_serial = ntohl(soa->serial); tmp_serial = ntohl(soa->serial); + ixfr_state = DELETING_RRs; } else if(ntohl(soa->serial) == zone->latest_xfr->msg_new_serial) { /* saw another SOA of new serial. */ @@ -2093,13 +2157,44 @@ xfrd_xfr_check_rrs(xfrd_zone_type* zone, "serial decreasing not allowed", zone->apex_str)); return 0; /* middle serial decreases in IXFR */ } + if(ntohl(soa->serial) == tmp_serial) { + if(ixfr_state == DELETING_RRs) { + DEBUG(DEBUG_XFRD,1, ( LOG_ERR, "xfrd: zone %s xfr serial duplicate " + "not allowed for serial %"PRIu32" while %s", + zone->apex_str, tmp_serial, + ( ixfr_state == DELETING_RRs + ? "deleting RRs" : "adding RRs"))); + return 0; /* middle serial is the same as the previous in IXFR */ + } + ixfr_state = DELETING_RRs; + } else { + assert(ntohl(soa->serial) > tmp_serial); + ixfr_state = ADDING_RRs; + } /* serial ok, update tmp serial */ tmp_serial = ntohl(soa->serial); } + else { + /* AXFR mode, rr_count>1, serial!=new_serial, + * RFC 5936 s2.2 forbids SOA in a non-terminal + * position with a different serial. Reject + * the stream. */ + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s axfr " + "unexpected mid-stream SOA serial %u", + zone->apex_str, + (unsigned)ntohl(soa->serial))); + return 0; /* SOA in middle of AXFR */ + } } buffer_set_position(packet, mempos); buffer_skip(packet, rrlen); } + if(buffer_position(packet) != buffer_limit(packet)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr bad, " + "trailing data %d bytes", zone->apex_str, + (int)(buffer_limit(packet)-buffer_position(packet)))); + return 0; /* trailing data */ + } /* packet seems to have a valid DNS RR structure */ return 1; } @@ -2291,12 +2386,12 @@ xfrd_parse_received_xfr_packet(xfrd_zone if(zone->soa_disk_acquired != 0 && zone->state != xfrd_zone_expired /* if expired - accept anything */ && compare_serial(ntohl(soa->serial), ntohl(zone->soa_disk.serial)) < 0) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, - "xfrd: zone %s ignoring old serial (%u/%u) from %s", - zone->apex_str, ntohl(zone->soa_disk.serial), ntohl(soa->serial), zone->master->ip_address_spec)); - VERBOSITY(1, (LOG_INFO, - "xfrd: zone %s ignoring old serial (%u/%u) from %s", - zone->apex_str, ntohl(zone->soa_disk.serial), ntohl(soa->serial), zone->master->ip_address_spec)); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: zone %s ignoring old serial (local: %u, remote: %u) from %s", + zone->apex_str, ntohl(zone->soa_disk.serial), ntohl(soa->serial), zone->master->ip_address_spec)); + VERBOSITY(1, (LOG_INFO, + "xfrd: zone %s ignoring old serial (local: %u, remote: %u) from %s", + zone->apex_str, ntohl(zone->soa_disk.serial), ntohl(soa->serial), zone->master->ip_address_spec)); region_destroy(tempregion); return xfrd_packet_bad; } @@ -2617,6 +2712,7 @@ xfrd_handle_received_xfr_packet(xfrd_zon zone->latest_xfr->xfrfilenumber)) { zone->latest_xfr->sent = xfrd->nsd->mytask + 1; + xfrd->num_xfrs_in_reload++; } /* reset msg seq nr, so if that is nonnull we know xfr file exists */ zone->latest_xfr->msg_seq_nr = 0; @@ -2721,6 +2817,12 @@ xfrd_handle_notify_and_start_xfr(xfrd_zo } } +struct xfrd_zone* +xfrd_find_zone(xfrd_state_type* xfrd, const dname_type* dname) +{ + return (xfrd_zone_type*)rbtree_search(xfrd->zones, dname); +} + void xfrd_handle_passed_packet(buffer_type* packet, int acl_num, int acl_num_xfr) @@ -2931,6 +3033,8 @@ xfrd_prepare_zones_for_reload(void) xfr->msg_old_serial, xfr->msg_new_serial, xfr->xfrfilenumber); + if(send) + xfrd->num_xfrs_in_reload++; if(send && !reload) { reload = 1; xfrd_set_reload_timeout(); @@ -3005,9 +3109,14 @@ void xfrd_process_task_result(xfrd_state void xfrd_set_reload_now(xfrd_state_type* xfrd) { -#ifdef HAVE_SYSTEMD - sd_notify(0, "RELOADING=1"); -#endif + /* systemd kills after a timer, but a large zone can take time. + * so there is no, + * #ifdef HAVE_SYSTEMD + * sd_notify(0, "RELOADING=1"); + * #endif + * message. + * Also, NSD stays responsive during that time, due to the forked + * operation. */ xfrd->need_to_send_reload = 1; if(!(xfrd->ipc_handler_flags&EV_WRITE)) { ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ|EV_WRITE); Index: usr.sbin/nsd/xfrd.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/xfrd.h,v diff -u -p -r1.14 xfrd.h --- usr.sbin/nsd/xfrd.h 3 Sep 2025 18:46:48 -0000 1.14 +++ usr.sbin/nsd/xfrd.h 21 Sep 2026 16:29:09 -0000 @@ -85,6 +85,8 @@ struct xfrd_state { uint8_t reload_failed; uint8_t can_send_reload; pid_t reload_pid; + int num_reload_failed_repeat; + int num_xfrs_in_reload; /* timeout for lost sigchild and reaping children */ struct event child_timer; int child_timer_added; @@ -137,7 +139,6 @@ struct xfrd_soa { uint16_t type; /* = TYPE_SOA */ uint16_t klass; /* = CLASS_IN */ uint32_t ttl; - uint16_t rdata_count; /* = 7 */ /* format is 1 octet length, + wireformat dname. one more octet since parse_dname_wire_from_packet needs it. maximum size is allocated to avoid memory alloc/free. */ @@ -390,6 +391,8 @@ void xfrd_set_timer(xfrd_zone_type* zone void xfrd_set_refresh_now(xfrd_zone_type* zone); /* unset the timer - no more timeouts, for when zone is queued */ void xfrd_unset_timer(xfrd_zone_type* zone); +/* remove the zone from the udp waiting list */ +void udp_zone_waiting_list_remove(xfrd_zone_type* z); /* remove the 'refresh now', remove it from the activated list */ void xfrd_deactivate_zone(xfrd_zone_type* z); @@ -488,5 +491,9 @@ const char* xfrd_pretty_time(time_t v); xfrd_xfr_type *xfrd_prepare_zone_xfr(xfrd_zone_type *zone, uint16_t query_type); void xfrd_delete_zone_xfr(xfrd_zone_type *zone, xfrd_xfr_type *xfr); + +/* Find zone by name. */ +struct xfrd_zone* xfrd_find_zone(xfrd_state_type* xfrd, + const dname_type* dname); #endif /* XFRD_H */ Index: usr.sbin/nsd/zonec.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/zonec.c,v diff -u -p -r1.32 zonec.c --- usr.sbin/nsd/zonec.c 3 Sep 2025 18:46:48 -0000 1.32 +++ usr.sbin/nsd/zonec.c 21 Sep 2026 16:29:09 -0000 @@ -45,60 +45,6 @@ #include "zone.h" /* - * Compares two rdata arrays. - * - * Returns: - * - * zero if they are equal - * non-zero if not - * - */ -static int -zrdatacmp(uint16_t type, const union rdata_atom *rdatas, size_t rdata_count, rr_type *b) -{ - assert(rdatas); - assert(b); - - /* One is shorter than another */ - if (rdata_count != b->rdata_count) - return 1; - - /* Compare element by element */ - for (size_t i = 0; i < rdata_count; ++i) { - if (rdata_atom_is_domain(type, 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(rdatas[i]) - != rdata_atom_size(b->rdatas[i])) - return 1; - if (!dname_equal_nocase(rdata_atom_data(rdatas[i]), - rdata_atom_data(b->rdatas[i]), - rdata_atom_size(rdatas[i]))) - return 1; - } else { - if (rdata_atom_size(rdatas[i]) - != rdata_atom_size(b->rdatas[i])) - { - return 1; - } - if (memcmp(rdata_atom_data(rdatas[i]), - rdata_atom_data(b->rdatas[i]), - rdata_atom_size(rdatas[i])) != 0) - { - return 1; - } - } - } - - /* Otherwise they are equal */ - return 0; -} - -/* * Find rrset type for any zone */ static rrset_type* @@ -162,13 +108,99 @@ has_soa(domain_type* domain) 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; + + struct collect_rrs c; }; +static void zonec_commit_rrset(zone_parser_t *parser, struct zonec_state *state) +{ + struct rrset *rrset; + int priority = parser->options.secondary ? ZONE_WARNING : ZONE_ERROR; + + if(!state->c.domain || state->c.rr_count == 0) + return; + if (!state->c.rrset) { + rrset = region_alloc(state->database->region, sizeof(*rrset) +#ifdef PACKED_STRUCTS + + sizeof(rr_type*) * state->c.rr_count /* Add space for RRs. */ +#endif + ); + rrset->zone = state->zone; + rrset->rr_count = state->c.rr_count; +#ifndef PACKED_STRUCTS + rrset->rrs = region_alloc(state->database->region, + sizeof(rr_type*) * state->c.rr_count); +#endif + memcpy(rrset->rrs, state->c.rrs, state->c.rr_count * sizeof(rr_type*)); + switch (state->c.type) { + case TYPE_CNAME: + if (!domain_find_non_cname_rrset(state->c.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(state->c.domain, state->zone, TYPE_CNAME)) + break; + zone_log(parser, priority, "CNAME and other data at the same name"); + break; + } + /* Add it */ + domain_add_rrset(state->c.domain, rrset); + } else { +#ifndef PACKED_STRUCTS + struct rr **rrs; +#else + struct rrset *rrset_orig; +#endif + /* Add it... */ + rrset = state->c.rrset; +#ifndef PACKED_STRUCTS + rrs = rrset->rrs; + rrset->rrs = region_alloc_array( + state->database->region, rrset->rr_count + state->c.rr_count, sizeof(*rrs)); + memcpy(rrset->rrs, rrs, rrset->rr_count * sizeof(*rrs)); + region_recycle(state->database->region, rrs, rrset->rr_count * sizeof(*rrs)); +#else + rrset_orig = rrset; + rrset = region_alloc(state->database->region, + sizeof(rrset_type) + + (rrset_orig->rr_count+state->c.rr_count)*sizeof(rr_type*)); + memcpy(rrset, rrset_orig, + sizeof(rrset_type) + + rrset_orig->rr_count*sizeof(rr_type*)); + if(state->c.rrset_prev) + state->c.rrset_prev->next = rrset; + else state->c.domain->rrsets = rrset; + region_recycle(state->database->region, rrset_orig, + sizeof(rrset_type) + + rrset_orig->rr_count*sizeof(rr_type*)); +#endif /* PACKED_STRUCTS */ + memcpy(rrset->rrs + rrset->rr_count, state->c.rrs, state->c.rr_count * sizeof(rr_type*)); + rrset->rr_count += state->c.rr_count; + } + state->records += state->c.rr_count;; + /* Check we have SOA */ + if (state->c.rrs[0]->owner == state->zone->apex) + apex_rrset_checks(state->database, rrset, state->c.rrs[0]->owner); + + state->c.domain = NULL; + state->c.type = -1; + state->c.rrset = NULL; +#ifdef PACKED_STRUCTS + state->c.rrset_prev = NULL; +#endif + state->c.rr_count = 0; +} + int32_t zonec_accept( zone_parser_t *parser, const zone_name_t *owner, @@ -180,36 +212,53 @@ int32_t zonec_accept( void *user_data) { struct rr *rr; - struct rrset *rrset; - const struct dname *dname; + struct dname_buffer dname; struct domain *domain; struct buffer buffer; int priority; - union rdata_atom *rdatas; - ssize_t rdata_count; + int32_t code; + const struct nsd_type_descriptor *descriptor; struct zonec_state *state = (struct zonec_state *)user_data; - assert(state); - // 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 + /* 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); + if(!dname_make_buffered(&dname, (uint8_t*)owner->octets, 1)) { + zone_log(parser, ZONE_ERROR, "the owner cannot be converted"); + return ZONE_BAD_PARAMETER; + } + domain = domain_table_insert(state->domains, (void*)&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); + if (domain != state->c.domain || type != state->c.type + || state->c.rr_count >= (int)(sizeof(state->c.rrs) / sizeof(*state->c.rrs))){ + zonec_commit_rrset(parser, state); + state->c.domain = domain; + state->c.type = type; + state->c.rrset = NULL; +#ifdef PACKED_STRUCTS + state->c.rrset_prev = NULL; +#endif + } + descriptor = nsd_type_descriptor(type); + code = descriptor->read_rdata(state->domains, rdlength, &buffer, &rr); + if(code < 0) { + zone_log(parser, ZONE_ERROR, "the RR rdata fields are wrong for the type, %s %s %s", + dname_to_string((void*)&dname,0), + rrtype_to_string(type), + read_rdata_fail_str(code)); + if(code == TRUNCATED) + return ZONE_OUT_OF_MEMORY; + return ZONE_BAD_PARAMETER; + } + rr->owner = domain; + rr->type = type; + rr->klass = class; + rr->ttl = ttl; /* we have the zone already */ if (type == TYPE_SOA) { @@ -230,97 +279,67 @@ int32_t zonec_accept( 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(domain, state->zone, type); - if (!rrset) { - 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)); - + /* With the first RR for a RRset in this position in the zone file, + * find the RRset */ + if (state->c.rr_count == 0) { +#ifndef PACKED_STRUCTS + state->c.rrset = domain_find_rrset(state->c.domain, state->zone, state->c.type); +#else + state->c.rrset = domain_find_rrset_and_prev(state->c.domain, state->zone, state->c.type, &state->c.rrset_prev); +#endif + } + if (type == TYPE_RRSIG) + ; /* pass */ + else if (state->c.rrset && ttl != state->c.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, + state->c.rrset->rrs[0]->ttl, rrtype_to_string(type)); + + } else if (state->c.rr_count && ttl != state->c.rrs[0]->ttl) { + zone_log(parser, ZONE_WARNING, + "%s TTL %"PRIu32" does not match TTL %u of %s RRset", + domain_to_string(domain), ttl, + state->c.rrs[0]->ttl, rrtype_to_string(type)); + } + if (state->c.rrset || state->c.rr_count) { 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"); + zone_log(parser, priority, "multiple CNAMEs at the same name"); break; - case TYPE_RRSIG: - case TYPE_NXT: - case TYPE_SIG: - case TYPE_NSEC: - case TYPE_NSEC3: + case TYPE_DNAME: + zone_log(parser, priority, "multiple DNAMEs at the same name"); 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(domain, rrset); - } else { - 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 (int i = 0; i < rrset->rr_count; i++) { - if (zrdatacmp(type, rdatas, rdata_count, &rrset->rrs[i]) != 0) + } + if (state->c.rrset) { + /* Search for possible duplicates in existing RRset */ + for (int i = 0; i < state->c.rrset->rr_count; i++) { + if (!equal_rr_rdata(descriptor, rr, state->c.rrset->rrs[i])) 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(state->database->region, rdatas, sizeof(*rdatas) * rdata_count); - region_free_all(state->rr_region); + /* Lower the usage counter for domains in the rdata. */ + rr_lower_usage(state->database, rr); + region_recycle(state->database->region, rr, sizeof(*rr) + rr->rdlength); 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... */ - 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)); } - - 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 == state->zone->apex) - apex_rrset_checks(state->database, rrset, rr->owner); - - state->records++; - region_free_all(state->rr_region); + /* Search for possible duplicates in already batched RRs */ + for (int i = 0; i < state->c.rr_count; i++) { + if (!equal_rr_rdata(descriptor, rr, state->c.rrs[i])) + continue; + /* Discard the duplicates... */ + /* Lower the usage counter for domains in the rdata. */ + rr_lower_usage(state->database, rr); + region_recycle(state->database->region, rr, sizeof(*rr) + rr->rdlength); + return 0; + } + state->c.rrs[state->c.rr_count++] = rr; return 0; } @@ -416,12 +435,17 @@ zonec_read( 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; + state.c.domain = NULL; + state.c.type = -1; + state.c.rrset = NULL; +#ifdef PACKED_STRUCTS + state.c.rrset_prev = NULL; +#endif + state.c.rr_count = 0; origin = domain_dname(zone->apex); memset(&options, 0, sizeof(options)); options.origin.octets = dname_name(origin); @@ -436,9 +460,19 @@ zonec_read( /* Parse and process all RRs. */ if (zone_parse(&parser, &options, &buffers, zonefile, &state) != 0) { - region_destroy(state.rr_region); + /* With all socked up RRs, + * lower the usage counter for domains in the rdata. + */ + for (int i = 0; i < state.c.rr_count; i++) { + /* Lower the usage counter for domains in the rdata. */ + rr_lower_usage(database, state.c.rrs[i]); + region_recycle( database->region, state.c.rrs[i] + , sizeof(*state.c.rrs[i]) + + state.c.rrs[i]->rdlength); + } return state.errors; } + zonec_commit_rrset(&parser, &state); /* Check if zone file contained a correct SOA record */ if (!zone) { @@ -447,23 +481,22 @@ zonec_read( } 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) { + } 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)); + name, domain_to_string(zone->soa_rrset->rrs[0]->owner)); state.errors++; } if(!zone_is_slave(zone->opts) && !check_dname(zone)) state.errors++; - region_destroy(state.rr_region); return state.errors; } void apex_rrset_checks(namedb_type* db, rrset_type* rrset, domain_type* domain) { - uint32_t soa_minimum; + uint32_t soa_minimum = 0; unsigned i; zone_type* zone = rrset->zone; assert(domain == zone->apex); @@ -474,26 +507,34 @@ apex_rrset_checks(namedb_type* db, rrset /* BUG #103 add another soa with a tweaked ttl */ if(zone->soa_nx_rrset == 0) { zone->soa_nx_rrset = region_alloc(db->region, - sizeof(rrset_type)); + sizeof(rrset_type) +#ifdef PACKED_STRUCTS + + sizeof(rr_type*) +#endif + ); zone->soa_nx_rrset->rr_count = 1; zone->soa_nx_rrset->next = 0; zone->soa_nx_rrset->zone = zone; +#ifndef PACKED_STRUCTS zone->soa_nx_rrset->rrs = region_alloc(db->region, - sizeof(rr_type)); + sizeof(rr_type*)); +#endif + zone->soa_nx_rrset->rrs[0] = region_alloc(db->region, + sizeof(rr_type)+rrset->rrs[0]->rdlength); } - memcpy(zone->soa_nx_rrset->rrs, rrset->rrs, sizeof(rr_type)); + memcpy(zone->soa_nx_rrset->rrs[0], rrset->rrs[0], + sizeof(rr_type)+rrset->rrs[0]->rdlength); /* check the ttl and MINIMUM value and set accordingly */ - memcpy(&soa_minimum, rdata_atom_data(rrset->rrs->rdatas[6]), - rdata_atom_size(rrset->rrs->rdatas[6])); - if (rrset->rrs->ttl > ntohl(soa_minimum)) { - zone->soa_nx_rrset->rrs[0].ttl = ntohl(soa_minimum); + retrieve_soa_rdata_minttl(rrset->rrs[0], &soa_minimum); + if (rrset->rrs[0]->ttl > soa_minimum) { + zone->soa_nx_rrset->rrs[0]->ttl = soa_minimum; } } else if (rrset_rrtype(rrset) == TYPE_NS) { zone->ns_rrset = rrset; } else if (rrset_rrtype(rrset) == TYPE_RRSIG) { for (i = 0; i < rrset->rr_count; ++i) { - if(rr_rrsig_type_covered(&rrset->rrs[i])==TYPE_DNSKEY){ + if(rr_rrsig_type_covered(rrset->rrs[i])==TYPE_DNSKEY){ zone->is_secure = 1; break; } Index: usr.sbin/nsd/zonec.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/zonec.h,v diff -u -p -r1.15 zonec.h --- usr.sbin/nsd/zonec.h 6 Sep 2025 17:41:37 -0000 1.15 +++ usr.sbin/nsd/zonec.h 21 Sep 2026 16:29:09 -0000 @@ -30,6 +30,26 @@ #define DEFAULT_TTL 3600 +/* Some zones, such as DNS-SD zones, have RRsets with many RRs in them. + * By minimizing the need to reallocate the list of RRs in an RRset, + * we reduce memory fragmentation significantly for such zones. + * To this end, `domain`, `type`, `rrset`, `rrset_prev`, `rr_count` and + * `rrs` are used to commit the RRs within an RRset, that are grouped + * together in a zone file, to the database in batches. + */ +struct collect_rrs { + struct domain *domain; + int type; + struct rrset *rrset; +#ifdef PACKED_STRUCTS + struct rrset *rrset_prev; +#endif + int rr_count; + /* When the RRset is more than 256 RRs, the set will be committed in + * batches of 256 RRs (and resized if needed) */ + struct rr* rrs[256]; +}; + /* 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( Index: usr.sbin/nsd/dnstap/dnstap.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/dnstap/dnstap.c,v diff -u -p -r1.7 dnstap.c --- usr.sbin/nsd/dnstap/dnstap.c 3 Sep 2025 18:46:48 -0000 1.7 +++ usr.sbin/nsd/dnstap/dnstap.c 21 Sep 2026 16:29:09 -0000 @@ -353,27 +353,37 @@ dt_tls_writer_open(void* obj) } if(connect(dtw->fd, (struct sockaddr*)&addr, addrlen) < 0) { log_msg(LOG_ERR, "dnstap: connect failed: %s", strerror(errno)); + close(dtw->fd); + dtw->fd = -1; return fstrm_res_failure; } - dtw->connected = 1; /* setup SSL */ dtw->ssl = SSL_new(dtw->ctx); if(!dtw->ssl) { log_msg(LOG_ERR, "dnstap: SSL_new failed"); + close(dtw->fd); + dtw->fd = -1; return fstrm_res_failure; } SSL_set_connect_state(dtw->ssl); (void)SSL_set_mode(dtw->ssl, SSL_MODE_AUTO_RETRY); if(!SSL_set_fd(dtw->ssl, dtw->fd)) { log_msg(LOG_ERR, "dnstap: SSL_set_fd failed"); +res_failure: + if(dtw->ssl) + SSL_shutdown(dtw->ssl); + SSL_free(dtw->ssl); + dtw->ssl = NULL; + close(dtw->fd); + dtw->fd = -1; return fstrm_res_failure; } if(dtw->tls_server_name && dtw->tls_server_name[0]) { if(!SSL_set1_host(dtw->ssl, dtw->tls_server_name)) { log_msg(LOG_ERR, "dnstap: TLS setting of hostname %s failed to %s", dtw->tls_server_name, dtw->ip); - return fstrm_res_failure; + goto res_failure; } } @@ -387,14 +397,14 @@ dt_tls_writer_open(void* obj) if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) { if(r == SSL_ERROR_ZERO_RETURN) { log_msg(LOG_ERR, "dnstap: EOF on SSL_do_handshake"); - return fstrm_res_failure; + goto res_failure; } if(r == SSL_ERROR_SYSCALL) { log_msg(LOG_ERR, "dnstap: SSL_do_handshake failed: %s", strerror(errno)); - return fstrm_res_failure; + goto res_failure; } log_crypto_err("dnstap: SSL_do_handshake failed"); - return fstrm_res_failure; + goto res_failure; } /* wants to be called again */ } @@ -402,7 +412,7 @@ dt_tls_writer_open(void* obj) /* check authenticity of server */ if(SSL_get_verify_result(dtw->ssl) != X509_V_OK) { log_crypto_err("SSL verification failed"); - return fstrm_res_failure; + goto res_failure; } #ifdef HAVE_SSL_GET1_PEER_CERTIFICATE x = SSL_get1_peer_certificate(dtw->ssl); @@ -411,9 +421,10 @@ dt_tls_writer_open(void* obj) #endif if(!x) { log_crypto_err("Server presented no peer certificate"); - return fstrm_res_failure; + goto res_failure; } X509_free(x); + dtw->connected = 1; return fstrm_res_success; } Index: usr.sbin/nsd/dnstap/dnstap.m4 =================================================================== RCS file: /cvs/src/usr.sbin/nsd/dnstap/dnstap.m4,v diff -u -p -r1.5 dnstap.m4 --- usr.sbin/nsd/dnstap/dnstap.m4 6 Sep 2025 17:41:37 -0000 1.5 +++ usr.sbin/nsd/dnstap/dnstap.m4 21 Sep 2026 16:29:09 -0000 @@ -78,6 +78,8 @@ EOF ]) AC_SEARCH_LIBS([fstrm_iothr_init], [fstrm], [], AC_MSG_ERROR([[The fstrm library was not found. It is needed for dnstap, use --disable-dnstap, or install fstrm-devel]])) + AC_CHECK_FUNCS([fstrm_tcp_writer_options_init], [], + AC_MSG_ERROR([[The fstrm library >= 0.4 was not found. It is needed for dnstap, use --disable-dnstap, or install fstrm-devel]])) AC_SEARCH_LIBS([protobuf_c_message_pack], [protobuf-c], [], AC_MSG_ERROR([[The protobuf-c library was not found. It is needed for dnstap, use --disable-dnstap, or install protobuf-c]])) $2 Index: usr.sbin/nsd/doc/ChangeLog =================================================================== RCS file: /cvs/src/usr.sbin/nsd/doc/ChangeLog,v diff -u -p -r1.20 ChangeLog --- usr.sbin/nsd/doc/ChangeLog 6 Sep 2025 17:41:37 -0000 1.20 +++ usr.sbin/nsd/doc/ChangeLog 21 Sep 2026 16:29:09 -0000 @@ -1,5 +1,384 @@ +28 August 2026: Willem + - Fix client certificate checking by linking the tls-auth-name + requirement on an access control list item, to the other requirements + (IP address and/or TSIG key) on the same list item. + Thanks to voguemerry for the report + +24 August 2026: Willem + - Merge #502: An `udp-padding-port` configuration file option to enable + responding to the padding option on UDP, but on specific ports only. + This is convenient for setups where NSD is behind a frontend handling + the DNS over TLS. + - `--enable-multiple-catalog-zones` option to configure to enable + experimental support for multiple catalog consumer zones. + +19 August 2026: Willem + - Continue with 4.15.2 for development + +17 August 2026: Willem + - Fix for CVE-2026-18664: IP range access control restrictions are + bypassed for some unintended IP. + Thanks to Qifan Zhang, Palo Alto Networks for the report + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-18664.txt + - Fix for CVE-2026-18916: Any remote client can denial TCP service by + throttling the TCP receive window (down to 1). + Thanks to Qifan Zhang, Palo Alto Networks for the report + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-18916.txt + - Fix for CVE-2026-19401: Any remote client can denial UDP service by + sending a specifically crafted query with multiple DNS Cookie options. + Thanks to Qifan Zhang, Palo Alto Networks for the report + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-19401.txt + - Fix for CVE-2026-19538: Anyone with access to the proxy protocol port + over TCP or TLS can bypass BLOCKED access control items. + Thanks to Qifan Zhang, Palo Alto Networks for the report + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-19538.txt + +14 August 2026: Willem + - Fix to ignore NSEC3 records with malformed owner name. In depth fix + to clear key for NSEC3 tree collisions. And a fix to limit the + reenactment of failed reloads, that ordinarily marks transfers as + corrupt, so that for crashes it waits with that zone for retry. This + allows other zones to update in the meantime. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix nsd-control reconfig so that change of a secondary zone while it + is on the udp or tcp waiting list does not crash after that. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to reset the length of the incoming packet buffer after a failed + PROXYv2 header on UDP. It can cause an assertion failure on the next + packet. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix XoT transfer authentication to not fall back to the common name + if a DNS name is present in the certificate. This fixes the RFC6125 + conformance. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix dnstap over TLS certificate check for reconnection. Also clean up + file descriptor and SSL state on connection failures. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + +3 August 2026: Willem + - An `allow-proxy` configuration file option to specify allow access + control list for the PROXYv2 protocol port. + Thanks to Qifan Zhang, Palo Alto Networks, for the suggestion. + - Fix to prevent a configured primary from hampering loading its own + zone by providing a transfer with the OPT RR in the answer section + and the UDP Payload size (i.e. class) of the OPT RR set to something + else than 1 (IN). + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + +29 July 2026: Wouter + - Fix that when DNAME is limited due to the maximum CNAME chain + length, it copies the final DNAME with its CNAME into the + packet. Also fix unit test for limit on CNAME chain length. + +16 July 2026: Willem + - Merge #500: Support for the "oots" SVCB Service Parameter Key + +15 July 2026: Willem + - Merge #498: Support for the HHIT and BRID RR types + - Merge #499: Support for the "docpath" and "pvd" + SVCB Service Parameter Keys + +14 July 2026: Willem + - Fix that IXFR queries with overly large SOA records do not cause an + assertion failure in the server child process. The malformed SOA + record is rejected with FORMERR. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that notify ahead of the zone serial does not cause perpetual + retransfer. After one round, and after another retry, it gives up, + and forgets the notified serial number. This allows the primary a + moment to retrieve the new zone version. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that AXFR out for a zone with an oversize record does not loop. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that IXFR out for a zone with an oversize record does not loop. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix serve childs crashing when queried for a very long CNAME chain + on ASAN builds. The maximum CNAME chain in responses is set to 18. + The value can be configured with the --with-max-cname-chain=number + option to configure. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to have failed zone transfers retry after a time, instead of in a + loop, for transfers with failed contents. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that an increase verbosity level is needed to warn about unfound + RRs to be deleted (from IXFR). Verbosity 2 for unfound RRsets and + unfound RDATA. Verbosity 3 to log which RR in the existing set does + not match. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to ignore NSEC3s with wrong non-base32hex chars in owner label. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix Y2038 bug, where the server process would fail with undefined + behaviour, if it was built with address sanitizer undefined checks + enabled, for the timestamp in EDNS cookies. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to not let `nsd-control zonestatus` crash the xfrd processes + when configured with multiple catalog consumers. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + +13 July 2026: Wouter + - Merge #496 from maertsen: Fix allow-query syntax in catalog + documentation. + +8 July 2026: Willem + - Merge #494: RFC 7830 EDNS0 padding option support with DoT + With padding suggestions from RFC 8467 for responses + Thanks kdrenard for the suggestion + +7 July 2026: Willem + - Merge the NSD 4.15.0 release into master. + - Continue with 4.15.1 for development + +7 July 2026: Wouter + - (part of 4.15.1): + - Merge #495 from ruuda: Include inttypes.h from metrics.c. + +29 June 2026: Jannik + - Merge #483 from ruuda: Improve Prometheus metrics: Move zonestats from + metric name to label + +2g June 2026: Willem + - Fix nsd-control assoc_tsig, if that interrupts a zone transfer in + progress, to not crash. It restarts the transfer from the primary. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix nsd-control del_tsig, if that interrupts a zone transfer in + progress, to not crash. It does not delete the key, if in use. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix catalog producer zone with long name, so that it does not crash + on that. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix catalog consumer zone with long name for member unique label that + is long, so that it does not crash on that. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that non-IN-class records cause a zone transfer to be rejected. + Also such records are not added from a transfer. This stops an + assertion failure. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to disallow a SOA record in the middle of an AXFR. This stops an + assertion failure. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to set zone is_secure to false when IXFR removes RRSIG DNSKEY. + This stops an assertion failure. Also fix soa and ns rrset change in + IXFR when packed rrsets are disabled. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to handle NSEC3 zones without space for hashes. Zones with a apex + domain name length >= 223 bytes, that have a NSEC3PARAM must not be + prehashed, since the hashed owner name would not fit. + Thanks Qifan Zhang, Palo Alto Networks, for the report + - Fix to add hardening to zone_ixfr_remove_oldest, for IXFR processing. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix for xfrd crash with too short response to a UDP SOA query + Only for release builds and only when configured for XFR over UDP + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that out-of-zone records are skipped from zone transfers. + Otherwise such records could stick around after zone deletion and + cause failures for DS queries. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + +25 June 2026: Willem + - Merge the NSD version 4.14.3 security release branch into master. + - Continue with 4.14.4 for development + +24 June 2026: Willem + - Fix for CVE-2026-12244: A specially crafted SVCB RR can cause a heap + overflow of up to 65509 attacker controlled bytes. + Thanks to Qifan Zhang, Palo Alto Networks for the report + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-12244.txt + - Fix for CVE-2026-12245: If NSD is configured with DNS over TLS, a + client that performs a TLS action, closing the connection early, + causes a crash and restart of the server process. An attacker can + keep all children in a crash-restart loop denying DoT service. + Thanks to Qifan Zhang, Palo Alto Networks for the report. + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-12245.txt + - Fix for CVE-2026-12246: The RR type APL rdata address, if too large, + causes out of bounds write on the stack, when the zonefile is written + out. Thanks to Qifan Zhang from Palo Alto Networks, Haruki Oyama from + Waseda University and zhangph for the report. + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-12246.txt + - Fix for CVE-2026-12490: Secondaries authenticated by a client + certificate to transfer a zone over TLS, can bypass verification by + transferring over TCP. + Thanks to Qifan Zhang, Palo Alto Networks for the report. + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-12490.txt + +22 Jun 2026: Willem + - More robust removing of RRs from an IXFR processing. + Thanks zhangph for reporting this issue + +22 Jun 2026: Wouter + - Fix unit test for stopmany for process role logs. + +22 Jun 2026: Willem + - Fix that wrong buffer position in IXFR for the first SOA causes the + storage to retrieve wrong information. Later data would overwrite it + so it did not cause observable trouble. Thanks to Tristan Madani + (@TristanInSec) from Talence Security for the report. + +22 Jun 2026: Willem + - Fix notify and zone transfer processing for malformed SOA records, + with a short rdata content. It stops an assertion failure. Thanks to + Tristan Madani (@TristanInSec) from Talence Security for the report. + +19 Jun 2026: Wouter + - Fix to update github ci actions/checkout to v7. + +18 Jun 2026: Willem + - Fix overflow for NSEC3 zones with 255-octet name + Thanks to Haruki Oyama (Waseda University) for the report, and + Qifan Zhang, Palo Alto Networks, for also reporting this issue. + +15 Jun 2026: Willem + - Fix print of NXT RR without bitmap + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + +15 Jun 2026: Willem + - Fix to not fail on NSEC3 records with a bad owner name. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + +15 Jun 2026: Willem + - Fix print of malformed HIP records. + Thanks to Qifan Zhang, Palo Alto Networks, for the report, and + Haruki Oyama (Waseda University) for also reporting this issue. + +15 Jun 2026: Willem + - Fix notify relay ipc to check for large size. + This stops desync of the internal notify pipe. + Thanks to Qifan Zhang, Palo Alto Networks for the report. + +15 Jun 2026: Willem + - Fix PROXYv2 header read and consume, it checks the header size. + Thanks to Qifan Zhang, Palo Alto Networks for the report. + +12 May 2026: Jannik + - Merge #484 from orlitzky: OpenRC: fix network deps and support both + supervisors + +24 April 2026: Jannik + - Merge #481 from jaredmauch: Fix pedantic/CodeQL warning in sources + +14 April 2026: Jannik + - Fix XDP cleanup code being executed even if xdp is not configured + +25 March 2026: Wouter + - Fix #478: Feature request: reduce syslog noise from frequent + read-only control commands (e.g. stats_noreset). It logs the + verbosity command always, and others at 2 and higher. + +19 March 2026: Jannik + - Continue with 4.14.3 for development + +11 March 2026: Wouter + - Fix in IXFR processing, to commit the collected RRs before + deletions. + +6 March 2026: Wouter + - Merge #477: Improve ignored old serial log message. + +24 February 2026: Willem + - The master branch continues with version 4.14.2 in development + +23 February 2026: Wouter + - Fix systemd signalling so that it does not reload for too long. + The reload is not signalled to systemd, so that long operations + can complete, without systemd acting on a timer to stop them. + +20 February 2026: Wouter + - Fix man page for ip-address, add text about process numbers, + bindtodevice and setfib. + +16 February 2026: Willem + - Merge #472: Reduce memory usage with zones with RRsets + consisting of many RRs. + +13 February 2026: Wouter + - Fix to escape slashes when they appear in the zone name for a + pattern zonefile that is created. Also for per zone statistics. + +2 February 2026: Wouter + - Fix metrics to clear server variable after close and log error + on allocation failure. + +15 January 2026: Wouter + - Fix #475 info: axfr for domain from not-verified. + +7 January 2026: Wouter + - Fix that non normalized NSEC next owner names are preserved. + - Fix to preserve case in literal dnames in RR types RRSIG, + IPSECKEY, TALINK, DSYNC and AMTRELAY. + - Fix for #474: Fix metrics name for zone statistics for + the queries_total to have disallowed characters changed + to underscores. + - Fix to silence restricted userns check in test script. + +6 January 2026: Wouter + - Fix #474: metrics output with zone statistics to change + disallowed characters in metric names to underscores. + +21 December 2025: Willem + - Fix rr-test.tdir so AMTRELAY relay field is "." with type 0 + - Fix checkconf.tdir test to anticipate default values for + send-buffer-size and receive-buffer-size when configured with 0 + - skip dns-cookies.tdir test with restricted unpriviledged userns + +1 December 2025: Jannik + - The master branch continues with version 4.14.1 in development + - Merge #469 from jschlyter: Add container build files + - Merge #470 from jschlyter: Update path to default container + configuration and entrypoint + +1 December 2025: Wouter + - Fix to note DSYNC RFC9859 reference. + - Fix to note reference for NXNAME in comment. + +27 November 2025: Jannik + - Merge #444: Refactor RDATA storage to reduce memory footprint + +12 November 2025: Willem + - Merge #466: Do not delete nodes from non-existent zone's NSEC3 hash + trees + +6 November 2025: Wouter + - Update acx_nlnetlabs.m4 to version 51, with nonstring unknown + attribute warning fix. + +3 November 2025: Wouter + - For #137: Change default for the listen TCP backlog to -1 on + BSDs and Linux. + +2 October 2025: Wouter + - Fix #137: Adds tcp-listen-queue: number config option to set + the TCP backlog. + +29 September 2025: Wouter + - Update in acx_nlnetlabs.m4 to version 49. + - Update in acx_nlnetlabs.m4 to version 50, with cache value for + malloc function check. + +17 September 2025: Wouter + - Fix to log more details when send-buffer-size or receive-buffer-size + is not granted, on verbosity level 2. + +15 September 2025: Jannik + - Fix confusing report for default send and receive buffer-size by + nsd-checkconf + +11 September 2025: Jannik + - Merge #460: Add XDP_OBJ fixing link errors for XDP. + - Fix XDP build error with --enable-checking + - Resolve warnings about mixed declaration and code and unused variable + +11 September 2025: Wouter + - Merge #459: Check for libfstrm version >= 0.4. + - For #459: Add configure check for fstrm_tcp_writer_options_init + in addition to the check for fstrm_iothr_init. + +8 September 2025: Wouter + - Fix empty debug statement body in catalog consumer zone process. + 03 September 2025: Jannik - Fix zonestatfd check + - The main branch continues with version 4.13.1 in development. + +3 September 2025: Wouter + - Set code repository configure for version 4.13.1 in development. 18 August 2025: Willem - Merge #455: --with-dbdir option for configure to set the base Index: usr.sbin/nsd/doc/README =================================================================== RCS file: /cvs/src/usr.sbin/nsd/doc/README,v diff -u -p -r1.10 README --- usr.sbin/nsd/doc/README 6 Sep 2025 17:41:37 -0000 1.10 +++ usr.sbin/nsd/doc/README 21 Sep 2026 16:29:09 -0000 @@ -21,7 +21,7 @@ 1.0 Introduction -This is NSD Name Server Daemon (NSD) version 4.13.0. +This is NSD Name Server Daemon (NSD) version 4.15.2. 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.13.0.tar.gz +Step 1: Unpack the source with gtar -xzvf nsd-4.15.2.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.13.0.tar.gz +$ gtar -xzvf nsd-4.15.2.tar.gz -will unpack the source into the ./nsd-4.13.0 directory... +will unpack the source into the ./nsd-4.15.2 directory... 2.2 Configuring NSD @@ -926,4 +926,4 @@ larger and regular donations please cont see http://www.nlnetlabs.nl/labs/contributors/. -$Id: README,v 1.10 2025/09/06 17:41:37 sthen Exp $ +$Id: README,v 1.14 2026/09/21 08:41:19 sthen Exp $ Index: usr.sbin/nsd/doc/RELNOTES =================================================================== RCS file: /cvs/src/usr.sbin/nsd/doc/RELNOTES,v diff -u -p -r1.18 RELNOTES --- usr.sbin/nsd/doc/RELNOTES 6 Sep 2025 17:41:37 -0000 1.18 +++ usr.sbin/nsd/doc/RELNOTES 21 Sep 2026 16:29:10 -0000 @@ -1,5 +1,287 @@ NSD RELEASE NOTES +4.15.2 +================ +FEATURES: + - Merge #494: RFC 7830 EDNS0 padding option support with DoT + With padding suggestions from RFC 8467 for responses + Thanks kdrenard for the suggestion + - Merge #498: Support for the HHIT and BRID RR types + - Merge #499: Support for the "docpath" and "pvd" + SVCB Service Parameter Keys + - Merge #500: Support for the "oots" SVCB Service Parameter Key + - An `allow-proxy` configuration file option to specify allow access + control list for the PROXYv2 protocol port. + Thanks to Qifan Zhang, Palo Alto Networks, for the suggestion. + - Merge #502: An `udp-padding-port` configuration file option to enable + responding to the padding option on UDP, but on specific ports only. + This is convenient for setups where NSD is behind a frontend handling + the DNS over TLS. + - `--enable-multiple-catalog-zones` option to configure to enable + experimental support for multiple catalog consumer zones. + +BUG FIXES: + - Merge #495 from ruuda: Include inttypes.h from metrics.c. + - Merge #496 from maertsen: Fix allow-query syntax in catalog + documentation. + - Fix that IXFR queries with overly large SOA records do not cause an + assertion failure in the server child process. The malformed SOA + record is rejected with FORMERR. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that notify ahead of the zone serial does not cause perpetual + retransfer. After one round, and after another retry, it gives up, + and forgets the notified serial number. This allows the primary a + moment to retrieve the new zone version. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that AXFR out for a zone with an oversize record does not loop. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that IXFR out for a zone with an oversize record does not loop. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix serve childs crashing when queried for a very long CNAME chain + on ASAN builds. The maximum CNAME chain in responses is set to 18. + The value can be configured with the --with-max-cname-chain=number + option to configure. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to have failed zone transfers retry after a time, instead of in a + loop, for transfers with failed contents. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that an increase verbosity level is needed to warn about unfound + RRs to be deleted (from IXFR). Verbosity 2 for unfound RRsets and + unfound RDATA. Verbosity 3 to log which RR in the existing set does + not match. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to ignore NSEC3s with wrong non-base32hex chars in owner label. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix Y2038 bug, where the server process would fail with undefined + behaviour, if it was built with address sanitizer undefined checks + enabled, for the timestamp in EDNS cookies. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to not let `nsd-control zonestatus` crash the xfrd processes + when configured with multiple catalog consumers. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to prevent a configured primary from hampering loading its own + zone by providing a transfer with the OPT RR in the answer section + and the UDP Payload size (i.e. class) of the OPT RR set to something + else than 1 (IN). + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to ignore NSEC3 records with malformed owner name. In depth fix + to clear key for NSEC3 tree collisions. And a fix to limit the + reenactment of failed reloads, that ordinarily marks transfers as + corrupt, so that for crashes it waits with that zone for retry. This + allows other zones to update in the meantime. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix nsd-control reconfig so that change of a secondary zone while it + is on the udp or tcp waiting list does not crash after that. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to reset the length of the incoming packet buffer after a failed + PROXYv2 header on UDP. It can cause an assertion failure on the next + packet. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix XoT transfer authentication to not fall back to the common name + if a DNS name is present in the certificate. This fixes the RFC6125 + conformance. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix dnstap over TLS certificate check for reconnection. Also clean up + file descriptor and SSL state on connection failures. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix client certificate checking by linking the tls-auth-name + requirement on an access control list item, to the other requirements + (IP address and/or TSIG key) on the same list item. + Thanks to voguemerry for the report + +4.15.1 +================ +FEATURES: +BUG FIXES: + - Fix for CVE-2026-18664: IP range access control restrictions are + bypassed for some unintended IP. + Thanks to Qifan Zhang, Palo Alto Networks for the report + Thanks to Claude and Ada Logics for the report + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-18664.txt + - Fix for CVE-2026-18916: Any remote client can denial TCP service by + throttling the TCP receive window (down to 1). + Thanks to Akhil Koul (https://github.com/akoul) for the report + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-18916.txt + - Fix for CVE-2026-19401: Any remote client can denial UDP service by + sending a specifically crafted query with multiple DNS Cookie options. + Thanks to Qifan Zhang, Palo Alto Networks for the report + Thanks to afldl for the report + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-19401.txt + - Fix for CVE-2026-19538: Anyone with access to the proxy protocol port + over TCP or TLS can bypass BLOCKED access control items. + Thanks to Qifan Zhang, Palo Alto Networks for the report + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-19538.txt + +4.15.0 +================ +FEATURES: + - Merge #483 from ruuda: Improve Prometheus metrics: Move zonestats from + metric name to label +BUG FIXES: + - Fix #478: Feature request: reduce syslog noise from frequent + read-only control commands (e.g. stats_noreset). It logs the + verbosity command always, and others at 2 and higher. + - Fix XDP cleanup code being executed even if xdp is not configured + - Merge #481 from jaredmauch: Fix pedantic/CodeQL warning in sources + - Merge #484 from orlitzky: OpenRC: fix network deps and support both + supervisors + - Fix PROXYv2 header read and consume, it checks the header size. + Thanks to Qifan Zhang, Palo Alto Networks for the report. + - Fix notify relay ipc to check for large size. + This stops desync of the internal notify pipe. + Thanks to Qifan Zhang, Palo Alto Networks for the report. + - Fix print of malformed HIP records. + Thanks to Qifan Zhang, Palo Alto Networks, for the report, and + Haruki Oyama (Waseda University) for also reporting this issue. + - Fix to not fail on NSEC3 records with a bad owner name. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix print of NXT RR without bitmap + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix overflow for NSEC3 zones with 255-octet name + Thanks to Haruki Oyama (Waseda University) for the report, and + Qifan Zhang, Palo Alto Networks, for also reporting this issue. + - Fix to update github ci actions/checkout to v7. + - Fix notify and zone transfer processing for malformed SOA records, + with a short rdata content. It stops an assertion failure. Thanks to + Tristan Madani (@TristanInSec) from Talence Security for the report. + - Fix that wrong buffer position in IXFR for the first SOA causes the + storage to retrieve wrong information. Later data would overwrite it + so it did not cause observable trouble. Thanks to Tristan Madani + (@TristanInSec) from Talence Security for the report. + - More robust removing of RRs from an IXFR processing. + Thanks zhangph for reporting this issue + - Fix unit test for stopmany for process role logs. + - Fix nsd-control assoc_tsig, if that interrupts a zone transfer in + progress, to not crash. It restarts the transfer from the primary. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix nsd-control del_tsig, if that interrupts a zone transfer in + progress, to not crash. It does not delete the key, if in use. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix catalog producer zone with long name, so that it does not crash + on that. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix catalog consumer zone with long name for member unique label that + is long, so that it does not crash on that. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that non-IN-class records cause a zone transfer to be rejected. + Also such records are not added from a transfer. This stops an + assertion failure. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to disallow a SOA record in the middle of an AXFR. This stops an + assertion failure. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to set zone is_secure to false when IXFR removes RRSIG DNSKEY. + This stops an assertion failure. Also fix soa and ns rrset change in + IXFR when packed rrsets are disabled. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix to handle NSEC3 zones without space for hashes. Zones with a apex + domain name length >= 223 bytes, that have a NSEC3PARAM must not be + prehashed, since the hashed owner name would not fit. + Thanks Qifan Zhang, Palo Alto Networks, for the report + - Fix to add hardening to zone_ixfr_remove_oldest, for IXFR processing. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix for xfrd crash with too short response to a UDP SOA query + Only for release builds and only when configured for XFR over UDP + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that out-of-zone records are skipped from zone transfers. + Otherwise such records could stick around after zone deletion and + cause failures for DS queries. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. + +4.14.3 +================ +FEATURES: +BUG FIXES: + - Fix for CVE-2026-12244: A specially crafted SVCB RR can cause a heap + overflow of up to 65509 attacker controlled bytes. + Thanks to Qifan Zhang, Palo Alto Networks for the report + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-12244.txt + - Fix for CVE-2026-12245: If NSD is configured with DNS over TLS, a + client that performs a TLS action, closing the connection early, + causes a crash and restart of the server process. An attacker can + keep all children in a crash-restart loop denying DoT service. + Thanks to Qifan Zhang, Palo Alto Networks for the report. + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-12245.txt + - Fix for CVE-2026-12246: The RR type APL rdata address, if too large, + causes out of bounds write on the stack, when the zonefile is written + out. Thanks to Qifan Zhang from Palo Alto Networks, Haruki Oyama from + Waseda University and zhangph for the report. + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-12246.txt + - Fix for CVE-2026-12490: Secondaries authenticated by a client + certificate to transfer a zone over TLS, can bypass verification by + transferring over TCP. + Thanks to Qifan Zhang, Palo Alto Networks for the report. + https://www.nlnetlabs.nl/downloads/nsd/CVE-2026-12490.txt + +4.14.2 +================ +FEATURES: +BUG FIXES: + - Merge #477: Improve ignored old serial log message. + - Fix in IXFR processing, to commit the collected RRs before + deletions. + +4.14.1 +================ +FEATURES: + - Merge #469 from jschlyter: Add container build files +BUG FIXES: + - Fix to note DSYNC RFC9859 reference. + - Fix to note reference for NXNAME in comment. + - Merge #470 from jschlyter: Update path to default container + configuration and entrypoint + - Fix rr-test.tdir so AMTRELAY relay field is "." with type 0 + - Fix checkconf.tdir test to anticipate default values for + send-buffer-size and receive-buffer-size when configured with 0 + - skip dns-cookies.tdir test with restricted unpriviledged userns + - Fix #474: metrics output with zone statistics to change + disallowed characters in metric names to underscores. + - Fix that non normalized NSEC next owner names are preserved. + - Fix to preserve case in literal dnames in RR types RRSIG, + IPSECKEY, TALINK, DSYNC and AMTRELAY. + - Fix for #474: Fix metrics name for zone statistics for + the queries_total to have disallowed characters changed + to underscores. + - Fix to silence restricted userns check in test script. + - Fix #475 info: axfr for domain from not-verified. + - Fix metrics to clear server variable after close and log error + on allocation failure. + - Fix to escape slashes when they appear in the zone name for a + pattern zonefile that is created. Also for per zone statistics. + - Merge #472: Reduce memory usage with zones with RRsets + consisting of many RRs. + - Fix man page for ip-address, add text about process numbers, + bindtodevice and setfib. + - Fix systemd signalling so that it does not reload for too long. + The reload is not signalled to systemd, so that long operations + can complete, without systemd acting on a timer to stop them. + +4.14.0 +================ +FEATURES: + - Fix #137: Adds tcp-listen-queue: number config option to set + the TCP backlog. And the default for the listen TCP backlog is + set to -1 on BSDs and Linux. + - Merge #444: Refactor RDATA storage to reduce memory footprint +BUG FIXES: + - Fix empty debug statement body in catalog consumer zone process. + - Merge #459: Check for libfstrm version >= 0.4. + - For #459: Add configure check for fstrm_tcp_writer_options_init + in addition to the check for fstrm_iothr_init. + - Merge #460: Add XDP_OBJ fixing link errors for XDP. + - Fix XDP build error with --enable-checking + - Resolve warnings about mixed declaration and code and unused variable + - Fix confusing report for default send and receive buffer-size by + nsd-checkconf + - Fix to log more details when send-buffer-size or receive-buffer-size + is not granted, on verbosity level 2. + - Update in acx_nlnetlabs.m4 to version 49. + - Update in acx_nlnetlabs.m4 to version 50, with cache value for + malloc function check. + - Update acx_nlnetlabs.m4 to version 51, with nonstring unknown + attribute warning fix. + - Merge #466: Do not delete nodes from non-existent zone's NSEC3 hash + trees + 4.13.0 ================ FEATURES: @@ -68,6 +350,9 @@ BUG FIXES: - Update simdzone with support for --enable-pie. - Merge #454 from jaredmauch: handle rare case but seen in production where data->query is NULL. + - Fix zonestatfd check + - Fix code analyzer warning, and bail out of handle_tcp_writing + and handle_tls_writing early when data->query is NULL. 4.12.0 ================ Index: usr.sbin/nsd/simdzone/CHANGELOG.md =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/CHANGELOG.md,v diff -u -p -r1.1.1.2 CHANGELOG.md --- usr.sbin/nsd/simdzone/CHANGELOG.md 6 Sep 2025 17:38:34 -0000 1.1.1.2 +++ usr.sbin/nsd/simdzone/CHANGELOG.md 21 Sep 2026 16:29:10 -0000 @@ -5,6 +5,42 @@ All notable changes to simdzone will be 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.6] - 2026-09-02 + +### Added + +- Support for the HHIT and BRID RR types. +- Support for the "docpath", "pvd" and "oots" SVCB Service Parameters + +### Fixed + +- TXT records with more than 6465 rdata elements, crashes simdzone parsing. + Thanks to Qifan Zhang, Palo Alto Networks, for the report. +- Fix unit test implicit conversion change of signedness warning. +- Fix unit test delimiters_overflow_txt to add block size padding for simd + operations. + +## [0.2.5] - 2026-07-07 + +### Added + +- Add riscv fallback detection (#261) + +### Fixed + +- Fix to parse SVCB and HTTPS svcparam ech=0. + +## [0.2.4] - 2025-12-04 + +### Added + +### Fixed + +- Correct lengths for GOST R 34.10-2012 and SM3 delegation signer (DS) digest + algorithms +- Require the AMTRELAY relay field to be `.` for the no gateway relay type as + specified by RFC 8777 (#257) + ## [0.2.3] - 2025-09-03 ### Added Index: usr.sbin/nsd/simdzone/README.md =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/README.md,v diff -u -p -r1.1.1.1 README.md --- usr.sbin/nsd/simdzone/README.md 3 Sep 2025 18:44:23 -0000 1.1.1.1 +++ usr.sbin/nsd/simdzone/README.md 21 Sep 2026 16:29:10 -0000 @@ -1,6 +1,7 @@ ![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) +[![Discuss on Discourse](https://img.shields.io/badge/Discourse-NLnet_Labs-orange?logo=Discourse)](https://community.nlnetlabs.nl/c/dns-libraries-tools/12) +[![Mastodon Follow](https://img.shields.io/mastodon/follow/114692612288811644?domain=social.nlnetlabs.nl&style=social)](https://social.nlnetlabs.nl/@nlnetlabs) # simdzone: Parsing zone files really fast @@ -33,7 +34,7 @@ instructions for parsing structured text 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. +> Currently SSE4.2 and AVX2 are supported. RISC-V and other non-x86_64 targets use the fallback kernel until a dedicated SIMD backend is added. > simdzone copies some code from the [simdjson][simdjson] project, with > permission to use and distribute it under the terms of Index: usr.sbin/nsd/simdzone/config.guess =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/config.guess,v diff -u -p -r1.1.1.2 config.guess --- usr.sbin/nsd/simdzone/config.guess 6 Sep 2025 17:38:34 -0000 1.1.1.2 +++ usr.sbin/nsd/simdzone/config.guess 21 Sep 2026 16:29:10 -0000 @@ -1,10 +1,10 @@ #! /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-08-22' +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. @@ -155,9 +155,6 @@ Linux|GNU|GNU/*) set_cc_for_build cat <<-EOF > "$dummy.c" - #if defined(__ANDROID__) - LIBC=android - #else #include #if defined(__UCLIBC__) LIBC=uclibc @@ -172,7 +169,6 @@ Linux|GNU|GNU/*) LIBC=musl #endif #endif - #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" @@ -463,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 ;; @@ -908,7 +904,7 @@ EOF fi ;; *:FreeBSD:*:*) - UNAME_PROCESSOR=`uname -p` + UNAME_PROCESSOR=`/usr/bin/uname -p` case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; @@ -970,37 +966,11 @@ 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 ;; aarch64: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 __ARM_EABI__ - #ifdef __ARM_PCS_VFP - ABI=eabihf - #else - ABI=eabi - #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 - eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; - esac - fi - GUESS=$CPU-unknown-linux-$LIBCABI + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be @@ -1066,16 +1036,7 @@ EOF k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; - kvx:Linux:*:*) - GUESS=$UNAME_MACHINE-unknown-linux-$LIBC - ;; - kvx:cos:*:*) - GUESS=$UNAME_MACHINE-unknown-cos - ;; - kvx:mbr:*:*) - GUESS=$UNAME_MACHINE-unknown-mbr - ;; - loongarch32:Linux:*:* | loongarch64:Linux:*:*) + loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) @@ -1190,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 @@ -1230,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 ;; @@ -1371,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 GUESS=i586-unisys-sysv4 ;; @@ -1417,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: usr.sbin/nsd/simdzone/config.h.in =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/config.h.in,v diff -u -p -r1.1.1.2 config.h.in --- usr.sbin/nsd/simdzone/config.h.in 6 Sep 2025 17:38:34 -0000 1.1.1.2 +++ usr.sbin/nsd/simdzone/config.h.in 21 Sep 2026 16:29:10 -0000 @@ -1,14 +1,14 @@ /* config.h.in. Generated from configure.ac by autoheader. */ -/* Define to 1 if you have the declaration of 'bswap16', and to 0 if you +/* 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 +/* 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 +/* Define to 1 if you have the declaration of `bswap64', and to 0 if you don't. */ #undef HAVE_DECL_BSWAP64 @@ -21,7 +21,7 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H -/* Define to 1 if you have the 'realpath' function. */ +/* Define to 1 if you have the `realpath' function. */ #undef HAVE_REALPATH /* Define to 1 if you have the header file. */ @@ -72,7 +72,7 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION -/* Define to 1 if all of the C89 standard headers exist (not just the ones +/* 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 Index: usr.sbin/nsd/simdzone/config.sub =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/config.sub,v diff -u -p -r1.1.1.2 config.sub --- usr.sbin/nsd/simdzone/config.sub 6 Sep 2025 17:38:34 -0000 1.1.1.2 +++ usr.sbin/nsd/simdzone/config.sub 21 Sep 2026 16:29:10 -0000 @@ -1,10 +1,10 @@ #! /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-09-19' +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 &2 + echo Invalid configuration \`"$1"\': more than four components >&2 exit 1 ;; *-*-*-*) @@ -145,8 +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-* \ - | windows-* ) + | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 basic_os=$maybe_os ;; @@ -944,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) @@ -1076,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-*) @@ -1181,7 +1180,7 @@ case $cpu-$vendor in case $cpu in 1750a | 580 \ | a29k \ - | aarch64 | aarch64_be | aarch64c | arm64ec \ + | aarch64 | aarch64_be \ | abacus \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \ @@ -1200,23 +1199,45 @@ case $cpu-$vendor in | d10v | d30v | dlx | dsp16xx \ | e2k | elxsi | epiphany \ | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \ - | javascript \ | h8300 | h8500 \ | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i*86 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ - | kvx \ | 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 \ | m88110 | m88k | maxq | mb | mcore | mep | metag \ | microblaze | microblazeel \ - | mips* \ + | 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 \ @@ -1264,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 @@ -1285,12 +1306,11 @@ esac # Decode manufacturer-specific aliases for certain operating systems. -if test x"$basic_os" != x +if test x$basic_os != x then # First recognize some ad-hoc cases, or perhaps split kernel-os, or else just # set os. -obj= case $basic_os in gnu/linux*) kernel=linux @@ -1321,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 @@ -1490,16 +1506,10 @@ case $os in os=eabi ;; *) - os= - obj=elf + os=elf ;; esac ;; - aout* | coff* | elf* | pe*) - # These are machine code file formats, not OSes - obj=$os - os= - ;; *) # No normalization, but not necessarily accepted, that comes below. ;; @@ -1518,15 +1528,12 @@ else # system, and we'll never get to this point. kernel= -obj= case $cpu-$vendor in score-*) - os= - obj=elf + os=elf ;; spu-*) - os= - obj=elf + os=elf ;; *-acorn) os=riscix1.2 @@ -1536,35 +1543,28 @@ case $cpu-$vendor in os=gnu ;; arm*-semi) - os= - obj=aout + os=aout ;; c4x-* | tic4x-*) - os= - obj=coff + os=coff ;; c8051-*) - os= - obj=elf + os=elf ;; clipper-intergraph) os=clix ;; hexagon-*) - os= - obj=elf + os=elf ;; tic54x-*) - os= - obj=coff + os=coff ;; tic55x-*) - os= - obj=coff + os=coff ;; tic6x-*) - os= - obj=coff + os=coff ;; # This must come before the *-dec entry. pdp10-*) @@ -1586,24 +1586,19 @@ case $cpu-$vendor in os=sunos3 ;; m68*-cisco) - os= - obj=aout + os=aout ;; mep-*) - os= - obj=elf + os=elf ;; mips*-cisco) - os= - obj=elf + os=elf ;; mips*-*) - os= - obj=elf + os=elf ;; or32-*) - os= - obj=coff + os=coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=sysv3 @@ -1612,8 +1607,7 @@ case $cpu-$vendor in os=sunos4.1.1 ;; pru-*) - os= - obj=elf + os=elf ;; *-be) os=beos @@ -1694,12 +1688,10 @@ case $cpu-$vendor in os=uxpv ;; *-rom68k) - os= - obj=coff + os=coff ;; *-*bug) - os= - obj=coff + os=coff ;; *-apple) os=macos @@ -1717,8 +1709,7 @@ esac fi -# Now, validate our (potentially fixed-up) individual pieces (OS, OBJ). - +# 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*) @@ -1729,9 +1720,6 @@ case $os in # VxWorks passes extra cpu info in the 4th filed. simlinux | simwindows | spe) ;; - # See `case $cpu-$os` validation below - ghcjs) - ;; # Now accept the basic system types. # The portable systems comes first. # Each alternative MUST end in a * to match a version number. @@ -1740,7 +1728,7 @@ case $os in | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ | hiux* | abug | nacl* | netware* | windows* \ - | os9* | macos* | osx* | ios* | tvos* | watchos* \ + | os9* | macos* | osx* | ios* \ | mpw* | magic* | mmixware* | mon960* | lnews* \ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ @@ -1749,11 +1737,11 @@ case $os in | mirbsd* | netbsd* | dicos* | openedition* | ose* \ | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \ | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ - | bosx* | nextstep* | cxux* | oabi* \ - | ptx* | ecoff* | winnt* | domain* | vsta* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ | udi* | lites* | ieee* | go32* | aux* | hcos* \ | chorusrdb* | cegcc* | glidix* | serenity* \ - | cygwin* | msys* | moss* | proelf* | rtems* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ | midipix* | mingw32* | mingw64* | mint* \ | uxpv* | beos* | mpeix* | udk* | moxiebox* \ | interix* | uwin* | mks* | rhapsody* | darwin* \ @@ -1766,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* | cos* | mbr* ) + | fiwix* ) ;; # This one is extra strict with allowed versions sco3.2v2 | sco3.2v[4-9]* | sco5v6*) @@ -1774,99 +1762,41 @@ case $os in ;; none) ;; - kernel* | msvc* ) - # Restricted further below - ;; - '') - if test x"$obj" = x - then - echo "Invalid configuration '$1': Blank OS only allowed with explicit machine code file format" 1>&2 - fi - ;; - *) - echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 - exit 1 - ;; -esac - -case $obj in - aout* | coff* | elf* | pe*) - ;; - '') - # empty is fine - ;; *) - echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 - exit 1 - ;; -esac - -# Here we handle the constraint that a (synthetic) cpu and os are -# valid only in combination with each other and nowhere else. -case $cpu-$os in - # The "javascript-unknown-ghcjs" triple is used by GHC; we - # accept it here in order to tolerate that, but reject any - # variations. - javascript-ghcjs) - ;; - javascript-* | *-ghcjs) - echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 + 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-$obj in - linux-gnu*- | linux-dietlibc*- | linux-android*- | linux-newlib*- \ - | linux-musl*- | linux-relibc*- | linux-uclibc*- | linux-mlibc*- ) - ;; - uclinux-uclibc*- ) - ;; - managarm-mlibc*- | managarm-kernel*- ) +case $kernel-$os in + linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ + | linux-musl* | linux-relibc* | linux-uclibc* ) ;; - windows*-msvc*-) + uclinux-uclibc* ) ;; - -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 ;; - *-msvc*- ) - echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 - exit 1 - ;; - kfreebsd*-gnu*- | kopensolaris*-gnu*-) + kfreebsd*-gnu* | kopensolaris*-gnu*) ;; - vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) + vxworks-simlinux | vxworks-simwindows | vxworks-spe) ;; - nto-qnx*-) - ;; - os2-emx-) + nto-qnx*) ;; - *-eabi*- | *-gnueabi*-) + os2-emx) ;; - none--*) - # None (no kernel, i.e. freestanding / bare metal), - # can be paired with an machine code file format + *-eabi* | *-gnueabi*) ;; - -*-) + -*) # Blank kernel with real OS is always fine. ;; - --*) - # Blank kernel and OS with real machine code file format 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 @@ -1949,7 +1879,7 @@ case $vendor in ;; esac -echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" +echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: Index: usr.sbin/nsd/simdzone/configure =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/configure,v diff -u -p -r1.1.1.2 configure --- usr.sbin/nsd/simdzone/configure 6 Sep 2025 17:38:34 -0000 1.1.1.2 +++ usr.sbin/nsd/simdzone/configure 21 Sep 2026 16:29:10 -0000 @@ -1,11 +1,11 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.72 for simdzone 0.2.3. +# Generated by GNU Autoconf 2.71 for simdzone 0.2.5. # # Report bugs to . # # -# Copyright (C) 1992-1996, 1998-2017, 2020-2023 Free Software Foundation, +# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Inc. # # @@ -17,6 +17,7 @@ # 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 @@ -25,13 +26,12 @@ then : # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST -else case e in #( - e) case `(set -o) 2>/dev/null` in #( +else $as_nop + case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; -esac ;; esac fi @@ -103,7 +103,7 @@ IFS=$as_save_IFS ;; esac -# We did not find ourselves, most probably we were run as 'sh COMMAND' +# 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 @@ -133,14 +133,15 @@ case $- in # (((( 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'. +# 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="if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 + as_bourne_compatible="as_nop=: +if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: @@ -148,13 +149,12 @@ then : # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST -else case e in #( - e) case \`(set -o) 2>/dev/null\` in #( +else \$as_nop + case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; -esac ;; esac fi " @@ -172,9 +172,8 @@ as_fn_ret_failure && { exitcode=1; echo if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : -else case e in #( - e) exitcode=1; echo positional parameters were not saved. ;; -esac +else \$as_nop + exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) @@ -187,15 +186,14 @@ test -x / || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes -else case e in #( - e) as_have_required=no ;; -esac +else $as_nop + as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : -else case e in #( - e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +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 @@ -228,13 +226,12 @@ IFS=$as_save_IFS if $as_found then : -else case e in #( - e) if { test -f "$SHELL" || test -f "$SHELL.exe"; } && +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 ;; -esac +fi fi @@ -256,7 +253,7 @@ case $- in # (((( 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'. +# out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi @@ -276,8 +273,7 @@ $0: message. Then install a modern shell $0: the script under such a shell if you do have one." fi exit 1 -fi ;; -esac +fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} @@ -316,6 +312,14 @@ as_fn_exit () 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 # ------------- @@ -384,12 +388,11 @@ then : { eval $1+=\$2 }' -else case e in #( - e) as_fn_append () +else $as_nop + as_fn_append () { eval $1=\$$1\$2 - } ;; -esac + } fi # as_fn_append # as_fn_arith ARG... @@ -403,14 +406,21 @@ then : { as_val=$(( $* )) }' -else case e in #( - e) as_fn_arith () +else $as_nop + as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` - } ;; -esac + } 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] # ---------------------------------------- @@ -484,8 +494,6 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits /[$]LINENO/= ' <$as_myself | sed ' - t clear - :clear s/[$]LINENO.*/&-/ t lineno b @@ -534,6 +542,7 @@ esac 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 @@ -545,9 +554,9 @@ 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'. + # 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 @@ -572,12 +581,10 @@ as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. -as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" -as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated +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_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" -as_tr_sh="eval sed '$as_sed_sh'" # deprecated +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 /dev/null && - as_fn_error $? "invalid feature name: '$ac_useropt'" + 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 @@ -853,7 +860,7 @@ do 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'" + 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 @@ -1066,7 +1073,7 @@ do 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'" + 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 @@ -1082,7 +1089,7 @@ do 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'" + 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 @@ -1112,8 +1119,8 @@ do | --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" + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" ;; *=*) @@ -1121,7 +1128,7 @@ Try '$0 --help' for more information" # 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'" ;; + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; @@ -1171,7 +1178,7 @@ do 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' +# 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 @@ -1239,7 +1246,7 @@ if test ! -r "$srcdir/$ac_unique_file"; 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_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)` @@ -1267,7 +1274,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 simdzone 0.2.3 to adapt to many kinds of systems. +\`configure' configures simdzone 0.2.5 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1281,11 +1288,11 @@ Configuration: --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 + -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' + -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 '..'] + --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX @@ -1293,10 +1300,10 @@ Installation directories: --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'. +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. @@ -1334,7 +1341,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of simdzone 0.2.3:";; + short | recursive ) echo "Configuration of simdzone 0.2.5:";; esac cat <<\_ACEOF @@ -1356,7 +1363,7 @@ Some influential environment variables: CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory -Use these variables to override the choices made by 'configure' or to help +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 . @@ -1423,10 +1430,10 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -simdzone configure 0.2.3 -generated by GNU Autoconf 2.72 +simdzone configure 0.2.5 +generated by GNU Autoconf 2.71 -Copyright (C) 2023 Free Software Foundation, Inc. +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 @@ -1465,12 +1472,11 @@ printf "%s\n" "$ac_try_echo"; } >&5 } && test -s conftest.$ac_objext then : ac_retval=0 -else case e in #( - e) printf "%s\n" "$as_me: failed program was:" >&5 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=1 ;; -esac + ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval @@ -1508,12 +1514,11 @@ printf "%s\n" "$ac_try_echo"; } >&5 } then : ac_retval=0 -else case e in #( - e) printf "%s\n" "$as_me: failed program was:" >&5 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=1 ;; -esac + 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 @@ -1537,8 +1542,8 @@ printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 -else case e in #( - e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> @@ -1546,12 +1551,10 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" -else case e in #( - e) eval "$3=no" ;; -esac +else $as_nop + eval "$3=no" fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; -esac +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 @@ -1573,8 +1576,8 @@ printf %s "checking whether $as_decl_nam if eval test \${$3+y} then : printf %s "(cached) " >&6 -else case e in #( - e) as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` +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 @@ -1598,14 +1601,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" -else case e in #( - e) eval "$3=no" ;; -esac +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 - ;; -esac + fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -1625,15 +1626,15 @@ printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 -else case e in #( - e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (void); below. */ + which can conflict with char $2 (); below. */ #include #undef $2 @@ -1644,7 +1645,7 @@ else case e in #( #ifdef __cplusplus extern "C" #endif -char $2 (void); +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. */ @@ -1663,13 +1664,11 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" -else case e in #( - e) eval "$3=no" ;; -esac +else $as_nop + eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext ;; -esac + conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -1701,8 +1700,8 @@ 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.3, which was -generated by GNU Autoconf 2.72. Invocation command line was +It was created by simdzone $as_me 0.2.5, which was +generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -1948,10 +1947,10 @@ esac 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;} + || { { 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; } +See \`config.log' for more details" "$LINENO" 5; } fi done @@ -1987,7 +1986,9 @@ 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 (char **p, int i) +static char *e (p, i) + char **p; + int i; { return p[i]; } @@ -2001,21 +2002,6 @@ static char *f (char * (*g) (char **, in return s; } -/* C89 style stringification. */ -#define noexpand_stringify(a) #a -const char *stringified = noexpand_stringify(arbitrary+token=sequence); - -/* C89 style token pasting. Exercises some of the corner cases that - e.g. old MSVC gets wrong, but not very hard. */ -#define noexpand_concat(a,b) a##b -#define expand_concat(a,b) noexpand_concat(a,b) -extern int vA; -extern int vbee; -#define aye A -#define bee B -int *pvA = &expand_concat(v,aye); -int *pvbee = &noexpand_concat(v,bee); - /* 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 @@ -2043,19 +2029,16 @@ ok |= (argc == 0 || f (e, argv, 0) != ar # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' -/* Does the compiler advertise C99 conformance? */ +// Does the compiler advertise C99 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif -// See if C++-style comments work. - #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); -extern void free (void *); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare @@ -2105,6 +2088,7 @@ 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) @@ -2170,8 +2154,6 @@ ac_c_conftest_c99_main=' ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; - // Work around memory leak warnings. - free (ia); // Check named initializers. struct named_init ni = { @@ -2193,7 +2175,7 @@ ac_c_conftest_c99_main=' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' -/* Does the compiler advertise C11 conformance? */ +// Does the compiler advertise C11 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif @@ -2385,9 +2367,8 @@ IFS=$as_save_IFS if $as_found then : -else case e in #( - e) as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 ;; -esac +else $as_nop + as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 fi @@ -2415,12 +2396,12 @@ for ac_var in $ac_precious_vars; do 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;} + { 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;} + { 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=: ;; ,);; *) @@ -2429,18 +2410,18 @@ printf "%s\n" "$as_me: error: '$ac_var' 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;} + { 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;} + { 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;} + { 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. @@ -2456,11 +2437,11 @@ printf "%s\n" "$as_me: current value: 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: 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' + as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## @@ -2773,8 +2754,8 @@ printf %s "checking for $ac_word... " >& if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else case e in #( - e) if test -n "$CC"; then +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 @@ -2796,8 +2777,7 @@ done done IFS=$as_save_IFS -fi ;; -esac +fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -2819,8 +2799,8 @@ printf %s "checking for $ac_word... " >& if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 -else case e in #( - e) if test -n "$ac_ct_CC"; then +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 @@ -2842,8 +2822,7 @@ done done IFS=$as_save_IFS -fi ;; -esac +fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then @@ -2878,8 +2857,8 @@ printf %s "checking for $ac_word... " >& if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else case e in #( - e) if test -n "$CC"; then +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 @@ -2901,8 +2880,7 @@ done done IFS=$as_save_IFS -fi ;; -esac +fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -2924,8 +2902,8 @@ printf %s "checking for $ac_word... " >& if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else case e in #( - e) if test -n "$CC"; then +else $as_nop + if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no @@ -2964,8 +2942,7 @@ if test $ac_prog_rejected = yes; then ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi -fi ;; -esac +fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -2989,8 +2966,8 @@ printf %s "checking for $ac_word... " >& if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else case e in #( - e) if test -n "$CC"; then +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 @@ -3012,8 +2989,7 @@ done done IFS=$as_save_IFS -fi ;; -esac +fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -3039,8 +3015,8 @@ printf %s "checking for $ac_word... " >& if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 -else case e in #( - e) if test -n "$ac_ct_CC"; then +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 @@ -3062,8 +3038,7 @@ done done IFS=$as_save_IFS -fi ;; -esac +fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then @@ -3101,8 +3076,8 @@ printf %s "checking for $ac_word... " >& if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else case e in #( - e) if test -n "$CC"; then +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 @@ -3124,8 +3099,7 @@ done done IFS=$as_save_IFS -fi ;; -esac +fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -3147,8 +3121,8 @@ printf %s "checking for $ac_word... " >& if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 -else case e in #( - e) if test -n "$ac_ct_CC"; then +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 @@ -3170,8 +3144,7 @@ done done IFS=$as_save_IFS -fi ;; -esac +fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then @@ -3200,10 +3173,10 @@ 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;} +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; } +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 @@ -3275,8 +3248,8 @@ printf "%s\n" "$ac_try_echo"; } >&5 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' + # 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. @@ -3296,7 +3269,7 @@ do 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' + # 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. @@ -3307,9 +3280,8 @@ do done test "$ac_cv_exeext" = no && ac_cv_exeext= -else case e in #( - e) ac_file='' ;; -esac +else $as_nop + ac_file='' fi if test -z "$ac_file" then : @@ -3318,14 +3290,13 @@ 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;} +{ { 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 case e in #( - e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } ;; -esac +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; } @@ -3349,10 +3320,10 @@ printf "%s\n" "$ac_try_echo"; } >&5 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'. + # 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 @@ -3362,12 +3333,11 @@ for ac_file in conftest.exe conftest con * ) break;; esac done -else case e in #( - e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} +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; } ;; -esac +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 @@ -3383,8 +3353,6 @@ int main (void) { FILE *f = fopen ("conftest.out", "w"); - if (!f) - return 1; return ferror (f) || fclose (f) != 0; ; @@ -3424,27 +3392,26 @@ printf "%s\n" "$ac_try_echo"; } >&5 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;} + { { 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; } +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.o conftest.obj conftest.out +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 case e in #( - e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -3476,18 +3443,16 @@ then : break;; esac done -else case e in #( - e) printf "%s\n" "$as_me: failed program was:" >&5 +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;} +{ { 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; } ;; -esac +See \`config.log' for more details" "$LINENO" 5; } fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext ;; -esac +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; } @@ -3498,8 +3463,8 @@ printf %s "checking whether the compiler if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 -else case e in #( - e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -3516,14 +3481,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes -else case e in #( - e) ac_compiler_gnu=no ;; -esac +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 - ;; -esac + 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; } @@ -3541,8 +3504,8 @@ printf %s "checking whether $CC accepts if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 -else case e in #( - e) ac_save_c_werror_flag=$ac_c_werror_flag +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" @@ -3560,8 +3523,8 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes -else case e in #( - e) CFLAGS="" +else $as_nop + CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -3576,8 +3539,8 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : -else case e in #( - e) ac_c_werror_flag=$ac_save_c_werror_flag +else $as_nop + ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -3594,15 +3557,12 @@ 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 ;; -esac +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 ;; -esac +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 ;; -esac + 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; } @@ -3629,8 +3589,8 @@ printf %s "checking for $CC option to en if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 -else case e in #( - e) ac_cv_prog_cc_c11=no +else $as_nop + ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -3647,28 +3607,25 @@ rm -f core conftest.err conftest.$ac_obj test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext -CC=$ac_save_CC ;; -esac +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 case e in #( - e) if test "x$ac_cv_prog_cc_c11" = x +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 case e in #( - e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +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" ;; -esac + CC="$CC $ac_cv_prog_cc_c11" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 - ac_prog_cc_stdc=c11 ;; -esac + ac_prog_cc_stdc=c11 fi fi if test x$ac_prog_cc_stdc = xno @@ -3678,8 +3635,8 @@ printf %s "checking for $CC option to en if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 -else case e in #( - e) ac_cv_prog_cc_c99=no +else $as_nop + ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -3696,28 +3653,25 @@ rm -f core conftest.err conftest.$ac_obj test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext -CC=$ac_save_CC ;; -esac +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 case e in #( - e) if test "x$ac_cv_prog_cc_c99" = x +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 case e in #( - e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +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" ;; -esac + CC="$CC $ac_cv_prog_cc_c99" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 - ac_prog_cc_stdc=c99 ;; -esac + ac_prog_cc_stdc=c99 fi fi if test x$ac_prog_cc_stdc = xno @@ -3727,8 +3681,8 @@ printf %s "checking for $CC option to en if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 -else case e in #( - e) ac_cv_prog_cc_c89=no +else $as_nop + ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -3745,28 +3699,25 @@ rm -f core conftest.err conftest.$ac_obj test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext -CC=$ac_save_CC ;; -esac +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 case e in #( - e) if test "x$ac_cv_prog_cc_c89" = x +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 case e in #( - e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +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" ;; -esac + CC="$CC $ac_cv_prog_cc_c89" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 - ac_prog_cc_stdc=c89 ;; -esac + ac_prog_cc_stdc=c89 fi fi @@ -3787,8 +3738,8 @@ cache=`echo g | sed 'y%.=/+-%___p_%'` if eval test \${cv_prog_cc_flag_$cache+y} then : printf %s "(cached) " >&6 -else case e in #( - e) +else $as_nop + echo 'void f(void){}' >conftest.c if test -z "`$CC $CPPFLAGS $CFLAGS -g -c conftest.c 2>&1`"; then eval "cv_prog_cc_flag_$cache=yes" @@ -3796,8 +3747,7 @@ else eval "cv_prog_cc_flag_$cache=no" fi rm -f conftest conftest.o conftest.c - ;; -esac + fi if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then @@ -3820,8 +3770,8 @@ cache=`echo O2 | sed 'y%.=/+-%___p_%'` if eval test \${cv_prog_cc_flag_$cache+y} then : printf %s "(cached) " >&6 -else case e in #( - e) +else $as_nop + echo 'void f(void){}' >conftest.c if test -z "`$CC $CPPFLAGS $CFLAGS -O2 -c conftest.c 2>&1`"; then eval "cv_prog_cc_flag_$cache=yes" @@ -3829,8 +3779,7 @@ else eval "cv_prog_cc_flag_$cache=no" fi rm -f conftest conftest.o conftest.c - ;; -esac + fi if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then @@ -3886,10 +3835,9 @@ printf "%s\n" "yes" >&6; } fi rm -f conftest conftest.c conftest.o -else case e in #( - e) LDFLAGS="$BAKLDFLAGS" ; CFLAGS="$BAKCFLAGS" ; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } ;; -esac +else $as_nop + LDFLAGS="$BAKLDFLAGS" ; CFLAGS="$BAKCFLAGS" ; { 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_exeext conftest.$ac_ext @@ -3947,8 +3895,8 @@ printf %s "checking for $CC options need if test ${ac_cv_c_undeclared_builtin_options+y} then : printf %s "(cached) " >&6 -else case e in #( - e) ac_save_CFLAGS=$CFLAGS +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" @@ -3967,8 +3915,8 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : -else case e in #( - e) # This test program should compile successfully. +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 @@ -3996,29 +3944,26 @@ then : if test x"$ac_arg" = x then : ac_cv_c_undeclared_builtin_options='none needed' -else case e in #( - e) ac_cv_c_undeclared_builtin_options=$ac_arg ;; -esac +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 ;; -esac +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 - ;; -esac + 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;} + { { 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; } ;; #( +See \`config.log' for more details" "$LINENO" 5; } ;; #( 'none needed') : ac_c_undeclared_builtin_options='' ;; #( *) : @@ -4038,9 +3983,8 @@ $ac_includes_default if test "x$ac_cv_have_decl_bswap16" = xyes then : ac_have_decl=1 -else case e in #( - e) ac_have_decl=0 ;; -esac +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" " @@ -4056,9 +4000,8 @@ $ac_includes_default if test "x$ac_cv_have_decl_bswap32" = xyes then : ac_have_decl=1 -else case e in #( - e) ac_have_decl=0 ;; -esac +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" " @@ -4074,9 +4017,8 @@ $ac_includes_default if test "x$ac_cv_have_decl_bswap64" = xyes then : ac_have_decl=1 -else case e in #( - e) ac_have_decl=0 ;; -esac +else $as_nop + ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_BSWAP64 $ac_have_decl" >>confdefs.h @@ -4109,8 +4051,8 @@ printf %s "checking whether C compiler a if test ${ax_cv_check_cflags___MMD+y} then : printf %s "(cached) " >&6 -else case e in #( - e) +else $as_nop + ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -MMD" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4127,22 +4069,19 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___MMD=yes -else case e in #( - e) ax_cv_check_cflags___MMD=no ;; -esac +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 ;; -esac + 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 case e in #( - e) : ;; -esac +else $as_nop + : fi # Oracle Developer Studio (no -MP) @@ -4151,8 +4090,8 @@ printf %s "checking whether C compiler a if test ${ax_cv_check_cflags___xMMD+y} then : printf %s "(cached) " >&6 -else case e in #( - e) +else $as_nop + ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -xMMD" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4169,22 +4108,19 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___xMMD=yes -else case e in #( - e) ax_cv_check_cflags___xMMD=no ;; -esac +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 ;; -esac + 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 case e in #( - e) : ;; -esac +else $as_nop + : fi @@ -4203,16 +4139,15 @@ printf %s "checking build system type... if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 -else case e in #( - e) ac_build_alias=$build_alias +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 - ;; -esac + fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf "%s\n" "$ac_cv_build" >&6; } @@ -4239,15 +4174,14 @@ printf %s "checking host system type... if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 -else case e in #( - e) if test "x$host_alias" = x; then +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 - ;; -esac + fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf "%s\n" "$ac_cv_host" >&6; } @@ -4274,15 +4208,14 @@ printf %s "checking target system type.. if test ${ac_cv_target+y} then : printf %s "(cached) " >&6 -else case e in #( - e) if test "x$target_alias" = x; then +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 - ;; -esac + fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 printf "%s\n" "$ac_cv_target" >&6; } @@ -4336,8 +4269,8 @@ printf %s "checking whether C compiler a if test ${ax_cv_check_cflags__Werror__march_westmere+y} then : printf %s "(cached) " >&6 -else case e in #( - e) +else $as_nop + ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -march=westmere" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4354,22 +4287,19 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__march_westmere=yes -else case e in #( - e) ax_cv_check_cflags__Werror__march_westmere=no ;; -esac +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 ;; -esac + 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 case e in #( - e) : ;; -esac +else $as_nop + : fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -march=haswell" >&5 @@ -4377,8 +4307,8 @@ printf %s "checking whether C compiler a if test ${ax_cv_check_cflags__Werror__march_haswell+y} then : printf %s "(cached) " >&6 -else case e in #( - e) +else $as_nop + ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -march=haswell" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4395,22 +4325,19 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__march_haswell=yes -else case e in #( - e) ax_cv_check_cflags__Werror__march_haswell=no ;; -esac +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 ;; -esac + 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 case e in #( - e) : ;; -esac +else $as_nop + : fi @@ -4449,12 +4376,11 @@ printf "%s\n" "#define HAVE_WESTMERE 1" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else case e in #( - e) +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - ;; -esac + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$BAKCFLAGS" @@ -4495,12 +4421,11 @@ printf "%s\n" "#define HAVE_HASWELL 1" > { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else case e in #( - e) +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - ;; -esac + fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$BAKCFLAGS" @@ -4515,9 +4440,8 @@ if test "x$ac_cv_func_realpath" = xyes then : printf "%s\n" "#define HAVE_REALPATH 1" >>confdefs.h -else case e in #( - e) as_fn_error $? "realpath is not available" "$LINENO" 5 ;; -esac +else $as_nop + as_fn_error $? "realpath is not available" "$LINENO" 5 fi done @@ -4538,8 +4462,8 @@ cat >confcache <<\_ACEOF # 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 +# `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 @@ -4569,14 +4493,14 @@ printf "%s\n" "$as_me: WARNING: cache va (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 + # `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. + # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | @@ -4666,6 +4590,7 @@ cat >>$CONFIG_STATUS <<\_ASEOF || as_wri # 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 @@ -4674,13 +4599,12 @@ then : # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST -else case e in #( - e) case `(set -o) 2>/dev/null` in #( +else $as_nop + case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; -esac ;; esac fi @@ -4752,7 +4676,7 @@ IFS=$as_save_IFS ;; esac -# We did not find ourselves, most probably we were run as 'sh COMMAND' +# 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 @@ -4781,6 +4705,7 @@ as_fn_error () } # as_fn_error + # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. @@ -4820,12 +4745,11 @@ then : { eval $1+=\$2 }' -else case e in #( - e) as_fn_append () +else $as_nop + as_fn_append () { eval $1=\$$1\$2 - } ;; -esac + } fi # as_fn_append # as_fn_arith ARG... @@ -4839,12 +4763,11 @@ then : { as_val=$(( $* )) }' -else case e in #( - e) as_fn_arith () +else $as_nop + as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` - } ;; -esac + } fi # as_fn_arith @@ -4927,9 +4850,9 @@ 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'. + # 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 @@ -5010,12 +4933,10 @@ as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. -as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" -as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated +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_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" -as_tr_sh="eval sed '$as_sed_sh'" # deprecated +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 @@ -5030,8 +4951,8 @@ 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 simdzone $as_me 0.2.3, which was -generated by GNU Autoconf 2.72. Invocation command line was +This file was extended by simdzone $as_me 0.2.5, which was +generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -5062,7 +4983,7 @@ _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ -'$as_me' instantiates files and other configuration actions +\`$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. @@ -5094,11 +5015,11 @@ 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="\\ -simdzone config.status 0.2.3 -configured by $0, generated by GNU Autoconf 2.72, +simdzone config.status 0.2.5 +configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" -Copyright (C) 2023 Free Software Foundation, Inc. +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." @@ -5157,8 +5078,8 @@ do ac_need_defaults=false;; --he | --h) # Conflict between --help and --header - as_fn_error $? "ambiguous option: '$1' -Try '$0 --help' for more information.";; + 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 \ @@ -5166,8 +5087,8 @@ Try '$0 --help' for more information.";; ac_cs_silent=: ;; # This is an error. - -*) as_fn_error $? "unrecognized option: '$1' -Try '$0 --help' for more information." ;; + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; @@ -5218,7 +5139,7 @@ do "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; - *) as_fn_error $? "invalid argument: '$ac_config_target'" "$LINENO" 5;; + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done @@ -5237,7 +5158,7 @@ fi # 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'. +# after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= @@ -5261,7 +5182,7 @@ 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'. +# This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then @@ -5419,13 +5340,13 @@ 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'. +# 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 +# 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. @@ -5535,7 +5456,7 @@ do esac case $ac_mode$ac_tag in :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag '$ac_tag'" "$LINENO" 5;; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac @@ -5557,19 +5478,19 @@ do -) 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 ':'. + # 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;; + 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 + # 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 '` @@ -5693,7 +5614,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_writ esac _ACEOF -# Neutralize VPATH when '$srcdir' = '.'. +# 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 @@ -5722,9 +5643,9 @@ test -z "$ac_datarootdir_hack$ac_dataroo { 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' + { 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' +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" Index: usr.sbin/nsd/simdzone/configure.ac =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/configure.ac,v diff -u -p -r1.1.1.2 configure.ac --- usr.sbin/nsd/simdzone/configure.ac 6 Sep 2025 17:38:34 -0000 1.1.1.2 +++ usr.sbin/nsd/simdzone/configure.ac 21 Sep 2026 16:29:10 -0000 @@ -10,7 +10,7 @@ # 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.3],[https://github.com/NLnetLabs/simdzone/issues]) +AC_INIT([simdzone],[0.2.5],[https://github.com/NLnetLabs/simdzone/issues]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile]) Index: usr.sbin/nsd/simdzone/include/zone.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/include/zone.h,v diff -u -p -r1.1.1.2 zone.h --- usr.sbin/nsd/simdzone/include/zone.h 6 Sep 2025 17:38:34 -0000 1.1.1.2 +++ usr.sbin/nsd/simdzone/include/zone.h 21 Sep 2026 16:29:10 -0000 @@ -185,8 +185,12 @@ extern "C" { #define ZONE_TYPE_SVCB (64u) /** Service binding @rfc{9460} */ #define ZONE_TYPE_HTTPS (65u) -/** Endpoint discovery for delegation synchronization @draft{ietf, dnsop-generalized-notify} */ +/** Endpoint discovery for delegation synchronization @rfc{9859]} */ #define ZONE_TYPE_DSYNC (66u) +/** Hierarchical Host Identity Tag @rfc{9886} */ +#define ZONE_TYPE_HHIT (67u) +/** UAS Broadcast Remote Identification @rfc{9886} */ +#define ZONE_TYPE_BRID (68u) /** Sender Policy Framework @rfc{7208} */ #define ZONE_TYPE_SPF (99u) /** Node Identifier @rfc{6742} */ @@ -255,6 +259,12 @@ extern "C" { #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) +/** DNS over CoAP resource path @rfc{9953} */ +#define ZONE_SVC_PARAM_KEY_DOCPATH (10u) +/** PvD configuration is available at the well-known path @draft{ietf, intarea-proxy-config} */ +#define ZONE_SVC_PARAM_KEY_PVD (11u) +/** Per-transport operator confidence in serving the nameserver's query load over that transport, as a percentage @draft{johani, dnsop-svcb-oots} */ +#define ZONE_SVC_PARAM_KEY_OOTS (12u) /** Reserved ("invalid key") @rfc{9460} */ #define ZONE_SVC_PARAM_KEY_INVALID_KEY (65535u) /** @} */ @@ -366,7 +376,7 @@ struct zone_file { /** 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 { const char **head, **tail, *tape[ZONE_TAPE_SIZE + 2]; } delimiters; struct { uint16_t *head, *tail, tape[ZONE_TAPE_SIZE + 1]; } newlines; }; Index: usr.sbin/nsd/simdzone/src/isadetection.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/src/isadetection.h,v diff -u -p -r1.1.1.2 isadetection.h --- usr.sbin/nsd/simdzone/src/isadetection.h 6 Sep 2025 17:38:34 -0000 1.1.1.2 +++ usr.sbin/nsd/simdzone/src/isadetection.h 21 Sep 2026 16:29:10 -0000 @@ -107,6 +107,12 @@ static inline uint32_t detect_supported_ #endif +#elif defined(__riscv) + +static inline uint32_t detect_supported_architectures(void) { + return DEFAULT; +} + #elif defined(__x86_64__) || defined(_M_AMD64) // x64 // Can be found on Intel ISA Reference for CPUID Index: usr.sbin/nsd/simdzone/src/fallback/scanner.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/src/fallback/scanner.h,v diff -u -p -r1.1.1.1 scanner.h --- usr.sbin/nsd/simdzone/src/fallback/scanner.h 3 Sep 2025 18:44:23 -0000 1.1.1.1 +++ usr.sbin/nsd/simdzone/src/fallback/scanner.h 21 Sep 2026 16:29:10 -0000 @@ -144,7 +144,7 @@ static really_inline int32_t reindex(par 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) { + while (data <= data_limit && tape+ZONE_BLOCK_SIZE <= tape_limit) { scan(parser, data, data + ZONE_BLOCK_SIZE); parser->file->buffer.index += ZONE_BLOCK_SIZE; data += ZONE_BLOCK_SIZE; @@ -156,11 +156,10 @@ static really_inline int32_t reindex(par } // only scan partial blocks after reading all data - if (parser->file->end_of_file) { - assert(left < ZONE_BLOCK_SIZE); + if (parser->file->end_of_file && left < ZONE_BLOCK_SIZE) { if (!left) { parser->file->end_of_file = NO_MORE_DATA; - } else if (((uintptr_t)tape_limit - (uintptr_t)tape) >= left) { + } else if(tape+left <= tape_limit) { scan(parser, data, data + left); parser->file->end_of_file = NO_MORE_DATA; parser->file->buffer.index += left; Index: usr.sbin/nsd/simdzone/src/generic/scanner.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/src/generic/scanner.h,v diff -u -p -r1.1.1.1 scanner.h --- usr.sbin/nsd/simdzone/src/generic/scanner.h 3 Sep 2025 18:44:23 -0000 1.1.1.1 +++ usr.sbin/nsd/simdzone/src/generic/scanner.h 21 Sep 2026 16:29:10 -0000 @@ -300,7 +300,7 @@ static really_inline int32_t reindex(par 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) { + while (data <= data_limit && tape+ZONE_BLOCK_SIZE <= tape_limit) { simd_loadu_8x64(&block.input, (const uint8_t *)data); scan(parser, &block); write_indexes(parser, &block, 0); @@ -314,11 +314,10 @@ static really_inline int32_t reindex(par } // only scan partial blocks after reading all data - if (parser->file->end_of_file) { - assert(left < ZONE_BLOCK_SIZE); + if(parser->file->end_of_file && left < ZONE_BLOCK_SIZE) { if (!left) { parser->file->end_of_file = NO_MORE_DATA; - } else if (((uintptr_t)tape_limit - (uintptr_t)tape) >= left) { + } else if (tape+left <= tape_limit) { // input is required to be padded, but may contain garbage uint8_t buffer[ZONE_BLOCK_SIZE] = { 0 }; memcpy(buffer, data, left); Index: usr.sbin/nsd/simdzone/src/generic/svcb.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/src/generic/svcb.h,v diff -u -p -r1.1.1.1 svcb.h --- usr.sbin/nsd/simdzone/src/generic/svcb.h 3 Sep 2025 18:44:23 -0000 1.1.1.1 +++ usr.sbin/nsd/simdzone/src/generic/svcb.h 21 Sep 2026 16:29:10 -0000 @@ -239,6 +239,10 @@ static int32_t parse_ech( (void)key; (void)param; + if(token->length == 1 && token->data[0] == '0') { + /* single 0 represents empty buffer */ + return 0; + } if (token->length / 4 > size / 3) SYNTAX_ERROR(parser, "maximum size exceeded"); @@ -416,6 +420,89 @@ static int32_t parse_tls_supported_group } nonnull_all +static int32_t parse_docpath( + 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) +{ + return parse_alpn(parser, type, field, key, param, rdata, token); +} + +nonnull_all +static int32_t parse_oots( + 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) { + const char *transport = t; + const uint8_t *transport_out = rdata->octets; + const char *colon = memchr(t, ':', (size_t)(te - t)); + + if (!colon) + SYNTAX_ERROR(parser, "No colon found in oots DNS transport in %s", NAME(type)); + + if(colon == t) + SYNTAX_ERROR(parser, "DNS transport name must have at least 1 character in %s", NAME(type)); + + if (rdata->octets + (colon - t) + 2 > rdata->limit) + SYNTAX_ERROR(parser, "No space for oots DNS transport in %s", NAME(type)); + + if (colon - t > 255) + SYNTAX_ERROR(parser, "DNS transport name too large in %s", NAME(type)); + + *rdata->octets++ = (uint8_t)(colon - t); + memcpy(rdata->octets, transport, (size_t)(colon - t)); + rdata->octets += (colon - t); + + t = colon + 1; + uint64_t number = 0; + for (;; t++) { + const uint64_t digit = (uint8_t)*t - '0'; + if (digit > 9) + break; + number = number * 10 + digit; + } + if(t == colon +1) + SYNTAX_ERROR(parser, "Oots percentage missing in %s", NAME(type)); + if (number > 100) + SYNTAX_ERROR(parser, "Invalid oots percentage in %s", NAME(type)); + + *rdata->octets++ = (uint8_t)number; + + const uint8_t *g; + for (g = rdata_start; g < transport_out; g += ((size_t)(*g)) + 2) { + if (memcmp(g, transport_out, ((size_t)(*transport_out)) + 1) == 0) + SEMANTIC_ERROR(parser, "Duplicate DNS transport in oots in %s", NAME(type)); + } + if (*t != ',') + break; + else + t++; + } + + if (t != te || rdata->octets > rdata->limit) + SYNTAX_ERROR(parser, "Invalid oots in %s", NAME(type)); + return 0; +} + + +nonnull_all static int32_t parse_mandatory_lax( parser_t *parser, const type_info_t *type, @@ -477,6 +564,12 @@ static const svc_param_info_t svc_params // 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), + // RFC 9953 section 5: + SVC_PARAM("docpath", 10u, OPTIONAL_VALUE, parse_docpath, parse_docpath), + // draft-ietf-intarea-proxy-config-13 section 2.1: + SVC_PARAM("pvd", 11u, NO_VALUE, parse_unknown, parse_unknown), + // draft-johani-dnsop-svcb-oots + SVC_PARAM("oots", 12u, MANDATORY_VALUE, parse_oots, parse_oots), }; static const svc_param_info_t unknown_svc_param = @@ -542,6 +635,12 @@ static really_inline size_t scan_svc_par 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, "docpath", 7) == 0) + return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_DOCPATH)]), 7; + else if (memcmp(data, "pvd", 3) == 0) + return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_PVD)]), 3; + else if (memcmp(data, "oots", 4) == 0) + return (void)(*param = &svc_params[(*key = ZONE_SVC_PARAM_KEY_OOTS)]), 4; else if (memcmp(data, "key", 3) == 0) return scan_unknown_svc_param_key(data, key, param); else Index: usr.sbin/nsd/simdzone/src/generic/type.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/src/generic/type.h,v diff -u -p -r1.1.1.2 type.h --- usr.sbin/nsd/simdzone/src/generic/type.h 6 Sep 2025 17:38:34 -0000 1.1.1.2 +++ usr.sbin/nsd/simdzone/src/generic/type.h 21 Sep 2026 16:29:10 -0000 @@ -18,38 +18,38 @@ 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), T(34), V(0), - V(0), V(0), T(30), V(0), V(0), T(57), V(0), T(16), - V(0), V(0), T(56), T(14), T(12), V(0), V(0), T(13), - T(61), V(0), T(105), V(0), V(0), V(0), T(32), T(258), - V(0), T(107), T(47), V(0), V(0), V(0), T(17), V(0), - T(257), V(0), V(0), V(0), V(0), V(0), V(0), V(0), - T(65), V(0), V(0), T(18), V(0), T(1), V(0), T(263), - V(0), V(0), V(0), V(0), T(51), V(0), V(0), T(106), - T(3), V(0), V(0), T(31), V(0), V(0), V(0), V(0), - V(0), T(50), T(44), T(104), T(10), V(0), V(0), V(0), - V(0), V(0), T(55), V(0), T(28), V(0), V(0), V(0), - V(0), V(0), V(0), V(0), V(0), V(0), V(0), T(39), - T(35), V(0), V(0), T(5), T(29), T(262), V(0), V(0), - T(109), V(0), T(264), V(0), V(0), V(0), V(0), V(0), - V(0), T(21), V(0), V(0), V(0), V(0), V(0), V(0), - T(37), C(1), T(58), V(0), V(0), V(0), V(0), V(0), - V(0), V(0), V(0), C(3), V(0), T(52), T(11), T(20), - V(0), T(261), V(0), V(0), V(0), T(48), V(0), V(0), - V(0), T(25), C(2), T(43), V(0), V(0), C(4), T(60), - V(0), V(0), T(7), T(2), V(0), V(0), T(46), T(22), - V(0), V(0), V(0), V(0), V(0), V(0), V(0), T(64), - V(0), T(260), V(0), V(0), V(0), V(0), T(38), V(0), - V(0), T(259), T(59), V(0), V(0), V(0), T(42), T(36), - T(8), T(15), V(0), T(26), T(27), T(6), V(0), T(99), - V(0), V(0), V(0), V(0), V(0), V(0), V(0), T(53), - T(9), T(63), T(33), V(0), T(271), T(270), V(0), T(40), - V(0), V(0), T(24), T(19), V(0), V(0), V(0), V(0), - V(0), V(0), V(0), T(108), V(0), V(0), V(0), T(62), - V(0), V(0), V(0), V(0), V(0), T(66), T(4), V(0), - V(0), V(0), T(256), V(0), T(49), V(0), V(0), V(0), - V(0), V(0), T(45), V(0), V(0), T(23), 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(58), T(66), V(0), V(0), T(65), V(0), + V(0), T(53), V(0), V(0), T(67), T(108), V(0), V(0), + V(0), T(4), V(0), V(0), V(0), T(49), V(0), V(0), + V(0), V(0), V(0), V(0), V(0), T(31), V(0), V(0), + V(0), V(0), V(0), T(28), T(104), V(0), T(257), V(0), + V(0), V(0), T(27), V(0), V(0), V(0), V(0), T(48), + V(0), V(0), V(0), V(0), V(0), T(1), V(0), T(47), + V(0), V(0), T(34), V(0), T(263), V(0), T(106), V(0), + V(0), V(0), V(0), V(0), V(0), T(35), V(0), T(25), + V(0), V(0), V(0), V(0), V(0), V(0), V(0), T(56), + T(14), T(260), T(107), V(0), T(13), T(68), V(0), T(17), + V(0), V(0), V(0), V(0), V(0), V(0), T(50), V(0), + V(0), T(109), T(52), T(258), V(0), V(0), T(45), V(0), + T(264), V(0), T(3), V(0), V(0), T(19), V(0), V(0), + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + T(61), V(0), V(0), T(42), V(0), V(0), V(0), T(40), + V(0), V(0), T(24), T(60), V(0), V(0), T(55), V(0), + V(0), T(12), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), T(11), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), T(37), V(0), T(46), T(23), V(0), T(262), + T(64), V(0), T(261), V(0), T(29), T(21), V(0), V(0), + V(0), C(1), V(0), V(0), V(0), T(10), T(57), C(3), + T(271), T(33), V(0), V(0), V(0), V(0), V(0), T(259), + T(18), V(0), T(59), T(30), V(0), V(0), V(0), V(0), + T(16), V(0), V(0), T(7), T(6), C(2), T(43), T(20), + V(0), C(4), V(0), V(0), V(0), V(0), T(2), T(38), + T(22), T(51), V(0), V(0), V(0), V(0), V(0), T(99), + V(0), T(256), V(0), V(0), V(0), T(105), V(0), V(0), + T(44), T(39), V(0), V(0), T(8), V(0), T(5), V(0), + V(0), V(0), V(0), V(0), V(0), T(36), T(32), T(15), + V(0), T(26), V(0), V(0), V(0), T(270), V(0), V(0), + V(0), V(0), T(9), V(0), T(63), V(0), T(62), V(0) }; #undef V @@ -110,7 +110,7 @@ static really_inline uint8_t hash(uint64 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 * 3537259401ull) >> 32); + return (uint8_t)((value * 3547541308ull) >> 32); } nonnull_all Index: usr.sbin/nsd/simdzone/src/generic/types.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/src/generic/types.h,v diff -u -p -r1.1.1.2 types.h --- usr.sbin/nsd/simdzone/src/generic/types.h 6 Sep 2025 17:38:34 -0000 1.1.1.2 +++ usr.sbin/nsd/simdzone/src/generic/types.h 21 Sep 2026 16:29:10 -0000 @@ -1385,8 +1385,8 @@ static int32_t check_ds_rr( 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 + 32, // 5: GOST R 34.10-2012 + 32, // 6: SM3 0 // 7: Unassigned }; @@ -1435,8 +1435,8 @@ static int32_t parse_ds_rdata( 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 + 32, // 5: GOST R 34.10-2012 + 32, // 6: SM3 0 // 7: Unassigned }; @@ -2340,6 +2340,55 @@ static int32_t parse_dsync_rdata( } nonnull_all +static int32_t check_hhit_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + // FIXME: It is a CBOR blob with some internal structure, but ignoring the + // internal structure, a minimally sized CBOR blob is 1 byte + if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets < 1) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_hhit_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_hhit_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_brid_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + // FIXME: It is a CBOR blob with some internal structure, but ignoring the + // internal structure, a minimally sized CBOR blob is 1 byte + if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets < 1) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_brid_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_brid_rr(parser, type, rdata); +} + + +nonnull_all static int32_t check_nid_rr( parser_t *parser, const type_info_t *type, const rdata_t *rdata) { @@ -2752,29 +2801,33 @@ static int32_t parse_amtrelay_rdata( if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0) return code; - if (octets[1]) { - if ((code = take_contiguous(parser, type, &fields[3], token)) < 0) - return code; - switch (octets[1]) { - case 1: /* IPv4 address */ - type = (const type_info_t *)amtrelay_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 *)amtrelay_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)); - } + if ((code = take_contiguous(parser, type, &fields[3], token)) < 0) + return code; + switch (octets[1]) { + case 0: + /* no gateway requires a '.' as the relay in presentation format + * without parsing it into wireformat rdata */ + if (!(token->length == 1 && *token->data == '.')) + SYNTAX_ERROR(parser, "Invalid %s in %s, the no gateway type (type 0) of AMTRELAY requires the relay field to have '.' in it", NAME(&fields[3]), NAME(type)); + break; + case 1: /* IPv4 address */ + type = (const type_info_t *)amtrelay_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 *)amtrelay_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)); } octets[1] |= D; if ((code = take_delimiter(parser, type, token)) < 0) @@ -3277,6 +3330,14 @@ static const rdata_info_t dsync_rdata_fi FIELD("target") }; +static const rdata_info_t hhit_rdata_fields[] = { + FIELD("cbor blob"), +}; + +static const rdata_info_t brid_rdata_fields[] = { + FIELD("cbor blob"), +}; + static const rdata_info_t spf_rdata_fields[] = { FIELD("text") }; @@ -3522,8 +3583,10 @@ static const type_info_t types[] = { check_https_rr, parse_https_rdata), TYPE("DSYNC", ZONE_TYPE_DSYNC, ZONE_CLASS_ANY, FIELDS(dsync_rdata_fields), check_dsync_rr, parse_dsync_rdata), - UNKNOWN_TYPE(67), - UNKNOWN_TYPE(68), + TYPE("HHIT", ZONE_TYPE_HHIT, ZONE_CLASS_ANY, FIELDS(hhit_rdata_fields), + check_hhit_rr, parse_hhit_rdata), + TYPE("BRID", ZONE_TYPE_BRID, ZONE_CLASS_ANY, FIELDS(brid_rdata_fields), + check_brid_rr, parse_brid_rdata), UNKNOWN_TYPE(69), UNKNOWN_TYPE(70), UNKNOWN_TYPE(71), Index: usr.sbin/nsd/simdzone/src/westmere/type.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/simdzone/src/westmere/type.h,v diff -u -p -r1.1.1.2 type.h --- usr.sbin/nsd/simdzone/src/westmere/type.h 6 Sep 2025 17:38:34 -0000 1.1.1.2 +++ usr.sbin/nsd/simdzone/src/westmere/type.h 21 Sep 2026 16:29:10 -0000 @@ -18,38 +18,38 @@ 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), T(34), V(0), - V(0), V(0), T(30), V(0), V(0), T(57), V(0), T(16), - V(0), V(0), T(56), T(14), T(12), V(0), V(0), T(13), - T(61), V(0), T(105), V(0), V(0), V(0), T(32), T(258), - V(0), T(107), T(47), V(0), V(0), V(0), T(17), V(0), - T(257), V(0), V(0), V(0), V(0), V(0), V(0), V(0), - T(65), V(0), V(0), T(18), V(0), T(1), V(0), T(263), - V(0), V(0), V(0), V(0), T(51), V(0), V(0), T(106), - T(3), V(0), V(0), T(31), V(0), V(0), V(0), V(0), - V(0), T(50), T(44), T(104), T(10), V(0), V(0), V(0), - V(0), V(0), T(55), V(0), T(28), V(0), V(0), V(0), - V(0), V(0), V(0), V(0), V(0), V(0), V(0), T(39), - T(35), V(0), V(0), T(5), T(29), T(262), V(0), V(0), - T(109), V(0), T(264), V(0), V(0), V(0), V(0), V(0), - V(0), T(21), V(0), V(0), V(0), V(0), V(0), V(0), - T(37), C(1), T(58), V(0), V(0), V(0), V(0), V(0), - V(0), V(0), V(0), C(3), V(0), T(52), T(11), T(20), - V(0), T(261), V(0), V(0), V(0), T(48), V(0), V(0), - V(0), T(25), C(2), T(43), V(0), V(0), C(4), T(60), - V(0), V(0), T(7), T(2), V(0), V(0), T(46), T(22), - V(0), V(0), V(0), V(0), V(0), V(0), V(0), T(64), - V(0), T(260), V(0), V(0), V(0), V(0), T(38), V(0), - V(0), T(259), T(59), V(0), V(0), V(0), T(42), T(36), - T(8), T(15), V(0), T(26), T(27), T(6), V(0), T(99), - V(0), V(0), V(0), V(0), V(0), V(0), V(0), T(53), - T(9), T(63), T(33), V(0), T(271), T(270), V(0), T(40), - V(0), V(0), T(24), T(19), V(0), V(0), V(0), V(0), - V(0), V(0), V(0), T(108), V(0), V(0), V(0), T(62), - V(0), V(0), V(0), V(0), V(0), T(66), T(4), V(0), - V(0), V(0), T(256), V(0), T(49), V(0), V(0), V(0), - V(0), V(0), T(45), V(0), V(0), T(23), 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(58), T(66), V(0), V(0), T(65), V(0), + V(0), T(53), V(0), V(0), T(67), T(108), V(0), V(0), + V(0), T(4), V(0), V(0), V(0), T(49), V(0), V(0), + V(0), V(0), V(0), V(0), V(0), T(31), V(0), V(0), + V(0), V(0), V(0), T(28), T(104), V(0), T(257), V(0), + V(0), V(0), T(27), V(0), V(0), V(0), V(0), T(48), + V(0), V(0), V(0), V(0), V(0), T(1), V(0), T(47), + V(0), V(0), T(34), V(0), T(263), V(0), T(106), V(0), + V(0), V(0), V(0), V(0), V(0), T(35), V(0), T(25), + V(0), V(0), V(0), V(0), V(0), V(0), V(0), T(56), + T(14), T(260), T(107), V(0), T(13), T(68), V(0), T(17), + V(0), V(0), V(0), V(0), V(0), V(0), T(50), V(0), + V(0), T(109), T(52), T(258), V(0), V(0), T(45), V(0), + T(264), V(0), T(3), V(0), V(0), T(19), V(0), V(0), + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + T(61), V(0), V(0), T(42), V(0), V(0), V(0), T(40), + V(0), V(0), T(24), T(60), V(0), V(0), T(55), V(0), + V(0), T(12), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), T(11), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), T(37), V(0), T(46), T(23), V(0), T(262), + T(64), V(0), T(261), V(0), T(29), T(21), V(0), V(0), + V(0), C(1), V(0), V(0), V(0), T(10), T(57), C(3), + T(271), T(33), V(0), V(0), V(0), V(0), V(0), T(259), + T(18), V(0), T(59), T(30), V(0), V(0), V(0), V(0), + T(16), V(0), V(0), T(7), T(6), C(2), T(43), T(20), + V(0), C(4), V(0), V(0), V(0), V(0), T(2), T(38), + T(22), T(51), V(0), V(0), V(0), V(0), V(0), T(99), + V(0), T(256), V(0), V(0), V(0), T(105), V(0), V(0), + T(44), T(39), V(0), V(0), T(8), V(0), T(5), V(0), + V(0), V(0), V(0), V(0), V(0), T(36), T(32), T(15), + V(0), T(26), V(0), V(0), V(0), T(270), V(0), V(0), + V(0), V(0), T(9), V(0), T(63), V(0), T(62), V(0) }; #undef V @@ -102,7 +102,7 @@ static really_inline uint8_t hash(uint64 { uint32_t value = (uint32_t)((prefix >> 32) ^ prefix); // magic value is generated using hash.c, rerun when adding types - return (uint8_t)((value * 3537259401ull) >> 32); + return (uint8_t)((value * 3547541308ull) >> 32); } nonnull_all Index: usr.sbin/nsd/util/proxy_protocol.c =================================================================== RCS file: /cvs/src/usr.sbin/nsd/util/proxy_protocol.c,v diff -u -p -r1.1 proxy_protocol.c --- usr.sbin/nsd/util/proxy_protocol.c 20 Dec 2023 17:29:02 -0000 1.1 +++ usr.sbin/nsd/util/proxy_protocol.c 21 Sep 2026 16:29:10 -0000 @@ -184,14 +184,23 @@ pp2_read_header(uint8_t* buf, size_t buf (header->ver_cmd & 0xF) != PP2_CMD_PROXY) { return PP_PARSE_UNKNOWN_CMD; } - /* Check for supported family and protocol */ - if(header->fam_prot != PP2_UNSPEC_UNSPEC && - header->fam_prot != PP2_INET_STREAM && - header->fam_prot != PP2_INET_DGRAM && - header->fam_prot != PP2_INET6_STREAM && - header->fam_prot != PP2_INET6_DGRAM && - header->fam_prot != PP2_UNIX_STREAM && - header->fam_prot != PP2_UNIX_DGRAM) { + /* Check for supported family and protocol, and that len covers + * the per-family address block (proxy-protocol.txt s2.2). */ + switch(header->fam_prot) { + case PP2_UNSPEC_UNSPEC: + break; + case PP2_INET_STREAM: + case PP2_INET_DGRAM: + if(ntohs(header->len) < PP2_HEADER_LEN_INET) + return PP_PARSE_SIZE; + break; + case PP2_INET6_STREAM: + case PP2_INET6_DGRAM: + if(ntohs(header->len) < PP2_HEADER_LEN_INET6) + return PP_PARSE_SIZE; + break; + default: + /* PP2_UNIX_STREAM, PP2_UNIX_DGRAM, others. */ return PP_PARSE_UNKNOWN_FAM_PROT; } /* We have a correct header */ Index: usr.sbin/nsd/util/proxy_protocol.h =================================================================== RCS file: /cvs/src/usr.sbin/nsd/util/proxy_protocol.h,v diff -u -p -r1.1 proxy_protocol.h --- usr.sbin/nsd/util/proxy_protocol.h 20 Dec 2023 17:29:02 -0000 1.1 +++ usr.sbin/nsd/util/proxy_protocol.h 21 Sep 2026 16:29:10 -0000 @@ -54,6 +54,15 @@ /** PROXYv2 version (protocol value) */ #define PP2_VERSION 0x2 +/** PROXYv2 minimum header.len value for TCP/UDP over IPv4 */ +#define PP2_HEADER_LEN_INET 12 + +/** PROXYv2 minimum header.len value for TCP/UDP over IPv6 */ +#define PP2_HEADER_LEN_INET6 36 + +/** PROXYv2 minimum header.len value for TCP/UDP over AF_UNIX */ +#define PP2_HEADER_LEN_UNIX 216 + /** * PROXYv2 command (protocol value). */