From: Stuart Henderson Subject: unbound 1.22.0 To: tech Date: Fri, 7 Feb 2025 21:30:57 +0000 Upstream release was a few months ago. As usual not super easy to read as a diff in one go, and many of the changes relate to things not used in the OpenBSD build. Onky lightly tested so far. Index: doc/Changelog =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/Changelog,v diff -u -p -r1.53 Changelog --- doc/Changelog 4 Sep 2024 09:36:40 -0000 1.53 +++ doc/Changelog 7 Feb 2025 21:25:44 -0000 @@ -1,6 +1,155 @@ +16 October 2024: Yorgos + - Fix for dnsoverquic and dnstap to use the correct dnstap + environment. + +16 October 2024: Wouter + - Fix for dnstap with dnscrypt and dnstap without dnsoverquic. + +14 October 2024: Wouter + - Fix to display warning if quic-port is set but dnsoverquic is not + enabled when compiled. + - Fix dnsoverquic to extend the number of streams when one is closed. + +11 October 2024: Wouter + - Fix to disable detection of quic configured ports when quic is + not compiled in. + - Fix harden-unverified-glue for AAAA cache_fill_missing lookups. + - Fix contrib/aaaa-filter-iterator.patch for change in call + signature for cache_fill_missing. + +10 October 2024: Wouter + - Fix cookie_file test sporadic fails for time change during + the test. + - Fix add reallocarray to alloc stats unit test, and disable + override of strdup in unbound-host, and the result of config + get option is freed properly. + +9 October 2024: Wouter + - Merge #871: DNS over QUIC. This adds `quic-port: 853` and + `quic-size: 8m` that enable dnsoverquic, and the counters + `num.query.quic` and `mem.quic` in the statistics output. + The feature needs to be enabled by compiling with libngtcp2, + with `--with-libngtcp2=path` and libngtcp2 needs openssl+quic, + pass that with `--with-ssl=path` to compile unbound as well. + - Fix to limit NSEC TTL for messages from cachedb. Fix to limit the + prefetch ttl for messages after a CNAME with short TTL. + - Fix for dnstap compile of doqclient with doq disabled. + +8 October 2024: Wouter + - Fix #1149: unbound-control-setup hangs sometimes depending on + the openssl version. + - Fix #1128: Cannot override tcp-upstream and tls-upstream with + forward-tcp-upstream and forward-tls-upstream. + +3 October 2024: Yorgos + - Fix CVE-2024-8508, unbounded name compression could lead to denial + of service. + - This fix was part of 1.21.1, a security point release on 1.21.0. + The code repository continues with this fix and the version number + 1.22.0. + +30 September 2024: Wouter + - Fix negative cache NSEC3 parameter compares for zero length NSEC3 + salt. + - Fix unbound dnstap socket test program analyzer warnings about + unused variable assignments and variable initialization. + +25 September 2024: Wouter + - Fix #1144: [FR] log timestamps in ISO8601 format with timezone. + This adds the option `log-time-iso: yes` that logs in ISO8601 + format. + +24 September 2024: Yorgos + - Attempt to further fix doh_downstream_buffer_size.tdir flakiness. + - More clear text for prefetch and minimal-responses in the + unbound.conf man page. + - Merge #1143: Fix cache update when serve expired is used. Expired + records are favored over resolution and validation failures when + serve-expired is used. + +23 September 2024: Wouter + - Fix dns64 with prefetch that the prefetch is stored in cache. + +23 September 2024: Yorgos + - Fix doxygen warnings by commenting out CLANG_ASSISTED_PARSING, + CLANG_ADD_INC_PATHS, CLANG_OPTIONS and CLANG_DATABASE_PATH; they were + already disabled. + +17 September 2024: Wouter + - Add redis-command-timeout: 20 and redis-connect-timeout: 200, + that can set the timeout separately for commands and the + connection set up to the redis server. If they are not + specified, the redis-timeout value is used. + +16 September 2024: Wouter + - Merge #1140: Fix spelling mistake in comments. + +11 September 2024: Yorgos + - Fix and add comments in testdata/val_negcache_ttl.rpl. + +10 September 2024: Wouter + - Fix to limit NSEC and NSEC3 TTL when aggressive nsec is + enabled (RFC9077). + - Add unit test for ttl limit for aggressive nsec. + +6 September 2024: Yorgos + - Fix alloc-size and calloc-transposed-args compiler warnings. + - Fix comment to not trigger doxygen unknown command. + +5 September 2024: Wouter + - Fix config file read for dnstap-sample-rate. + +2 September 2024: Wouter + - Merge #1135: Add new IANA trust anchor. + +30 August 2024: Wouter + - Merge #1132: b.root renumbering. + - Fix for #1132, adjusted unit test for change in the test file. + - Fix for #1132, comment about adjusted copy of reference check. + +29 August 2024: Wouter + - Unit test for auth zone transfer TLS, and TLS failure. + - Fix to print port number in logs for auth zone transfer activities. + +28 August 2024: Wouter + - Fix that when rpz is applied the message does not get picked up by + the validator. That stops validation failures for the message. + - Fix that stub-zone and forward-zone clauses do not exhaust memory + for long content. + +27 August 2024: Wouter + - Fix #1130: Loads of logs: "validation failure: key for validation + . is marked as invalid because of a previous" for + non-DNSSEC signed zone. + +23 August 2024: Wouter + - Merge patch to fix for glue that is outside of zone, with + `harden-unverified-glue`, from Karthik Umashankar (Microsoft). + Enabling this option protects the Unbound resolver against bad + glue, that is unverified out of zone glue, by resolving them. + It uses the records as last resort if there is no other working + glue. + - Fix #1127: error: "memory exhausted" when defining more than 9994 + local-zones. + - Fix documentation for cache_fill_missing function. + +21 August 2024: Wouter + - Add cross platform freebsd, openbsd and netbsd to github ci. + - Fix for char signedness warnings on NetBSD. + +20 August 2024: Wouter + - Add iter-scrub-ns, iter-scrub-cname and max-global-quota + configuration options. + +19 August 2024: Wouter + - Fix #1126: unbound-control-setup hangs while testing for openssl + presence starting from version 1.21.0. + 9 August 2024: Wouter - Fix spelling for the cache-min-negative-ttl entry in the example.conf. + - Tag for release 1.21.0, the repository continues with 1.21.1 + in development. 8 August 2024: Wouter - Fix CAMP issues with global quota. Thanks to Huayi Duan, Marco @@ -8,7 +157,7 @@ - Fix CacheFlush issues with limit on NS RRs. Thanks to Yehuda Afek, Anat Bremler-Barr, Shoham Danino and Yuval Shavitt (Tel-Aviv University and Reichman University). - - Set version number to 1.21.0 for release. + - Set version number to 1.21.0 for release. This has tag 1.21.0rc1. - Fix that for windows the module startup is called and sets up the module-config. Index: Makefile.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/Makefile.in,v diff -u -p -r1.45 Makefile.in --- Makefile.in 4 Sep 2024 09:36:40 -0000 1.45 +++ Makefile.in 7 Feb 2025 21:25:43 -0000 @@ -179,11 +179,11 @@ testcode/unitlruhash.c testcode/unitmain testcode/unitneg.c testcode/unitregional.c testcode/unitslabhash.c \ testcode/unitverify.c testcode/readhex.c testcode/testpkts.c testcode/unitldns.c \ testcode/unitecs.c testcode/unitauth.c testcode/unitzonemd.c \ -testcode/unittcpreuse.c +testcode/unittcpreuse.c testcode/unitdoq.c UNITTEST_OBJ=unitanchor.lo unitdname.lo unitlruhash.lo unitmain.lo \ unitmsgparse.lo unitneg.lo unitregional.lo unitslabhash.lo unitverify.lo \ readhex.lo testpkts.lo unitldns.lo unitecs.lo unitauth.lo unitzonemd.lo \ -unittcpreuse.lo +unittcpreuse.lo unitdoq.lo UNITTEST_OBJ_LINK=$(UNITTEST_OBJ) worker_cb.lo $(COMMON_OBJ) $(SLDNS_OBJ) \ $(COMPAT_OBJ) DAEMON_SRC=daemon/acl_list.c daemon/cachedump.c daemon/daemon.c \ @@ -242,6 +242,10 @@ DOHCLIENT_SRC=testcode/dohclient.c DOHCLIENT_OBJ=dohclient.lo DOHCLIENT_OBJ_LINK=$(DOHCLIENT_OBJ) worker_cb.lo $(COMMON_OBJ) $(COMPAT_OBJ) \ $(SLDNS_OBJ) +DOQCLIENT_SRC=testcode/doqclient.c +DOQCLIENT_OBJ=doqclient.lo +DOQCLIENT_OBJ_LINK=$(DOQCLIENT_OBJ) $(COMMON_OBJ) $(COMPAT_OBJ) \ +$(SLDNS_OBJ) PERF_SRC=testcode/perf.c PERF_OBJ=perf.lo PERF_OBJ_LINK=$(PERF_OBJ) worker_cb.lo $(COMMON_OBJ) $(COMPAT_OBJ) $(SLDNS_OBJ) @@ -288,7 +292,7 @@ ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $( $(CONTROL_SRC) $(UBANCHOR_SRC) $(PETAL_SRC) $(DNSTAP_SOCKET_SRC)\ $(PYTHONMOD_SRC) $(PYUNBOUND_SRC) $(WIN_DAEMON_THE_SRC) \ $(SVCINST_SRC) $(SVCUNINST_SRC) $(ANCHORUPD_SRC) $(SLDNS_SRC) \ - $(DOHCLIENT_SRC) $(READZONE_SRC) + $(DOHCLIENT_SRC) $(DOQCLIENT_SRC) $(READZONE_SRC) ALL_OBJ=$(COMMON_OBJ) $(UNITTEST_OBJ) $(DAEMON_OBJ) \ $(TESTBOUND_OBJ) $(LOCKVERIFY_OBJ) $(PKTVIEW_OBJ) \ @@ -297,7 +301,7 @@ ALL_OBJ=$(COMMON_OBJ) $(UNITTEST_OBJ) $( $(CONTROL_OBJ) $(UBANCHOR_OBJ) $(PETAL_OBJ) $(DNSTAP_SOCKET_OBJ)\ $(COMPAT_OBJ) $(PYUNBOUND_OBJ) \ $(SVCINST_OBJ) $(SVCUNINST_OBJ) $(ANCHORUPD_OBJ) $(SLDNS_OBJ) \ - $(DOHCLIENT_OBJ) $(READZONE_OBJ) + $(DOHCLIENT_OBJ) $(DOQCLIENT_OBJ) $(READZONE_OBJ) COMPILE=$(LIBTOOL) --tag=CC --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) @PTHREAD_CFLAGS_ONLY@ LINK=$(LIBTOOL) --tag=CC --mode=link $(CC) $(staticexe) $(RUNTIME_PATH) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) @@ -334,7 +338,7 @@ rsrc_unbound_checkconf.o: $(srcdir)/winr TEST_BIN=asynclook$(EXEEXT) delayer$(EXEEXT) \ lock-verify$(EXEEXT) memstats$(EXEEXT) perf$(EXEEXT) \ petal$(EXEEXT) pktview$(EXEEXT) streamtcp$(EXEEXT) \ - $(DNSTAP_SOCKET_TESTBIN) dohclient$(EXEEXT) \ + $(DNSTAP_SOCKET_TESTBIN) dohclient$(EXEEXT) doqclient$(EXEEXT) \ testbound$(EXEEXT) unittest$(EXEEXT) readzone$(EXEEXT) tests: all $(TEST_BIN) @@ -416,6 +420,9 @@ streamtcp$(EXEEXT): $(STREAMTCP_OBJ_LINK dohclient$(EXEEXT): $(DOHCLIENT_OBJ_LINK) $(LINK) -o $@ $(DOHCLIENT_OBJ_LINK) $(SSLLIB) $(LIBS) +doqclient$(EXEEXT): $(DOQCLIENT_OBJ_LINK) + $(LINK) -o $@ $(DOQCLIENT_OBJ_LINK) $(SSLLIB) $(LIBS) + perf$(EXEEXT): $(PERF_OBJ_LINK) $(LINK) -o $@ $(PERF_OBJ_LINK) $(SSLLIB) $(LIBS) @@ -704,6 +711,8 @@ depend: # build rules ipset.lo ipset.o: $(srcdir)/ipset/ipset.c +doqclient.lo doqclient.o: $(srcdir)/testcode/doqclient.c +unitdoq.lo unitdoq.o: $(srcdir)/testcode/unitdoq.c # Dependencies dns.lo dns.o: $(srcdir)/services/cache/dns.c config.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/util/log.h \ Index: aclocal.m4 =================================================================== RCS file: /cvs/src/usr.sbin/unbound/aclocal.m4,v diff -u -p -r1.10 aclocal.m4 --- aclocal.m4 7 Oct 2024 15:38:21 -0000 1.10 +++ aclocal.m4 7 Feb 2025 21:25:43 -0000 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.16.5 -*- Autoconf -*- +# generated automatically by aclocal 1.16.2 -*- Autoconf -*- -# Copyright (C) 1996-2021 Free Software Foundation, Inc. +# Copyright (C) 1996-2020 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -14,8 +14,7 @@ m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) # libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # -# Copyright (C) 1996-2001, 2003-2019, 2021-2022 Free Software -# Foundation, Inc. +# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives @@ -46,7 +45,7 @@ m4_define([_LT_COPYING], [dnl # along with this program. If not, see . ]) -# serial 59 LT_INIT +# serial 58 LT_INIT # LT_PREREQ(VERSION) @@ -196,7 +195,6 @@ m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl -m4_require([_LT_DECL_FILECMD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl @@ -235,8 +233,8 @@ esac ofile=libtool can_build_shared=yes -# All known linkers require a '.a' archive for static linking (except MSVC and -# ICC, which need '.lib'). +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld @@ -788,7 +786,7 @@ _LT_EOF # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? - $SED '$q' "$ltmain" >> "$cfgfile" \ + sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || @@ -1050,8 +1048,8 @@ int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD - echo "$AR $AR_FLAGS libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD - $AR $AR_FLAGS libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF @@ -1075,12 +1073,17 @@ _LT_EOF _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; - darwin*) - case $MACOSX_DEPLOYMENT_TARGET,$host in - 10.[[012]],*|,*powerpc*-darwin[[5-8]]*) - _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; - *) - _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[[012]][[,.]]*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac @@ -1129,12 +1132,12 @@ m4_defun([_LT_DARWIN_LINKER_FEATURES], output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" - _LT_TAGVAR(archive_expsym_cmds, $1)="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" - _LT_TAGVAR(module_expsym_cmds, $1)="$SED -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" m4_if([$1], [CXX], [ if test yes != "$lt_cv_apple_cc_single_mod"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" - _LT_TAGVAR(archive_expsym_cmds, $1)="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi ],[]) else @@ -1248,8 +1251,7 @@ _LT_DECL([], [ECHO], [1], [An echo progr # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], -[m4_require([_LT_DECL_SED])dnl -AC_MSG_CHECKING([for sysroot]) +[AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], [Search for dependent libraries within DIR (or the compiler's sysroot @@ -1266,7 +1268,7 @@ case $with_sysroot in #( fi ;; #( /*) - lt_sysroot=`echo "$with_sysroot" | $SED -e "$sed_quote_subst"` + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( @@ -1296,7 +1298,7 @@ ia64-*-hpux*) # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; @@ -1313,7 +1315,7 @@ ia64-*-hpux*) echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test yes = "$lt_cv_prog_gnu_ld"; then - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; @@ -1325,7 +1327,7 @@ ia64-*-hpux*) ;; esac else - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; @@ -1347,7 +1349,7 @@ mips64*-*linux*) echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then emul=elf - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; @@ -1355,7 +1357,7 @@ mips64*-*linux*) emul="${emul}64" ;; esac - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; @@ -1363,7 +1365,7 @@ mips64*-*linux*) emul="${emul}ltsmip" ;; esac - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; @@ -1383,14 +1385,14 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux* # not appear in the list. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then - case `$FILECMD conftest.o` in + case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) - case `$FILECMD conftest.o` in + case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; @@ -1458,7 +1460,7 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux* # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then - case `$FILECMD conftest.o` in + case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) @@ -1497,22 +1499,9 @@ need_locks=$enable_libtool_lock m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} +: ${AR_FLAGS=cru} _LT_DECL([], [AR], [1], [The archiver]) - -# Use ARFLAGS variable as AR's operation code to sync the variable naming with -# Automake. If both AR_FLAGS and ARFLAGS are specified, AR_FLAGS should have -# higher priority because thats what people were doing historically (setting -# ARFLAGS for automake and AR_FLAGS for libtool). FIXME: Make the AR_FLAGS -# variable obsoleted/removed. - -test ${AR_FLAGS+y} || AR_FLAGS=${ARFLAGS-cr} -lt_ar_flags=$AR_FLAGS -_LT_DECL([], [lt_ar_flags], [0], [Flags to create an archive (by configure)]) - -# Make AR_FLAGS overridable by 'make ARFLAGS='. Don't try to run-time override -# by AR_FLAGS because that was never working and AR_FLAGS is about to die. -_LT_DECL([], [AR_FLAGS], [\@S|@{ARFLAGS-"\@S|@lt_ar_flags"}], - [Flags to create an archive]) +_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no @@ -1731,7 +1720,7 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [d lt_cv_sys_max_cmd_len=8192; ;; - bitrig* | darwin* | dragonfly* | freebsd* | midnightbsd* | netbsd* | openbsd*) + bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` @@ -1774,7 +1763,7 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [d sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then - lt_cv_sys_max_cmd_len=`echo $kargmax | $SED 's/.*[[ ]]//'` + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi @@ -2224,35 +2213,26 @@ m4_defun([_LT_CMD_STRIPLIB], striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) -if test -z "$STRIP"; then - AC_MSG_RESULT([no]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) else - if $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then - old_striplib="$STRIP --strip-debug" - striplib="$STRIP --strip-unneeded" - AC_MSG_RESULT([yes]) - else - case $host_os in - darwin*) - # FIXME - insert some real tests, host_os isn't really good enough +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) - ;; - freebsd*) - if $STRIP -V 2>&1 | $GREP "elftoolchain" >/dev/null; then - old_striplib="$STRIP --strip-debug" - striplib="$STRIP --strip-unneeded" - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - ;; - *) + else AC_MSG_RESULT([no]) - ;; - esac - fi + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) @@ -2575,7 +2555,7 @@ cygwin* | mingw* | pw32* | cegcc*) case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo $libname | $SED -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; @@ -2585,14 +2565,14 @@ m4_if([$1], [],[ ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo $libname | $SED -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; - *,cl* | *,icl*) - # Native MSVC or ICC + *,cl*) + # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' @@ -2611,7 +2591,7 @@ m4_if([$1], [],[ done IFS=$lt_save_ifs # Convert to MSYS style. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form @@ -2648,7 +2628,7 @@ m4_if([$1], [],[ ;; *) - # Assume MSVC and ICC wrapper + # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; @@ -2681,7 +2661,7 @@ dgux*) shlibpath_var=LD_LIBRARY_PATH ;; -freebsd* | dragonfly* | midnightbsd*) +freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then @@ -3483,7 +3463,7 @@ beos*) bsdi[[45]]*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' - lt_cv_file_magic_cmd='$FILECMD -L' + lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; @@ -3517,14 +3497,14 @@ darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; -freebsd* | dragonfly* | midnightbsd*) +freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' - lt_cv_file_magic_cmd=$FILECMD + lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac @@ -3538,7 +3518,7 @@ haiku*) ;; hpux10.20* | hpux11*) - lt_cv_file_magic_cmd=$FILECMD + lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' @@ -3585,7 +3565,7 @@ netbsd*) newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' - lt_cv_file_magic_cmd=$FILECMD + lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; @@ -3712,13 +3692,13 @@ else mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac - case `"$tmp_nm" -B $lt_bad_file 2>&1 | $SED '1q'` in + case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) - case `"$tmp_nm" -p /dev/null 2>&1 | $SED '1q'` in + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 @@ -3744,7 +3724,7 @@ else # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) - case `$DUMPBIN -symbols -headers /dev/null 2>&1 | $SED '1q'` in + case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; @@ -3984,7 +3964,7 @@ esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. - lt_cv_sys_global_symbol_to_import="$SED -n -e 's/^I .* \(.*\)$/\1/p'" + lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" @@ -4002,20 +3982,20 @@ fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. -lt_cv_sys_global_symbol_to_cdecl="$SED -n"\ +lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address -lt_cv_sys_global_symbol_to_c_name_address="$SED -n"\ +lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. -lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="$SED -n"\ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ @@ -4039,7 +4019,7 @@ for ac_symprfx in "" "_"; do if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. - # Also find C++ and __fastcall symbols from MSVC++ or ICC, + # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ @@ -4057,9 +4037,9 @@ for ac_symprfx in "" "_"; do " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else - lt_cv_sys_global_symbol_pipe="$SED -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi - lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | $SED '/ __gnu_lto/d'" + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no @@ -4346,7 +4326,7 @@ m4_if([$1], [CXX], [ ;; esac ;; - freebsd* | dragonfly* | midnightbsd*) + freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) @@ -4429,7 +4409,7 @@ m4_if([$1], [CXX], [ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) - case `$CC -V 2>&1 | $SED 5q` in + case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' @@ -4765,7 +4745,7 @@ m4_if([$1], [CXX], [ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) - case `$CC -V 2>&1 | $SED 5q` in + case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' @@ -4948,7 +4928,7 @@ m4_if([$1], [CXX], [ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else - _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) @@ -4956,7 +4936,7 @@ m4_if([$1], [CXX], [ ;; cygwin* | mingw* | cegcc*) case $cc_basename in - cl* | icl*) + cl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) @@ -5013,15 +4993,15 @@ dnl Note also adjust exclude_expsyms for case $host_os in cygwin* | mingw* | pw32* | cegcc*) - # FIXME: the MSVC++ and ICC port hasn't been tested in a loooong time + # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using - # Microsoft Visual C++ or Intel C++ Compiler. + # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++ or ICC) + # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) @@ -5073,7 +5053,7 @@ dnl Note also adjust exclude_expsyms for _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no - case `$LD -v | $SED -e 's/([[^)]]\+)\s\+//' 2>&1` in + case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... @@ -5185,7 +5165,6 @@ _LT_EOF emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - _LT_TAGVAR(file_list_spec, $1)='@' ;; interix[[3-9]]*) @@ -5200,7 +5179,7 @@ _LT_EOF # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) @@ -5243,7 +5222,7 @@ _LT_EOF _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac - case `$CC -V 2>&1 | $SED 5q` in + case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes @@ -5255,7 +5234,7 @@ _LT_EOF if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi @@ -5271,7 +5250,7 @@ _LT_EOF _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi @@ -5403,7 +5382,7 @@ _LT_EOF if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else - _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no @@ -5586,12 +5565,12 @@ _LT_EOF cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using - # Microsoft Visual C++ or Intel C++ Compiler. + # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in - cl* | icl*) - # Native MSVC or ICC + cl*) + # Native MSVC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes @@ -5632,7 +5611,7 @@ _LT_EOF fi' ;; *) - # Assume MSVC and ICC wrapper + # Assume MSVC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. @@ -5680,7 +5659,7 @@ _LT_EOF ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | dragonfly* | midnightbsd*) + freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes @@ -5891,7 +5870,6 @@ _LT_EOF emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - _LT_TAGVAR(file_list_spec, $1)='@' ;; osf3*) @@ -6658,8 +6636,8 @@ if test yes != "$_lt_caught_CXX_error"; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in - ,cl* | no,cl* | ,icl* | no,icl*) - # Native MSVC or ICC + ,cl* | no,cl*) + # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' @@ -6757,7 +6735,6 @@ if test yes != "$_lt_caught_CXX_error"; emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - _LT_TAGVAR(file_list_spec, $1)='@' ;; dgux*) @@ -6788,7 +6765,7 @@ if test yes != "$_lt_caught_CXX_error"; _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; - freebsd* | dragonfly* | midnightbsd*) + freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes @@ -6925,7 +6902,7 @@ if test yes != "$_lt_caught_CXX_error"; # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in @@ -7065,13 +7042,13 @@ if test yes != "$_lt_caught_CXX_error"; _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) - case `$CC -V 2>&1 | $SED 5q` in + case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' @@ -8209,14 +8186,6 @@ _LT_DECL([], [DLLTOOL], [1], [DLL creati AC_SUBST([DLLTOOL]) ]) -# _LT_DECL_FILECMD -# ---------------- -# Check for a file(cmd) program that can be used to detect file type and magic -m4_defun([_LT_DECL_FILECMD], -[AC_CHECK_TOOL([FILECMD], [file], [:]) -_LT_DECL([], [FILECMD], [1], [A file(cmd) program that detects file types]) -])# _LD_DECL_FILECMD - # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates @@ -8396,8 +8365,8 @@ _LT_DECL([to_tool_file_cmd], [lt_cv_to_t # Helper functions for option handling. -*- Autoconf -*- # -# Copyright (C) 2004-2005, 2007-2009, 2011-2019, 2021-2022 Free -# Software Foundation, Inc. +# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software +# Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives @@ -8828,7 +8797,7 @@ LT_OPTION_DEFINE([LTDL_INIT], [convenien # ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # -# Copyright (C) 2004-2005, 2007-2008, 2011-2019, 2021-2022 Free Software +# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # @@ -8953,8 +8922,7 @@ m4_define([lt_dict_filter], # ltversion.m4 -- version numbers -*- Autoconf -*- # -# Copyright (C) 2004, 2011-2019, 2021-2022 Free Software Foundation, -# Inc. +# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives @@ -8963,23 +8931,23 @@ m4_define([lt_dict_filter], # @configure_input@ -# serial 4245 ltversion.m4 +# serial 4179 ltversion.m4 # This file is part of GNU Libtool -m4_define([LT_PACKAGE_VERSION], [2.4.7]) -m4_define([LT_PACKAGE_REVISION], [2.4.7]) +m4_define([LT_PACKAGE_VERSION], [2.4.6]) +m4_define([LT_PACKAGE_REVISION], [2.4.6]) AC_DEFUN([LTVERSION_VERSION], -[macro_version='2.4.7' -macro_revision='2.4.7' +[macro_version='2.4.6' +macro_revision='2.4.6' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) # lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # -# Copyright (C) 2004-2005, 2007, 2009, 2011-2019, 2021-2022 Free -# Software Foundation, Inc. +# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software +# Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives @@ -9076,8 +9044,8 @@ m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_L m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) -# pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*- -# serial 12 (pkg-config-0.29.2) +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 11 (pkg-config-0.29.1) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson @@ -9119,7 +9087,7 @@ dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], -[m4_define([PKG_MACROS_VERSION], [0.29.2]) +[m4_define([PKG_MACROS_VERSION], [0.29.1]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ @@ -9164,7 +9132,7 @@ dnl Check to see whether a particular se dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) -dnl only at the first occurrence in configure.ac, so if the first place +dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], @@ -9220,7 +9188,7 @@ AC_ARG_VAR([$1][_CFLAGS], [C compiler fl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no -AC_MSG_CHECKING([for $2]) +AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) @@ -9230,17 +9198,17 @@ and $1[]_LIBS to avoid the need to call See the pkg-config man page for more details.]) if test $pkg_failed = yes; then - AC_MSG_RESULT([no]) + AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then - $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` - else - $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD - m4_default([$4], [AC_MSG_ERROR( + m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS @@ -9251,8 +9219,8 @@ installed software in a non-standard pre _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then - AC_MSG_RESULT([no]) - m4_default([$4], [AC_MSG_FAILURE( + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. @@ -9262,10 +9230,10 @@ _PKG_TEXT To get pkg-config, see .])[]dnl ]) else - $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS - $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) - $3 + $3 fi[]dnl ])dnl PKG_CHECK_MODULES @@ -9422,7 +9390,7 @@ AS_IF([test "$AS_TR_SH([with_]m4_tolower # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2021 Free Software Foundation, Inc. +# Copyright (C) 1997-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9453,7 +9421,7 @@ AC_CONFIG_COMMANDS_PRE( Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 2006-2021 Free Software Foundation, Inc. +# Copyright (C) 2006-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, Index: config.h.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/config.h.in,v diff -u -p -r1.32 config.h.in --- config.h.in 4 Sep 2024 09:36:40 -0000 1.32 +++ config.h.in 7 Feb 2025 21:25:43 -0000 @@ -129,6 +129,14 @@ and to 0 if you don't. */ #undef HAVE_DECL_NGHTTP2_SESSION_SERVER_NEW +/* Define to 1 if you have the declaration of `ngtcp2_conn_server_new', and to + 0 if you don't. */ +#undef HAVE_DECL_NGTCP2_CONN_SERVER_NEW + +/* Define to 1 if you have the declaration of `ngtcp2_crypto_encrypt_cb', and + to 0 if you don't. */ +#undef HAVE_DECL_NGTCP2_CRYPTO_ENCRYPT_CB + /* Define to 1 if you have the declaration of `NID_ED25519', and to 0 if you don't. */ #undef HAVE_DECL_NID_ED25519 @@ -421,6 +429,65 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NGHTTP2_NGHTTP2_H +/* Define this to use ngtcp2. */ +#undef HAVE_NGTCP2 + +/* Define to 1 if you have the `ngtcp2_ccerr_default' function. */ +#undef HAVE_NGTCP2_CCERR_DEFAULT + +/* Define to 1 if you have the `ngtcp2_conn_encode_0rtt_transport_params' + function. */ +#undef HAVE_NGTCP2_CONN_ENCODE_0RTT_TRANSPORT_PARAMS + +/* Define to 1 if you have the `ngtcp2_conn_get_max_local_streams_uni' + function. */ +#undef HAVE_NGTCP2_CONN_GET_MAX_LOCAL_STREAMS_UNI + +/* Define to 1 if you have the `ngtcp2_conn_get_num_scid' function. */ +#undef HAVE_NGTCP2_CONN_GET_NUM_SCID + +/* Define to 1 if you have the `ngtcp2_conn_in_closing_period' function. */ +#undef HAVE_NGTCP2_CONN_IN_CLOSING_PERIOD + +/* Define to 1 if you have the `ngtcp2_conn_in_draining_period' function. */ +#undef HAVE_NGTCP2_CONN_IN_DRAINING_PERIOD + +/* Define if ngtcp2_conn_shutdown_stream has 4 arguments. */ +#undef HAVE_NGTCP2_CONN_SHUTDOWN_STREAM4 + +/* Define to 1 if you have the `ngtcp2_conn_tls_early_data_rejected' function. + */ +#undef HAVE_NGTCP2_CONN_TLS_EARLY_DATA_REJECTED + +/* Define to 1 if you have the `ngtcp2_crypto_encrypt_cb' function. */ +#undef HAVE_NGTCP2_CRYPTO_ENCRYPT_CB + +/* Define to 1 if you have the + `ngtcp2_crypto_quictls_configure_client_context' function. */ +#undef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_CLIENT_CONTEXT + +/* Define to 1 if you have the + `ngtcp2_crypto_quictls_configure_server_context' function. */ +#undef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT + +/* Define to 1 if you have the + `ngtcp2_crypto_quictls_from_ossl_encryption_level' function. */ +#undef HAVE_NGTCP2_CRYPTO_QUICTLS_FROM_OSSL_ENCRYPTION_LEVEL + +/* Define to 1 if the system has the type `ngtcp2_encryption_level'. */ +#undef HAVE_NGTCP2_ENCRYPTION_LEVEL + +/* Define to 1 if you have the header file. + */ +#undef HAVE_NGTCP2_NGTCP2_CRYPTO_OPENSSL_H + +/* Define to 1 if you have the header file. + */ +#undef HAVE_NGTCP2_NGTCP2_CRYPTO_QUICTLS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NGTCP2_NGTCP2_H + /* Use libnss for crypto */ #undef HAVE_NSS @@ -587,6 +654,9 @@ /* 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 `SSL_is_quic' function. */ +#undef HAVE_SSL_IS_QUIC + /* Define to 1 if you have the `SSL_set1_host' function. */ #undef HAVE_SSL_SET1_HOST @@ -629,6 +699,23 @@ /* Define to 1 if `ipi_spec_dst' is a member of `struct in_pktinfo'. */ #undef HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST +/* Define to 1 if `tokenlen' is a member of `struct ngtcp2_pkt_hd'. */ +#undef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN + +/* Define to 1 if `max_tx_udp_payload_size' is a member of `struct + ngtcp2_settings'. */ +#undef HAVE_STRUCT_NGTCP2_SETTINGS_MAX_TX_UDP_PAYLOAD_SIZE + +/* Define to 1 if `tokenlen' is a member of `struct ngtcp2_settings'. */ +#undef HAVE_STRUCT_NGTCP2_SETTINGS_TOKENLEN + +/* Define to 1 if `original_dcid_present' is a member of `struct + ngtcp2_transport_params'. */ +#undef HAVE_STRUCT_NGTCP2_TRANSPORT_PARAMS_ORIGINAL_DCID_PRESENT + +/* Define to 1 if the system has the type `struct ngtcp2_version_cid'. */ +#undef HAVE_STRUCT_NGTCP2_VERSION_CID + /* Define to 1 if `sun_len' is a member of `struct sockaddr_un'. */ #undef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN @@ -1497,6 +1584,9 @@ struct sockaddr_storage; # define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__) # define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__) # define strdup(s) unbound_stat_strdup_log(s, __FILE__, __LINE__, __func__) +#ifdef HAVE_REALLOCARRAY +# define reallocarray(p,n,s) unbound_stat_reallocarray_log(p, n, s, __FILE__, __LINE__, __func__) +#endif void *unbound_stat_malloc(size_t size); void *unbound_stat_calloc(size_t nmemb, size_t size); void unbound_stat_free(void *ptr); @@ -1509,6 +1599,8 @@ void unbound_stat_free_log(void *ptr, co const char* func); void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file, int line, const char* func); +void *unbound_stat_reallocarray_log(void *ptr, size_t nmemb, size_t size, + const char* file, int line, const char* func); char *unbound_stat_strdup_log(const char *s, const char* file, int line, const char* func); #elif defined(UNBOUND_ALLOC_LITE) @@ -1521,6 +1613,8 @@ char *unbound_stat_strdup_log(const char #define UNBOUND_DNS_OVER_TLS_PORT 853 /** default port for DNS over HTTPS traffic. */ #define UNBOUND_DNS_OVER_HTTPS_PORT 443 +/** default port for DNS over QUIC traffic. */ +#define UNBOUND_DNS_OVER_QUIC_PORT 853 /** default port for unbound control traffic, registered port with IANA, ub-dns-control 8953/tcp unbound dns nameserver control */ #define UNBOUND_CONTROL_PORT 8953 Index: configure =================================================================== RCS file: /cvs/src/usr.sbin/unbound/configure,v diff -u -p -r1.56 configure --- configure 7 Oct 2024 15:38:22 -0000 1.56 +++ configure 7 Feb 2025 21:25:43 -0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for unbound 1.21.1. +# Generated by GNU Autoconf 2.71 for unbound 1.22.0. # # Report bugs to . # @@ -622,8 +622,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='unbound' PACKAGE_TARNAME='unbound' -PACKAGE_VERSION='1.21.1' -PACKAGE_STRING='unbound 1.21.1' +PACKAGE_VERSION='1.22.0' +PACKAGE_STRING='unbound 1.22.0' PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues' PACKAGE_URL='' @@ -771,7 +771,6 @@ RANLIB ac_ct_AR DLLTOOL OBJDUMP -FILECMD LN_S NM ac_ct_DUMPBIN @@ -922,6 +921,7 @@ with_libevent with_libexpat with_libhiredis with_libnghttp2 +with_libngtcp2 enable_static_exe enable_fully_static enable_lock_checks @@ -1509,7 +1509,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 unbound 1.21.1 to adapt to many kinds of systems. +\`configure' configures unbound 1.22.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1575,7 +1575,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of unbound 1.21.1:";; + short | recursive ) echo "Configuration of unbound 1.22.0:";; esac cat <<\_ACEOF @@ -1710,6 +1710,7 @@ Optional Packages: --with-libexpat=path specify explicit path for libexpat. --with-libhiredis=path specify explicit path for libhiredis. --with-libnghttp2=path specify explicit path for libnghttp2. + --with-libngtcp2=path specify explicit path for libngtcp2, for QUIC. --with-dnstap-socket-path=pathname set default dnstap socket path --with-protobuf-c=path Path where protobuf-c is installed, for dnstap @@ -1823,7 +1824,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -unbound configure 1.21.1 +unbound configure 1.22.0 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2480,7 +2481,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 unbound $as_me 1.21.1, which was +It was created by unbound $as_me 1.22.0, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3242,13 +3243,13 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu UNBOUND_VERSION_MAJOR=1 -UNBOUND_VERSION_MINOR=21 +UNBOUND_VERSION_MINOR=22 -UNBOUND_VERSION_MICRO=1 +UNBOUND_VERSION_MICRO=0 LIBUNBOUND_CURRENT=9 -LIBUNBOUND_REVISION=29 +LIBUNBOUND_REVISION=30 LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -3345,6 +3346,7 @@ LIBUNBOUND_AGE=1 # 1.20.0 had 9:27:1 # 1.21.0 had 9:28:1 # 1.21.1 had 9:29:1 +# 1.22.0 had 9:30:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -7782,8 +7784,8 @@ esac -macro_version='2.4.7' -macro_revision='2.4.7' +macro_version='2.4.6' +macro_revision='2.4.6' @@ -8267,13 +8269,13 @@ else mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac - case `"$tmp_nm" -B $lt_bad_file 2>&1 | $SED '1q'` in + case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) - case `"$tmp_nm" -p /dev/null 2>&1 | $SED '1q'` in + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 @@ -8411,7 +8413,7 @@ esac fi fi - case `$DUMPBIN -symbols -headers /dev/null 2>&1 | $SED '1q'` in + case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; @@ -8515,7 +8517,7 @@ else $as_nop lt_cv_sys_max_cmd_len=8192; ;; - bitrig* | darwin* | dragonfly* | freebsd* | midnightbsd* | netbsd* | openbsd*) + bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` @@ -8558,7 +8560,7 @@ else $as_nop sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then - lt_cv_sys_max_cmd_len=`echo $kargmax | $SED 's/.*[ ]//'` + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi @@ -8764,114 +8766,6 @@ esac if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}file", so it can be a program name with args. -set dummy ${ac_tool_prefix}file; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_FILECMD+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$FILECMD"; then - ac_cv_prog_FILECMD="$FILECMD" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_FILECMD="${ac_tool_prefix}file" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -FILECMD=$ac_cv_prog_FILECMD -if test -n "$FILECMD"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FILECMD" >&5 -printf "%s\n" "$FILECMD" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_FILECMD"; then - ac_ct_FILECMD=$FILECMD - # Extract the first word of "file", so it can be a program name with args. -set dummy file; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_FILECMD+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_FILECMD"; then - ac_cv_prog_ac_ct_FILECMD="$ac_ct_FILECMD" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_FILECMD="file" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_FILECMD=$ac_cv_prog_ac_ct_FILECMD -if test -n "$ac_ct_FILECMD"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_FILECMD" >&5 -printf "%s\n" "$ac_ct_FILECMD" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_FILECMD" = x; then - FILECMD=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - FILECMD=$ac_ct_FILECMD - fi -else - FILECMD="$ac_cv_prog_FILECMD" -fi - - - - - - - -if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 @@ -9014,7 +8908,7 @@ beos*) bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' - lt_cv_file_magic_cmd='$FILECMD -L' + lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; @@ -9048,14 +8942,14 @@ darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; -freebsd* | dragonfly* | midnightbsd*) +freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' - lt_cv_file_magic_cmd=$FILECMD + lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac @@ -9069,7 +8963,7 @@ haiku*) ;; hpux10.20* | hpux11*) - lt_cv_file_magic_cmd=$FILECMD + lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' @@ -9116,7 +9010,7 @@ netbsd*) newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' - lt_cv_file_magic_cmd=$FILECMD + lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; @@ -9489,29 +9383,13 @@ esac fi : ${AR=ar} +: ${AR_FLAGS=cru} -# Use ARFLAGS variable as AR's operation code to sync the variable naming with -# Automake. If both AR_FLAGS and ARFLAGS are specified, AR_FLAGS should have -# higher priority because thats what people were doing historically (setting -# ARFLAGS for automake and AR_FLAGS for libtool). FIXME: Make the AR_FLAGS -# variable obsoleted/removed. - -test ${AR_FLAGS+y} || AR_FLAGS=${ARFLAGS-cr} -lt_ar_flags=$AR_FLAGS - - - - - - -# Make AR_FLAGS overridable by 'make ARFLAGS='. Don't try to run-time override -# by AR_FLAGS because that was never working and AR_FLAGS is about to die. - @@ -9975,7 +9853,7 @@ esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. - lt_cv_sys_global_symbol_to_import="$SED -n -e 's/^I .* \(.*\)$/\1/p'" + lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" @@ -9993,20 +9871,20 @@ fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. -lt_cv_sys_global_symbol_to_cdecl="$SED -n"\ +lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address -lt_cv_sys_global_symbol_to_c_name_address="$SED -n"\ +lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. -lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="$SED -n"\ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ @@ -10030,7 +9908,7 @@ for ac_symprfx in "" "_"; do if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. - # Also find C++ and __fastcall symbols from MSVC++ or ICC, + # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ @@ -10048,9 +9926,9 @@ for ac_symprfx in "" "_"; do " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else - lt_cv_sys_global_symbol_pipe="$SED -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi - lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | $SED '/ __gnu_lto/d'" + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no @@ -10253,7 +10131,7 @@ case $with_sysroot in #( fi ;; #( /*) - lt_sysroot=`echo "$with_sysroot" | $SED -e "$sed_quote_subst"` + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( @@ -10378,7 +10256,7 @@ ia64-*-hpux*) ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; @@ -10399,7 +10277,7 @@ ia64-*-hpux*) printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test yes = "$lt_cv_prog_gnu_ld"; then - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; @@ -10411,7 +10289,7 @@ ia64-*-hpux*) ;; esac else - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; @@ -10437,7 +10315,7 @@ mips64*-*linux*) printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then emul=elf - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; @@ -10445,7 +10323,7 @@ mips64*-*linux*) emul="${emul}64" ;; esac - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; @@ -10453,7 +10331,7 @@ mips64*-*linux*) emul="${emul}ltsmip" ;; esac - case `$FILECMD conftest.$ac_objext` in + case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; @@ -10477,14 +10355,14 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux* ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - case `$FILECMD conftest.o` in + case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) - case `$FILECMD conftest.o` in + case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; @@ -10592,7 +10470,7 @@ printf "%s\n" "$lt_cv_cc_needs_belf" >&6 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - case `$FILECMD conftest.o` in + case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) @@ -11375,8 +11253,8 @@ int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 - echo "$AR $AR_FLAGS libconftest.a conftest.o" >&5 - $AR $AR_FLAGS libconftest.a conftest.o 2>&5 + echo "$AR cru libconftest.a conftest.o" >&5 + $AR cru libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF @@ -11403,12 +11281,17 @@ printf "%s\n" "$lt_cv_ld_force_load" >&6 _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; - darwin*) - case $MACOSX_DEPLOYMENT_TARGET,$host in - 10.[012],*|,*powerpc*-darwin[5-8]*) - _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; - *) - _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[91]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[012][,.]*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac @@ -11769,8 +11652,8 @@ esac ofile=libtool can_build_shared=yes -# All known linkers require a '.a' archive for static linking (except MSVC and -# ICC, which need '.lib'). +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld @@ -12278,7 +12161,7 @@ lt_prog_compiler_static= lt_prog_compiler_static='-qstaticlink' ;; *) - case `$CC -V 2>&1 | $SED 5q` in + case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' @@ -12701,15 +12584,15 @@ printf %s "checking whether the $compile case $host_os in cygwin* | mingw* | pw32* | cegcc*) - # FIXME: the MSVC++ and ICC port hasn't been tested in a loooong time + # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using - # Microsoft Visual C++ or Intel C++ Compiler. + # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++ or ICC) + # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) @@ -12761,7 +12644,7 @@ printf %s "checking whether the $compile whole_archive_flag_spec= fi supports_anon_versioning=no - case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in + case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... @@ -12873,7 +12756,6 @@ _LT_EOF emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes - file_list_spec='@' ;; interix[3-9]*) @@ -12888,7 +12770,7 @@ _LT_EOF # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) @@ -12931,7 +12813,7 @@ _LT_EOF compiler_needs_object=yes ;; esac - case `$CC -V 2>&1 | $SED 5q` in + case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes @@ -12943,7 +12825,7 @@ _LT_EOF if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi @@ -12959,7 +12841,7 @@ _LT_EOF archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi @@ -13091,7 +12973,7 @@ _LT_EOF if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else - export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no @@ -13362,12 +13244,12 @@ fi cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using - # Microsoft Visual C++ or Intel C++ Compiler. + # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in - cl* | icl*) - # Native MSVC or ICC + cl*) + # Native MSVC hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported always_export_symbols=yes @@ -13408,7 +13290,7 @@ fi fi' ;; *) - # Assume MSVC and ICC wrapper + # Assume MSVC wrapper hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. @@ -13449,8 +13331,8 @@ fi output_verbose_link_cmd=func_echo_all archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" - archive_expsym_cmds="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" - module_expsym_cmds="$SED -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" else ld_shlibs=no @@ -13484,7 +13366,7 @@ fi ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | dragonfly* | midnightbsd*) + freebsd* | dragonfly*) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes @@ -13735,7 +13617,6 @@ printf "%s\n" "$lt_cv_irix_exported_symb emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes - file_list_spec='@' ;; osf3*) @@ -14428,7 +14309,7 @@ cygwin* | mingw* | pw32* | cegcc*) case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo $libname | $SED -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" ;; @@ -14438,14 +14319,14 @@ cygwin* | mingw* | pw32* | cegcc*) ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo $libname | $SED -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; - *,cl* | *,icl*) - # Native MSVC or ICC + *,cl*) + # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' @@ -14464,7 +14345,7 @@ cygwin* | mingw* | pw32* | cegcc*) done IFS=$lt_save_ifs # Convert to MSYS style. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form @@ -14501,7 +14382,7 @@ cygwin* | mingw* | pw32* | cegcc*) ;; *) - # Assume MSVC and ICC wrapper + # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; @@ -14534,7 +14415,7 @@ dgux*) shlibpath_var=LD_LIBRARY_PATH ;; -freebsd* | dragonfly* | midnightbsd*) +freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then @@ -15690,41 +15571,30 @@ striplib= old_striplib= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 printf %s "checking whether stripping libraries is possible... " >&6; } -if test -z "$STRIP"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -else - if $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then - old_striplib="$STRIP --strip-debug" - striplib="$STRIP --strip-unneeded" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - else - case $host_os in - darwin*) - # FIXME - insert some real tests, host_os isn't really good enough +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - ;; - freebsd*) - if $STRIP -V 2>&1 | $GREP "elftoolchain" >/dev/null; then - old_striplib="$STRIP --strip-debug" - striplib="$STRIP --strip-unneeded" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } - fi - ;; - *) + else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - ;; - esac - fi + fi + ;; + *) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + ;; + esac fi @@ -17840,8 +17710,8 @@ then : have_systemd=no pkg_failed=no -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libsystemd" >&5 -printf %s "checking for libsystemd... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SYSTEMD" >&5 +printf %s "checking for SYSTEMD... " >&6; } if test -n "$SYSTEMD_CFLAGS"; then pkg_cv_SYSTEMD_CFLAGS="$SYSTEMD_CFLAGS" @@ -17881,7 +17751,7 @@ fi if test $pkg_failed = yes; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then @@ -17890,14 +17760,14 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd" 2>&1` + SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd" 2>&1` else - SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd" 2>&1` + SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$SYSTEMD_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$SYSTEMD_PKG_ERRORS" >&5 - as_fn_error $? "Package requirements (libsystemd) were not met: + as_fn_error $? "Package requirements (libsystemd) were not met: $SYSTEMD_PKG_ERRORS @@ -17908,9 +17778,9 @@ Alternatively, you may set the environme and SYSTEMD_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full @@ -17923,11 +17793,11 @@ See the pkg-config man page for more det To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else - SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS - SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS + SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS + SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - have_systemd=yes + have_systemd=yes fi if test "x$have_systemd" != "xyes" then : @@ -17935,8 +17805,8 @@ then : have_systemd_daemon=no pkg_failed=no -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libsystemd-daemon" >&5 -printf %s "checking for libsystemd-daemon... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SYSTEMD_DAEMON" >&5 +printf %s "checking for SYSTEMD_DAEMON... " >&6; } if test -n "$SYSTEMD_DAEMON_CFLAGS"; then pkg_cv_SYSTEMD_DAEMON_CFLAGS="$SYSTEMD_DAEMON_CFLAGS" @@ -17976,7 +17846,7 @@ fi if test $pkg_failed = yes; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then @@ -17985,14 +17855,14 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - SYSTEMD_DAEMON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd-daemon" 2>&1` + SYSTEMD_DAEMON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd-daemon" 2>&1` else - SYSTEMD_DAEMON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd-daemon" 2>&1` + SYSTEMD_DAEMON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd-daemon" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$SYSTEMD_DAEMON_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$SYSTEMD_DAEMON_PKG_ERRORS" >&5 - as_fn_error $? "Package requirements (libsystemd-daemon) were not met: + as_fn_error $? "Package requirements (libsystemd-daemon) were not met: $SYSTEMD_DAEMON_PKG_ERRORS @@ -18003,9 +17873,9 @@ Alternatively, you may set the environme and SYSTEMD_DAEMON_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full @@ -18018,11 +17888,11 @@ See the pkg-config man page for more det To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else - SYSTEMD_DAEMON_CFLAGS=$pkg_cv_SYSTEMD_DAEMON_CFLAGS - SYSTEMD_DAEMON_LIBS=$pkg_cv_SYSTEMD_DAEMON_LIBS + SYSTEMD_DAEMON_CFLAGS=$pkg_cv_SYSTEMD_DAEMON_CFLAGS + SYSTEMD_DAEMON_LIBS=$pkg_cv_SYSTEMD_DAEMON_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - have_systemd_daemon=yes + have_systemd_daemon=yes fi if test "x$have_systemd_daemon" = "xyes" then : @@ -22337,6 +22207,353 @@ printf "%s\n" "#define HAVE_DECL_NGHTTP2 fi +# ngtcp2 + +# Check whether --with-libngtcp2 was given. +if test ${with_libngtcp2+y} +then : + withval=$with_libngtcp2; +else $as_nop + withval="no" +fi + +found_libngtcp2="no" +if test x_$withval = x_yes -o x_$withval != x_no; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libngtcp2" >&5 +printf %s "checking for libngtcp2... " >&6; } + if test x_$withval = x_ -o x_$withval = x_yes; then + withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" + fi + for dir in $withval ; do + if test -f "$dir/include/ngtcp2/ngtcp2.h"; then + found_libngtcp2="yes" + if test "$dir" != "/usr"; then + CPPFLAGS="$CPPFLAGS -I$dir/include" + LDFLAGS="$LDFLAGS -L$dir/lib" + fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: found in $dir" >&5 +printf "%s\n" "found in $dir" >&6; } + +printf "%s\n" "#define HAVE_NGTCP2 1" >>confdefs.h + + LIBS="$LIBS -lngtcp2" + break; + fi + done + if test x_$found_libngtcp2 != x_yes; then + as_fn_error $? "Could not find libngtcp2, ngtcp2.h" "$LINENO" 5 + fi + ac_fn_c_check_header_compile "$LINENO" "ngtcp2/ngtcp2.h" "ac_cv_header_ngtcp2_ngtcp2_h" "$ac_includes_default +" +if test "x$ac_cv_header_ngtcp2_ngtcp2_h" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_NGTCP2_H 1" >>confdefs.h + +fi +ac_fn_c_check_header_compile "$LINENO" "ngtcp2/ngtcp2_crypto_openssl.h" "ac_cv_header_ngtcp2_ngtcp2_crypto_openssl_h" "$ac_includes_default +" +if test "x$ac_cv_header_ngtcp2_ngtcp2_crypto_openssl_h" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_NGTCP2_CRYPTO_OPENSSL_H 1" >>confdefs.h + +fi +ac_fn_c_check_header_compile "$LINENO" "ngtcp2/ngtcp2_crypto_quictls.h" "ac_cv_header_ngtcp2_ngtcp2_crypto_quictls_h" "$ac_includes_default +" +if test "x$ac_cv_header_ngtcp2_ngtcp2_crypto_quictls_h" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_NGTCP2_CRYPTO_QUICTLS_H 1" >>confdefs.h + +fi + + ac_fn_check_decl "$LINENO" "ngtcp2_conn_server_new" "ac_cv_have_decl_ngtcp2_conn_server_new" "$ac_includes_default + #include + +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl_ngtcp2_conn_server_new" = xyes +then : + ac_have_decl=1 +else $as_nop + ac_have_decl=0 +fi +printf "%s\n" "#define HAVE_DECL_NGTCP2_CONN_SERVER_NEW $ac_have_decl" >>confdefs.h + + ac_fn_check_decl "$LINENO" "ngtcp2_crypto_encrypt_cb" "ac_cv_have_decl_ngtcp2_crypto_encrypt_cb" "$ac_includes_default + #include + +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl_ngtcp2_crypto_encrypt_cb" = xyes +then : + ac_have_decl=1 +else $as_nop + ac_have_decl=0 +fi +printf "%s\n" "#define HAVE_DECL_NGTCP2_CRYPTO_ENCRYPT_CB $ac_have_decl" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_openssl" >&5 +printf %s "checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_openssl... " >&6; } +if test ${ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-lngtcp2_crypto_openssl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char ngtcp2_crypto_encrypt_cb (); +int +main (void) +{ +return ngtcp2_crypto_encrypt_cb (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb=yes +else $as_nop + ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb" >&5 +printf "%s\n" "$ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb" >&6; } +if test "x$ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb" = xyes +then : + LIBS="$LIBS -lngtcp2_crypto_openssl" +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_quictls" >&5 +printf %s "checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_quictls... " >&6; } +if test ${ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-lngtcp2_crypto_quictls $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char ngtcp2_crypto_encrypt_cb (); +int +main (void) +{ +return ngtcp2_crypto_encrypt_cb (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb=yes +else $as_nop + ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb" >&5 +printf "%s\n" "$ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb" >&6; } +if test "x$ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb" = xyes +then : + LIBS="$LIBS -lngtcp2_crypto_quictls" +fi + + ac_fn_c_check_func "$LINENO" "ngtcp2_crypto_encrypt_cb" "ac_cv_func_ngtcp2_crypto_encrypt_cb" +if test "x$ac_cv_func_ngtcp2_crypto_encrypt_cb" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_CRYPTO_ENCRYPT_CB 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "ngtcp2_ccerr_default" "ac_cv_func_ngtcp2_ccerr_default" +if test "x$ac_cv_func_ngtcp2_ccerr_default" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_CCERR_DEFAULT 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "ngtcp2_conn_in_closing_period" "ac_cv_func_ngtcp2_conn_in_closing_period" +if test "x$ac_cv_func_ngtcp2_conn_in_closing_period" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_CONN_IN_CLOSING_PERIOD 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "ngtcp2_conn_in_draining_period" "ac_cv_func_ngtcp2_conn_in_draining_period" +if test "x$ac_cv_func_ngtcp2_conn_in_draining_period" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_CONN_IN_DRAINING_PERIOD 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "ngtcp2_conn_get_max_local_streams_uni" "ac_cv_func_ngtcp2_conn_get_max_local_streams_uni" +if test "x$ac_cv_func_ngtcp2_conn_get_max_local_streams_uni" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_CONN_GET_MAX_LOCAL_STREAMS_UNI 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "ngtcp2_crypto_quictls_from_ossl_encryption_level" "ac_cv_func_ngtcp2_crypto_quictls_from_ossl_encryption_level" +if test "x$ac_cv_func_ngtcp2_crypto_quictls_from_ossl_encryption_level" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_CRYPTO_QUICTLS_FROM_OSSL_ENCRYPTION_LEVEL 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "ngtcp2_crypto_quictls_configure_server_context" "ac_cv_func_ngtcp2_crypto_quictls_configure_server_context" +if test "x$ac_cv_func_ngtcp2_crypto_quictls_configure_server_context" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "ngtcp2_crypto_quictls_configure_client_context" "ac_cv_func_ngtcp2_crypto_quictls_configure_client_context" +if test "x$ac_cv_func_ngtcp2_crypto_quictls_configure_client_context" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_CLIENT_CONTEXT 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "ngtcp2_conn_get_num_scid" "ac_cv_func_ngtcp2_conn_get_num_scid" +if test "x$ac_cv_func_ngtcp2_conn_get_num_scid" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_CONN_GET_NUM_SCID 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "ngtcp2_conn_tls_early_data_rejected" "ac_cv_func_ngtcp2_conn_tls_early_data_rejected" +if test "x$ac_cv_func_ngtcp2_conn_tls_early_data_rejected" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_CONN_TLS_EARLY_DATA_REJECTED 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "ngtcp2_conn_encode_0rtt_transport_params" "ac_cv_func_ngtcp2_conn_encode_0rtt_transport_params" +if test "x$ac_cv_func_ngtcp2_conn_encode_0rtt_transport_params" = xyes +then : + printf "%s\n" "#define HAVE_NGTCP2_CONN_ENCODE_0RTT_TRANSPORT_PARAMS 1" >>confdefs.h + +fi + + + for ac_func in SSL_is_quic +do : + ac_fn_c_check_func "$LINENO" "SSL_is_quic" "ac_cv_func_SSL_is_quic" +if test "x$ac_cv_func_SSL_is_quic" = xyes +then : + printf "%s\n" "#define HAVE_SSL_IS_QUIC 1" >>confdefs.h + +else $as_nop + as_fn_error $? "No QUIC support detected in OpenSSL. Need OpenSSL version with QUIC support to enable DNS over QUIC with libngtcp2." "$LINENO" 5 +fi + +done + ac_fn_c_check_type "$LINENO" "struct ngtcp2_version_cid" "ac_cv_type_struct_ngtcp2_version_cid" "$ac_includes_default + #include + +" +if test "x$ac_cv_type_struct_ngtcp2_version_cid" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_NGTCP2_VERSION_CID 1" >>confdefs.h + + +fi +ac_fn_c_check_type "$LINENO" "ngtcp2_encryption_level" "ac_cv_type_ngtcp2_encryption_level" "$ac_includes_default + #include + +" +if test "x$ac_cv_type_ngtcp2_encryption_level" = xyes +then : + +printf "%s\n" "#define HAVE_NGTCP2_ENCRYPTION_LEVEL 1" >>confdefs.h + + +fi + + ac_fn_c_check_member "$LINENO" "struct ngtcp2_pkt_hd" "tokenlen" "ac_cv_member_struct_ngtcp2_pkt_hd_tokenlen" "$ac_includes_default + #include + +" +if test "x$ac_cv_member_struct_ngtcp2_pkt_hd_tokenlen" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN 1" >>confdefs.h + + +fi +ac_fn_c_check_member "$LINENO" "struct ngtcp2_settings" "tokenlen" "ac_cv_member_struct_ngtcp2_settings_tokenlen" "$ac_includes_default + #include + +" +if test "x$ac_cv_member_struct_ngtcp2_settings_tokenlen" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_NGTCP2_SETTINGS_TOKENLEN 1" >>confdefs.h + + +fi +ac_fn_c_check_member "$LINENO" "struct ngtcp2_settings" "max_tx_udp_payload_size" "ac_cv_member_struct_ngtcp2_settings_max_tx_udp_payload_size" "$ac_includes_default + #include + +" +if test "x$ac_cv_member_struct_ngtcp2_settings_max_tx_udp_payload_size" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_NGTCP2_SETTINGS_MAX_TX_UDP_PAYLOAD_SIZE 1" >>confdefs.h + + +fi +ac_fn_c_check_member "$LINENO" "struct ngtcp2_transport_params" "original_dcid_present" "ac_cv_member_struct_ngtcp2_transport_params_original_dcid_present" "$ac_includes_default + #include + +" +if test "x$ac_cv_member_struct_ngtcp2_transport_params_original_dcid_present" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_NGTCP2_TRANSPORT_PARAMS_ORIGINAL_DCID_PRESENT 1" >>confdefs.h + + +fi + + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ngtcp2_conn_shutdown_stream has 4 arguments" >&5 +printf %s "checking whether ngtcp2_conn_shutdown_stream has 4 arguments... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +#include + +int +main (void) +{ + + (void)ngtcp2_conn_shutdown_stream(NULL, 0, 0, 0); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } + +printf "%s\n" "#define HAVE_NGTCP2_CONN_SHUTDOWN_STREAM4 1" >>confdefs.h + + +else $as_nop + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + +fi + # set static linking for uninstalled libraries if requested staticexe="" @@ -23920,10 +24137,12 @@ if test x_$enable_lock_checks = x_yes; t UBSYMS="-export-symbols clubsyms.def" cp ${srcdir}/libunbound/ubsyms.def clubsyms.def echo lock_protect >> clubsyms.def + echo lock_protect_place >> clubsyms.def echo lock_unprotect >> clubsyms.def echo lock_get_mem >> clubsyms.def echo checklock_start >> clubsyms.def echo checklock_stop >> clubsyms.def + echo checklock_set_output_name >> clubsyms.def echo checklock_lock >> clubsyms.def echo checklock_unlock >> clubsyms.def echo checklock_init >> clubsyms.def @@ -24019,8 +24238,8 @@ else $as_nop if test -n "$PKG_CONFIG"; then pkg_failed=no -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libprotobuf-c" >&5 -printf %s "checking for libprotobuf-c... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for PROTOBUFC" >&5 +printf %s "checking for PROTOBUFC... " >&6; } if test -n "$PROTOBUFC_CFLAGS"; then pkg_cv_PROTOBUFC_CFLAGS="$PROTOBUFC_CFLAGS" @@ -24060,7 +24279,7 @@ fi if test $pkg_failed = yes; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then @@ -24069,12 +24288,12 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - PROTOBUFC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libprotobuf-c" 2>&1` + PROTOBUFC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libprotobuf-c" 2>&1` else - PROTOBUFC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libprotobuf-c" 2>&1` + PROTOBUFC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libprotobuf-c" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$PROTOBUFC_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$PROTOBUFC_PKG_ERRORS" >&5 # pkg-config failed; try falling back to known values @@ -24092,7 +24311,7 @@ fi elif test $pkg_failed = untried; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } # pkg-config failed; try falling back to known values @@ -24110,8 +24329,8 @@ printf "%s\n" "no" >&6; } else - PROTOBUFC_CFLAGS=$pkg_cv_PROTOBUFC_CFLAGS - PROTOBUFC_LIBS=$pkg_cv_PROTOBUFC_LIBS + PROTOBUFC_CFLAGS=$pkg_cv_PROTOBUFC_CFLAGS + PROTOBUFC_LIBS=$pkg_cv_PROTOBUFC_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -24778,7 +24997,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 1 -version=1.21.1 +version=1.22.0 date=`date +'%b %e, %Y'` @@ -25290,7 +25509,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 unbound $as_me 1.21.1, which was +This file was extended by unbound $as_me 1.22.0, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -25358,7 +25577,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="\\ -unbound config.status 1.21.1 +unbound config.status 1.22.0 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" @@ -25518,7 +25737,6 @@ lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_t lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' -FILECMD='`$ECHO "$FILECMD" | $SED "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' @@ -25527,7 +25745,6 @@ want_nocaseglob='`$ECHO "$want_nocaseglo DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' -lt_ar_flags='`$ECHO "$lt_ar_flags" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' @@ -25648,7 +25865,6 @@ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ -FILECMD \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ @@ -25657,6 +25873,7 @@ want_nocaseglob \ DLLTOOL \ sharedlib_from_linklib_cmd \ AR \ +AR_FLAGS \ archiver_list_spec \ STRIP \ RANLIB \ @@ -26469,9 +26686,6 @@ to_host_file_cmd=$lt_cv_to_host_file_cmd # convert \$build files to toolchain format. to_tool_file_cmd=$lt_cv_to_tool_file_cmd -# A file(cmd) program that detects file types. -FILECMD=$lt_FILECMD - # An object symbol dumper. OBJDUMP=$lt_OBJDUMP @@ -26496,11 +26710,8 @@ sharedlib_from_linklib_cmd=$lt_sharedlib # The archiver. AR=$lt_AR -# Flags to create an archive (by configure). -lt_ar_flags=$lt_ar_flags - # Flags to create an archive. -AR_FLAGS=\${ARFLAGS-"\$lt_ar_flags"} +AR_FLAGS=$lt_AR_FLAGS # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec @@ -26876,7 +27087,7 @@ ltmain=$ac_aux_dir/ltmain.sh # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? - $SED '$q' "$ltmain" >> "$cfgfile" \ + sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || Index: configure.ac =================================================================== RCS file: /cvs/src/usr.sbin/unbound/configure.ac,v diff -u -p -r1.56 configure.ac --- configure.ac 7 Oct 2024 15:38:22 -0000 1.56 +++ configure.ac 7 Feb 2025 21:25:43 -0000 @@ -10,15 +10,15 @@ sinclude(dnscrypt/dnscrypt.m4) # must be numbers. ac_defun because of later processing m4_define([VERSION_MAJOR],[1]) -m4_define([VERSION_MINOR],[21]) -m4_define([VERSION_MICRO],[1]) +m4_define([VERSION_MINOR],[22]) +m4_define([VERSION_MICRO],[0]) AC_INIT([unbound],m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]),[unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues],[unbound]) AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR]) AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR]) AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO]) LIBUNBOUND_CURRENT=9 -LIBUNBOUND_REVISION=29 +LIBUNBOUND_REVISION=30 LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -115,6 +115,7 @@ LIBUNBOUND_AGE=1 # 1.20.0 had 9:27:1 # 1.21.0 had 9:28:1 # 1.21.1 had 9:29:1 +# 1.22.0 had 9:30:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -1578,6 +1579,64 @@ if test x_$withval = x_yes -o x_$withval ]) fi +# ngtcp2 +AC_ARG_WITH(libngtcp2, AS_HELP_STRING([--with-libngtcp2=path],[specify explicit path for libngtcp2, for QUIC.]), + [ ],[ withval="no" ]) +found_libngtcp2="no" +if test x_$withval = x_yes -o x_$withval != x_no; then + AC_MSG_CHECKING(for libngtcp2) + if test x_$withval = x_ -o x_$withval = x_yes; then + withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" + fi + for dir in $withval ; do + if test -f "$dir/include/ngtcp2/ngtcp2.h"; then + found_libngtcp2="yes" + dnl assume /usr is in default path. + if test "$dir" != "/usr"; then + CPPFLAGS="$CPPFLAGS -I$dir/include" + LDFLAGS="$LDFLAGS -L$dir/lib" + fi + AC_MSG_RESULT(found in $dir) + AC_DEFINE([HAVE_NGTCP2], [1], [Define this to use ngtcp2.]) + LIBS="$LIBS -lngtcp2" + break; + fi + done + if test x_$found_libngtcp2 != x_yes; then + AC_MSG_ERROR([Could not find libngtcp2, ngtcp2.h]) + fi + AC_CHECK_HEADERS([ngtcp2/ngtcp2.h ngtcp2/ngtcp2_crypto_openssl.h ngtcp2/ngtcp2_crypto_quictls.h],,, [AC_INCLUDES_DEFAULT]) + AC_CHECK_DECLS([ngtcp2_conn_server_new], [], [], [AC_INCLUDES_DEFAULT + #include + ]) + AC_CHECK_DECLS([ngtcp2_crypto_encrypt_cb], [], [], [AC_INCLUDES_DEFAULT + #include + ]) + AC_CHECK_LIB([ngtcp2_crypto_openssl], [ngtcp2_crypto_encrypt_cb], [ LIBS="$LIBS -lngtcp2_crypto_openssl" ]) + AC_CHECK_LIB([ngtcp2_crypto_quictls], [ngtcp2_crypto_encrypt_cb], [ LIBS="$LIBS -lngtcp2_crypto_quictls" ]) + AC_CHECK_FUNCS([ngtcp2_crypto_encrypt_cb ngtcp2_ccerr_default ngtcp2_conn_in_closing_period ngtcp2_conn_in_draining_period ngtcp2_conn_get_max_local_streams_uni ngtcp2_crypto_quictls_from_ossl_encryption_level ngtcp2_crypto_quictls_configure_server_context ngtcp2_crypto_quictls_configure_client_context ngtcp2_conn_get_num_scid ngtcp2_conn_tls_early_data_rejected ngtcp2_conn_encode_0rtt_transport_params]) + AC_CHECK_FUNCS([SSL_is_quic], [], [AC_MSG_ERROR([No QUIC support detected in OpenSSL. Need OpenSSL version with QUIC support to enable DNS over QUIC with libngtcp2.])]) + AC_CHECK_TYPES([struct ngtcp2_version_cid, ngtcp2_encryption_level],,,[AC_INCLUDES_DEFAULT + #include + ]) + AC_CHECK_MEMBERS([struct ngtcp2_pkt_hd.tokenlen, struct ngtcp2_settings.tokenlen, struct ngtcp2_settings.max_tx_udp_payload_size, struct ngtcp2_transport_params.original_dcid_present],,,[AC_INCLUDES_DEFAULT + #include + ]) + + AC_MSG_CHECKING([whether ngtcp2_conn_shutdown_stream has 4 arguments]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT +#include + ],[ + (void)ngtcp2_conn_shutdown_stream(NULL, 0, 0, 0); + ])],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_NGTCP2_CONN_SHUTDOWN_STREAM4, 1, [Define if ngtcp2_conn_shutdown_stream has 4 arguments.]) + ],[ + AC_MSG_RESULT(no) + ]) + +fi + # set static linking for uninstalled libraries if requested AC_SUBST(staticexe) staticexe="" @@ -1893,10 +1952,12 @@ if test x_$enable_lock_checks = x_yes; t UBSYMS="-export-symbols clubsyms.def" cp ${srcdir}/libunbound/ubsyms.def clubsyms.def echo lock_protect >> clubsyms.def + echo lock_protect_place >> clubsyms.def echo lock_unprotect >> clubsyms.def echo lock_get_mem >> clubsyms.def echo checklock_start >> clubsyms.def echo checklock_stop >> clubsyms.def + echo checklock_set_output_name >> clubsyms.def echo checklock_lock >> clubsyms.def echo checklock_unlock >> clubsyms.def echo checklock_init >> clubsyms.def @@ -2331,6 +2392,9 @@ struct sockaddr_storage; # define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__) # define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__) # define strdup(s) unbound_stat_strdup_log(s, __FILE__, __LINE__, __func__) +#ifdef HAVE_REALLOCARRAY +# define reallocarray(p,n,s) unbound_stat_reallocarray_log(p, n, s, __FILE__, __LINE__, __func__) +#endif void *unbound_stat_malloc(size_t size); void *unbound_stat_calloc(size_t nmemb, size_t size); void unbound_stat_free(void *ptr); @@ -2343,6 +2407,8 @@ void unbound_stat_free_log(void *ptr, co const char* func); void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file, int line, const char* func); +void *unbound_stat_reallocarray_log(void *ptr, size_t nmemb, size_t size, + const char* file, int line, const char* func); char *unbound_stat_strdup_log(const char *s, const char* file, int line, const char* func); #elif defined(UNBOUND_ALLOC_LITE) @@ -2355,6 +2421,8 @@ char *unbound_stat_strdup_log(const char #define UNBOUND_DNS_OVER_TLS_PORT 853 /** default port for DNS over HTTPS traffic. */ #define UNBOUND_DNS_OVER_HTTPS_PORT 443 +/** default port for DNS over QUIC traffic. */ +#define UNBOUND_DNS_OVER_QUIC_PORT 853 /** default port for unbound control traffic, registered port with IANA, ub-dns-control 8953/tcp unbound dns nameserver control */ #define UNBOUND_CONTROL_PORT 8953 Index: install-sh =================================================================== RCS file: /cvs/src/usr.sbin/unbound/install-sh,v diff -u -p -r1.6 install-sh --- install-sh 7 Oct 2024 15:38:22 -0000 1.6 +++ install-sh 7 Feb 2025 21:25:43 -0000 @@ -1,7 +1,7 @@ #!/usr/bin/sh # install - install a program, script, or datafile -scriptversion=2020-11-14.01; # UTC +scriptversion=2013-12-25.23; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -69,11 +69,6 @@ posix_mkdir= # Desired mode of installed file. mode=0755 -# Create dirs (including intermediate dirs) using mode 755. -# This is like GNU 'install' as of coreutils 8.32 (2020). -mkdir_umask=22 - -backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= @@ -104,28 +99,18 @@ Options: --version display version info and exit. -c (ignored) - -C install only if different (preserve data modification time) + -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. - -p pass -p to $cpprog. -s $stripprog installed files. - -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG - -By default, rm is invoked with -f; when overridden with RMPROG, -it's up to you to specify -f if you want it. - -If -S is not specified, no backups are attempted. - -Email bug reports to bug-automake@gnu.org. -Automake home page: https://www.gnu.org/software/automake/ " while test $# -ne 0; do @@ -152,13 +137,8 @@ while test $# -ne 0; do -o) chowncmd="$chownprog $2" shift;; - -p) cpprog="$cpprog -p";; - -s) stripcmd=$stripprog;; - -S) backupsuffix="$2" - shift;; - -t) is_target_a_directory=always dst_arg=$2 @@ -275,10 +255,6 @@ do dstdir=$dst test -d "$dstdir" dstdir_status=$? - # Don't chown directories that already exist. - if test $dstdir_status = 0; then - chowncmd="" - fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command @@ -295,18 +271,15 @@ do fi dst=$dst_arg - # If destination is a directory, append the input filename. + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst - dstbase=`basename "$src"` - case $dst in - */) dst=$dst$dstbase;; - *) dst=$dst/$dstbase;; - esac + dst=$dstdir/`basename "$src"` dstdir_status=0 else dstdir=`dirname "$dst"` @@ -315,16 +288,27 @@ do fi fi - case $dstdir in - */) dstdirslash=$dstdir;; - *) dstdirslash=$dstdir/;; - esac - obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then @@ -334,49 +318,43 @@ do fi posix_mkdir=false - # The $RANDOM variable is not portable (e.g., dash). Use it - # here however when possible just to lower collision chance. - tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - - trap ' - ret=$? - rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null - exit $ret - ' 0 - - # Because "mkdir -p" follows existing symlinks and we likely work - # directly in world-writeable /tmp, make sure that the '$tmpdir' - # directory is successfully created first before we actually test - # 'mkdir -p'. - if (umask $mkdir_umask && - $mkdirprog $mkdir_mode "$tmpdir" && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 - then - if test -z "$dir_arg" || { - # Check for POSIX incompatibilities with -m. - # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writable bit of parent directory when it shouldn't. - # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - test_tmpdir="$tmpdir/a" - ls_ld_tmpdir=`ls -ld "$test_tmpdir"` - case $ls_ld_tmpdir in - d????-?r-*) different_mode=700;; - d????-?--*) different_mode=755;; - *) false;; - esac && - $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` - test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" - } - } - then posix_mkdir=: - fi - rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" - else - # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null - fi - trap '' 0;; + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; esac if @@ -387,7 +365,7 @@ do then : else - # mkdir does not conform to POSIX, + # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. @@ -416,7 +394,7 @@ do prefixes= else if $posix_mkdir; then - (umask $mkdir_umask && + (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 @@ -449,25 +427,14 @@ do else # Make a couple of temp file names in the proper directory. - dsttmp=${dstdirslash}_inst.$$_ - rmtmp=${dstdirslash}_rm.$$_ + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. - (umask $cp_umask && - { test -z "$stripcmd" || { - # Create $dsttmp read-write so that cp doesn't create it read-only, - # which would cause strip to fail. - if test -z "$doit"; then - : >"$dsttmp" # No need to fork-exec 'touch'. - else - $doit touch "$dsttmp" - fi - } - } && - $doit_exec $cpprog "$src" "$dsttmp") && + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # @@ -493,13 +460,6 @@ do then rm -f "$dsttmp" else - # If $backupsuffix is set, and the file being installed - # already exists, attempt a backup. Don't worry if it fails, - # e.g., if mv doesn't support -f. - if test -n "$backupsuffix" && test -f "$dst"; then - $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null - fi - # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || @@ -514,9 +474,9 @@ do # file should still install successfully. { test ! -f "$dst" || - $doit $rmcmd "$dst" 2>/dev/null || + $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && - { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 @@ -533,9 +493,9 @@ do done # Local variables: -# eval: (add-hook 'before-save-hook 'time-stamp) +# eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC0" +# time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: Index: ltmain.sh =================================================================== RCS file: /cvs/src/usr.sbin/unbound/ltmain.sh,v diff -u -p -r1.5 ltmain.sh --- ltmain.sh 7 Oct 2024 15:38:22 -0000 1.5 +++ ltmain.sh 7 Feb 2025 21:25:44 -0000 @@ -1,12 +1,12 @@ -#! /usr/bin/env sh +#! /bin/sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in -## by inline-source v2019-02-19.15 +## by inline-source v2014-01-03.01 -# libtool (GNU libtool) 2.4.7 +# libtool (GNU libtool) 2.4.6 # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 -# Copyright (C) 1996-2019, 2021-2022 Free Software Foundation, Inc. +# Copyright (C) 1996-2015 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. @@ -31,8 +31,8 @@ PROGRAM=libtool PACKAGE=libtool -VERSION=2.4.7 -package_revision=2.4.7 +VERSION=2.4.6 +package_revision=2.4.6 ## ------ ## @@ -64,25 +64,34 @@ package_revision=2.4.7 # libraries, which are installed to $pkgauxdir. # Set a version string for this script. -scriptversion=2019-02-19.15; # UTC +scriptversion=2015-01-20.17; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 -# This is free software. There is NO warranty; not even for -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# -# Copyright (C) 2004-2019, 2021 Bootstrap Authors -# -# This file is dual licensed under the terms of the MIT license -# , and GPL version 2 or later -# . You must apply one of -# these licenses when using or redistributing this software or any of -# the files within it. See the URLs above, or the file `LICENSE` -# included in the Bootstrap distribution for the full license texts. +# Copyright (C) 2004-2015 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. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +# As a special exception to the GNU General Public License, if you distribute +# this file as part of a program or library that is built using GNU Libtool, +# you may include this file under the same distribution terms that you use +# for the rest of that program. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . -# Please report bugs or propose patches to: -# +# Please report bugs or propose patches to gary@gnu.org. ## ------ ## @@ -130,12 +139,9 @@ do _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done -# These NLS vars are set unconditionally (bootstrap issue #24). Unset those -# in case the environment reset is needed later and the $save_* variant is not -# defined (see the code above). -LC_ALL=C -LANGUAGE=C -export LANGUAGE LC_ALL + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Make sure IFS has a sensible default sp=' ' @@ -153,26 +159,6 @@ if test "${PATH_SEPARATOR+set}" != set; fi -# func_unset VAR -# -------------- -# Portably unset VAR. -# In some shells, an 'unset VAR' statement leaves a non-zero return -# status if VAR is already unset, which might be problematic if the -# statement is used at the end of a function (thus poisoning its return -# value) or when 'set -e' is active (causing even a spurious abort of -# the script in this case). -func_unset () -{ - { eval $1=; (eval unset $1) >/dev/null 2>&1 && eval unset $1 || : ; } -} - - -# Make sure CDPATH doesn't cause `cd` commands to output the target dir. -func_unset CDPATH - -# Make sure ${,E,F}GREP behave sanely. -func_unset GREP_OPTIONS - ## ------------------------- ## ## Locate command utilities. ## @@ -273,7 +259,7 @@ test -z "$SED" && { rm -f conftest.in conftest.tmp conftest.nl conftest.out } - func_path_progs "sed gsed" func_check_prog_sed "$PATH:/usr/xpg4/bin" + func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin rm -f conftest.sed SED=$func_path_progs_result } @@ -309,7 +295,7 @@ test -z "$GREP" && { rm -f conftest.in conftest.tmp conftest.nl conftest.out } - func_path_progs "grep ggrep" func_check_prog_grep "$PATH:/usr/xpg4/bin" + func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin GREP=$func_path_progs_result } @@ -374,35 +360,6 @@ sed_double_backslash="\ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" -# require_check_ifs_backslash -# --------------------------- -# Check if we can use backslash as IFS='\' separator, and set -# $check_ifs_backshlash_broken to ':' or 'false'. -require_check_ifs_backslash=func_require_check_ifs_backslash -func_require_check_ifs_backslash () -{ - _G_save_IFS=$IFS - IFS='\' - _G_check_ifs_backshlash='a\\b' - for _G_i in $_G_check_ifs_backshlash - do - case $_G_i in - a) - check_ifs_backshlash_broken=false - ;; - '') - break - ;; - *) - check_ifs_backshlash_broken=: - break - ;; - esac - done - IFS=$_G_save_IFS - require_check_ifs_backslash=: -} - ## ----------------- ## ## Global variables. ## @@ -623,16 +580,16 @@ if test yes = "$_G_HAVE_PLUSEQ_OP"; then { $debug_cmd - func_quote_arg pretty "$2" - eval "$1+=\\ \$func_quote_arg_result" + func_quote_for_eval "$2" + eval "$1+=\\ \$func_quote_for_eval_result" }' else func_append_quoted () { $debug_cmd - func_quote_arg pretty "$2" - eval "$1=\$$1\\ \$func_quote_arg_result" + func_quote_for_eval "$2" + eval "$1=\$$1\\ \$func_quote_for_eval_result" } fi @@ -1134,203 +1091,85 @@ func_relative_path () } -# func_quote_portable EVAL ARG -# ---------------------------- -# Internal function to portably implement func_quote_arg. Note that we still -# keep attention to performance here so we as much as possible try to avoid -# calling sed binary (so far O(N) complexity as long as func_append is O(1)). -func_quote_portable () +# func_quote_for_eval ARG... +# -------------------------- +# Aesthetically quote ARGs to be evaled later. +# This function returns two values: +# i) func_quote_for_eval_result +# double-quoted, suitable for a subsequent eval +# ii) func_quote_for_eval_unquoted_result +# has all characters that are still active within double +# quotes backslashified. +func_quote_for_eval () { $debug_cmd - $require_check_ifs_backslash - - func_quote_portable_result=$2 - - # one-time-loop (easy break) - while true - do - if $1; then - func_quote_portable_result=`$ECHO "$2" | $SED \ - -e "$sed_double_quote_subst" -e "$sed_double_backslash"` - break - fi - - # Quote for eval. - case $func_quote_portable_result in + func_quote_for_eval_unquoted_result= + func_quote_for_eval_result= + while test 0 -lt $#; do + case $1 in *[\\\`\"\$]*) - # Fallback to sed for $func_check_bs_ifs_broken=:, or when the string - # contains the shell wildcard characters. - case $check_ifs_backshlash_broken$func_quote_portable_result in - :*|*[\[\*\?]*) - func_quote_portable_result=`$ECHO "$func_quote_portable_result" \ - | $SED "$sed_quote_subst"` - break - ;; - esac + _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; + *) + _G_unquoted_arg=$1 ;; + esac + if test -n "$func_quote_for_eval_unquoted_result"; then + func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" + else + func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" + fi - func_quote_portable_old_IFS=$IFS - for _G_char in '\' '`' '"' '$' - do - # STATE($1) PREV($2) SEPARATOR($3) - set start "" "" - func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy - IFS=$_G_char - for _G_part in $func_quote_portable_result - do - case $1 in - quote) - func_append func_quote_portable_result "$3$2" - set quote "$_G_part" "\\$_G_char" - ;; - start) - set first "" "" - func_quote_portable_result= - ;; - first) - set quote "$_G_part" "" - ;; - esac - done - done - IFS=$func_quote_portable_old_IFS + case $_G_unquoted_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and variable expansion + # for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + _G_quoted_arg=\"$_G_unquoted_arg\" ;; - *) ;; + *) + _G_quoted_arg=$_G_unquoted_arg + ;; esac - break - done - func_quote_portable_unquoted_result=$func_quote_portable_result - case $func_quote_portable_result in - # double-quote args containing shell metacharacters to delay - # word splitting, command substitution and variable expansion - # for a subsequent eval. - # many bourne shells cannot handle close brackets correctly - # in scan sets, so we specify it separately. - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - func_quote_portable_result=\"$func_quote_portable_result\" - ;; - esac + if test -n "$func_quote_for_eval_result"; then + func_append func_quote_for_eval_result " $_G_quoted_arg" + else + func_append func_quote_for_eval_result "$_G_quoted_arg" + fi + shift + done } -# func_quotefast_eval ARG -# ----------------------- -# Quote one ARG (internal). This is equivalent to 'func_quote_arg eval ARG', -# but optimized for speed. Result is stored in $func_quotefast_eval. -if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then - printf -v _GL_test_printf_tilde %q '~' - if test '\~' = "$_GL_test_printf_tilde"; then - func_quotefast_eval () - { - printf -v func_quotefast_eval_result %q "$1" - } - else - # Broken older Bash implementations. Make those faster too if possible. - func_quotefast_eval () - { - case $1 in - '~'*) - func_quote_portable false "$1" - func_quotefast_eval_result=$func_quote_portable_result - ;; - *) - printf -v func_quotefast_eval_result %q "$1" - ;; - esac - } - fi -else - func_quotefast_eval () - { - func_quote_portable false "$1" - func_quotefast_eval_result=$func_quote_portable_result - } -fi - +# func_quote_for_expand ARG +# ------------------------- +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + $debug_cmd -# func_quote_arg MODEs ARG -# ------------------------ -# Quote one ARG to be evaled later. MODEs argument may contain zero or more -# specifiers listed below separated by ',' character. This function returns two -# values: -# i) func_quote_arg_result -# double-quoted (when needed), suitable for a subsequent eval -# ii) func_quote_arg_unquoted_result -# has all characters that are still active within double -# quotes backslashified. Available only if 'unquoted' is specified. -# -# Available modes: -# ---------------- -# 'eval' (default) -# - escape shell special characters -# 'expand' -# - the same as 'eval'; but do not quote variable references -# 'pretty' -# - request aesthetic output, i.e. '"a b"' instead of 'a\ b'. This might -# be used later in func_quote to get output like: 'echo "a b"' instead -# of 'echo a\ b'. This is slower than default on some shells. -# 'unquoted' -# - produce also $func_quote_arg_unquoted_result which does not contain -# wrapping double-quotes. -# -# Examples for 'func_quote_arg pretty,unquoted string': -# -# string | *_result | *_unquoted_result -# ------------+-----------------------+------------------- -# " | \" | \" -# a b | "a b" | a b -# "a b" | "\"a b\"" | \"a b\" -# * | "*" | * -# z="${x-$y}" | "z=\"\${x-\$y}\"" | z=\"\${x-\$y}\" -# -# Examples for 'func_quote_arg pretty,unquoted,expand string': -# -# string | *_result | *_unquoted_result -# --------------+---------------------+-------------------- -# z="${x-$y}" | "z=\"${x-$y}\"" | z=\"${x-$y}\" -func_quote_arg () -{ - _G_quote_expand=false - case ,$1, in - *,expand,*) - _G_quote_expand=: - ;; + case $1 in + *[\\\`\"]*) + _G_arg=`$ECHO "$1" | $SED \ + -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; + *) + _G_arg=$1 ;; esac - case ,$1, in - *,pretty,*|*,expand,*|*,unquoted,*) - func_quote_portable $_G_quote_expand "$2" - func_quote_arg_result=$func_quote_portable_result - func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result - ;; - *) - # Faster quote-for-eval for some shells. - func_quotefast_eval "$2" - func_quote_arg_result=$func_quotefast_eval_result + case $_G_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + _G_arg=\"$_G_arg\" ;; esac -} - -# func_quote MODEs ARGs... -# ------------------------ -# Quote all ARGs to be evaled later and join them into single command. See -# func_quote_arg's description for more info. -func_quote () -{ - $debug_cmd - _G_func_quote_mode=$1 ; shift - func_quote_result= - while test 0 -lt $#; do - func_quote_arg "$_G_func_quote_mode" "$1" - if test -n "$func_quote_result"; then - func_append func_quote_result " $func_quote_arg_result" - else - func_append func_quote_result "$func_quote_arg_result" - fi - shift - done + func_quote_for_expand_result=$_G_arg } @@ -1376,8 +1215,8 @@ func_show_eval () _G_cmd=$1 _G_fail_exp=${2-':'} - func_quote_arg pretty,expand "$_G_cmd" - eval "func_notquiet $func_quote_arg_result" + func_quote_for_expand "$_G_cmd" + eval "func_notquiet $func_quote_for_expand_result" $opt_dry_run || { eval "$_G_cmd" @@ -1402,8 +1241,8 @@ func_show_eval_locale () _G_fail_exp=${2-':'} $opt_quiet || { - func_quote_arg expand,pretty "$_G_cmd" - eval "func_echo $func_quote_arg_result" + func_quote_for_expand "$_G_cmd" + eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || { @@ -1530,26 +1369,30 @@ func_lt_ver () # End: #! /bin/sh +# Set a version string for this script. +scriptversion=2014-01-07.03; # UTC + # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 -# This is free software. There is NO warranty; not even for -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# -# Copyright (C) 2010-2019, 2021 Bootstrap Authors -# -# This file is dual licensed under the terms of the MIT license -# , and GPL version 2 or later -# . You must apply one of -# these licenses when using or redistributing this software or any of -# the files within it. See the URLs above, or the file `LICENSE` -# included in the Bootstrap distribution for the full license texts. +# Copyright (C) 2010-2015 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. -# Please report bugs or propose patches to: -# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. -# Set a version string for this script. -scriptversion=2019-02-19.15; # UTC +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Please report bugs or propose patches to gary@gnu.org. ## ------ ## @@ -1572,7 +1415,7 @@ scriptversion=2019-02-19.15; # UTC # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file -# starting with '# Written by ' and ending with '# Copyright'. +# starting with '# Written by ' and ending with '# warranty; '. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the @@ -1584,7 +1427,7 @@ scriptversion=2019-02-19.15; # UTC # to display verbose messages only when your user has specified # '--verbose'. # -# After sourcing this file, you can plug in processing for additional +# After sourcing this file, you can plug processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. @@ -1633,8 +1476,8 @@ fatal_help="Try '\$progname --help' for ## ------------------------- ## # This section contains functions for adding, removing, and running hooks -# in the main code. A hook is just a list of function names that can be -# run in order later on. +# to the main code. A hook is just a named list of of function, that can +# be run in order later on. # func_hookable FUNC_NAME # ----------------------- @@ -1667,8 +1510,7 @@ func_add_hook () # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ -# Remove HOOK_FUNC from the list of hook functions to be called by -# FUNC_NAME. +# Remove HOOK_FUNC from the list of functions called by FUNC_NAME. func_remove_hook () { $debug_cmd @@ -1677,28 +1519,10 @@ func_remove_hook () } -# func_propagate_result FUNC_NAME_A FUNC_NAME_B -# --------------------------------------------- -# If the *_result variable of FUNC_NAME_A _is set_, assign its value to -# *_result variable of FUNC_NAME_B. -func_propagate_result () -{ - $debug_cmd - - func_propagate_result_result=: - if eval "test \"\${${1}_result+set}\" = set" - then - eval "${2}_result=\$${1}_result" - else - func_propagate_result_result=false - fi -} - - # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. -# It's assumed that the list of hook functions contains nothing more +# It is assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. @@ -1708,19 +1532,22 @@ func_run_hooks () case " $hookable_fns " in *" $1 "*) ;; - *) func_fatal_error "'$1' does not support hook functions." ;; + *) func_fatal_error "'$1' does not support hook funcions.n" ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do - func_unset "${_G_hook}_result" - eval $_G_hook '${1+"$@"}' - func_propagate_result $_G_hook func_run_hooks - if $func_propagate_result_result; then - eval set dummy "$func_run_hooks_result"; shift - fi + eval $_G_hook '"$@"' + + # store returned options list back into positional + # parameters for next 'cmd' execution. + eval _G_hook_result=\$${_G_hook}_result + eval set dummy "$_G_hook_result"; shift done + + func_quote_for_eval ${1+"$@"} + func_run_hooks_result=$func_quote_for_eval_result } @@ -1730,18 +1557,10 @@ func_run_hooks () ## --------------- ## # In order to add your own option parsing hooks, you must accept the -# full positional parameter list from your hook function. You may remove -# or edit any options that you action, and then pass back the remaining -# unprocessed options in '_result', escaped -# suitably for 'eval'. -# -# The '_result' variable is automatically unset -# before your hook gets called; for best performance, only set the -# *_result variable when necessary (i.e. don't call the 'func_quote' -# function unnecessarily because it can be an expensive operation on some -# machines). -# -# Like this: +# full positional parameter list in your hook function, remove any +# options that you action, and then pass back the remaining unprocessed +# options in '_result', escaped suitably for +# 'eval'. Like this: # # my_options_prep () # { @@ -1751,8 +1570,9 @@ func_run_hooks () # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' -# # No change in '$@' (ignored completely by this hook). Leave -# # my_options_prep_result variable intact. +# +# func_quote_for_eval ${1+"$@"} +# my_options_prep_result=$func_quote_for_eval_result # } # func_add_hook func_options_prep my_options_prep # @@ -1761,36 +1581,25 @@ func_run_hooks () # { # $debug_cmd # -# args_changed=false -# -# # Note that, for efficiency, we parse as many options as we can +# # Note that for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in -# --silent|-s) opt_silent=: -# args_changed=: -# ;; +# --silent|-s) opt_silent=: ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift -# args_changed=: # ;; -# *) # Make sure the first unrecognised option "$_G_opt" -# # is added back to "$@" in case we need it later, -# # if $args_changed was set to 'true'. -# set dummy "$_G_opt" ${1+"$@"}; shift; break ;; +# *) set dummy "$_G_opt" "$*"; shift; break ;; # esac # done # -# # Only call 'func_quote' here if we processed at least one argument. -# if $args_changed; then -# func_quote eval ${1+"$@"} -# my_silent_option_result=$func_quote_result -# fi +# func_quote_for_eval ${1+"$@"} +# my_silent_option_result=$func_quote_for_eval_result # } # func_add_hook func_parse_options my_silent_option # @@ -1801,26 +1610,17 @@ func_run_hooks () # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." +# +# func_quote_for_eval ${1+"$@"} +# my_option_validation_result=$func_quote_for_eval_result # } # func_add_hook func_validate_options my_option_validation # -# You'll also need to manually amend $usage_message to reflect the extra +# You'll alse need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. -# func_options_finish [ARG]... -# ---------------------------- -# Finishing the option parse loop (call 'func_options' hooks ATM). -func_options_finish () -{ - $debug_cmd - - func_run_hooks func_options ${1+"$@"} - func_propagate_result func_run_hooks func_options_finish -} - - # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the @@ -1830,27 +1630,17 @@ func_options () { $debug_cmd - _G_options_quoted=false + func_options_prep ${1+"$@"} + eval func_parse_options \ + ${func_options_prep_result+"$func_options_prep_result"} + eval func_validate_options \ + ${func_parse_options_result+"$func_parse_options_result"} - for my_func in options_prep parse_options validate_options options_finish - do - func_unset func_${my_func}_result - func_unset func_run_hooks_result - eval func_$my_func '${1+"$@"}' - func_propagate_result func_$my_func func_options - if $func_propagate_result_result; then - eval set dummy "$func_options_result"; shift - _G_options_quoted=: - fi - done + eval func_run_hooks func_options \ + ${func_validate_options_result+"$func_validate_options_result"} - $_G_options_quoted || { - # As we (func_options) are top-level options-parser function and - # nobody quoted "$@" for us yet, we need to do it explicitly for - # caller. - func_quote eval ${1+"$@"} - func_options_result=$func_quote_result - } + # save modified positional parameters for caller + func_options_result=$func_run_hooks_result } @@ -1859,8 +1649,9 @@ func_options () # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and -# needs to propagate that back to rest of this script, then the complete -# modified list must be put in 'func_run_hooks_result' before returning. +# needs to propogate that back to rest of this script, then the complete +# modified list must be put in 'func_run_hooks_result' before +# returning. func_hookable func_options_prep func_options_prep () { @@ -1871,7 +1662,9 @@ func_options_prep () opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} - func_propagate_result func_run_hooks func_options_prep + + # save modified positional parameters for caller + func_options_prep_result=$func_run_hooks_result } @@ -1883,32 +1676,25 @@ func_parse_options () { $debug_cmd - _G_parse_options_requote=false + func_parse_options_result= + # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} - func_propagate_result func_run_hooks func_parse_options - if $func_propagate_result_result; then - eval set dummy "$func_parse_options_result"; shift - # Even though we may have changed "$@", we passed the "$@" array - # down into the hook and it quoted it for us (because we are in - # this if-branch). No need to quote it again. - _G_parse_options_requote=false - fi + + # Adjust func_parse_options positional parameters to match + eval set dummy "$func_run_hooks_result"; shift # Break out of the loop if we already parsed every option. test $# -gt 0 || break - # We expect that one of the options parsed in this function matches - # and thus we remove _G_opt from "$@" and need to re-quote. - _G_match_parse_options=: _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' - func_echo "enabling shell trace mode" >&2 + func_echo "enabling shell trace mode" $debug_cmd ;; @@ -1918,10 +1704,7 @@ func_parse_options () ;; --warnings|--warning|-W) - if test $# = 0 && func_missing_arg $_G_opt; then - _G_parse_options_requote=: - break - fi + test $# = 0 && func_missing_arg $_G_opt && break case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above @@ -1974,24 +1757,15 @@ func_parse_options () shift ;; - --) _G_parse_options_requote=: ; break ;; + --) break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; - *) set dummy "$_G_opt" ${1+"$@"}; shift - _G_match_parse_options=false - break - ;; + *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; esac - - if $_G_match_parse_options; then - _G_parse_options_requote=: - fi done - if $_G_parse_options_requote; then - # save modified positional parameters for caller - func_quote eval ${1+"$@"} - func_parse_options_result=$func_quote_result - fi + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + func_parse_options_result=$func_quote_for_eval_result } @@ -2008,10 +1782,12 @@ func_validate_options () test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} - func_propagate_result func_run_hooks func_validate_options # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE + + # save modified positional parameters for caller + func_validate_options_result=$func_run_hooks_result } @@ -2067,8 +1843,8 @@ func_missing_arg () # func_split_equals STRING # ------------------------ -# Set func_split_equals_lhs and func_split_equals_rhs shell variables -# after splitting STRING at the '=' sign. +# Set func_split_equals_lhs and func_split_equals_rhs shell variables after +# splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ @@ -2083,9 +1859,8 @@ then func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} - if test "x$func_split_equals_lhs" = "x$1"; then - func_split_equals_rhs= - fi + test "x$func_split_equals_lhs" = "x$1" \ + && func_split_equals_rhs= }' else # ...otherwise fall back to using expr, which is often a shell builtin. @@ -2095,7 +1870,7 @@ else func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= - test "x$func_split_equals_lhs=" = "x$1" \ + test "x$func_split_equals_lhs" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals @@ -2121,7 +1896,7 @@ else { $debug_cmd - func_split_short_opt_name=`expr "x$1" : 'x\(-.\)'` + func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt @@ -2163,44 +1938,31 @@ func_usage_message () # func_version # ------------ # Echo version message to standard output and exit. -# The version message is extracted from the calling file's header -# comments, with leading '# ' stripped: -# 1. First display the progname and version -# 2. Followed by the header comment line matching /^# Written by / -# 3. Then a blank line followed by the first following line matching -# /^# Copyright / -# 4. Immediately followed by any lines between the previous matches, -# except lines preceding the intervening completely blank line. -# For example, see the header comments of this file. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' - /^# Written by /!b - s|^# ||; p; n - - :fwd2blnk - /./ { - n - b fwd2blnk + /(C)/!b go + :more + /\./!{ + N + s|\n# | | + b more } - p; n - - :holdwrnt - s|^# || - s|^# *$|| - /^Copyright /!{ - /./H - n - b holdwrnt + :go + /^# Written by /,/# warranty; / { + s|^# || + s|^# *$|| + s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| + p } - - s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| - G - s|\(\n\)\n*|\1|g - p; q' < "$progpath" + /^# Written by / { + s|^# || + p + } + /^warranty; /q' < "$progpath" exit $? } @@ -2210,12 +1972,12 @@ func_version () # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) -# time-stamp-pattern: "30/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. -scriptversion='(GNU libtool) 2.4.7' +scriptversion='(GNU libtool) 2.4.6' # func_echo ARG... @@ -2306,7 +2068,7 @@ include the following information: compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) - version: $progname (GNU libtool) 2.4.7 + version: $progname (GNU libtool) 2.4.6 automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` @@ -2508,8 +2270,6 @@ libtool_options_prep () nonopt= preserve_args= - _G_rc_lt_options_prep=: - # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) @@ -2533,16 +2293,11 @@ libtool_options_prep () uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; - *) - _G_rc_lt_options_prep=false - ;; esac - if $_G_rc_lt_options_prep; then - # Pass back the list of options. - func_quote eval ${1+"$@"} - libtool_options_prep_result=$func_quote_result - fi + # Pass back the list of options. + func_quote_for_eval ${1+"$@"} + libtool_options_prep_result=$func_quote_for_eval_result } func_add_hook func_options_prep libtool_options_prep @@ -2554,12 +2309,9 @@ libtool_parse_options () { $debug_cmd - _G_rc_lt_parse_options=false - # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do - _G_match_lt_parse_options=: _G_opt=$1 shift case $_G_opt in @@ -2634,20 +2386,15 @@ libtool_parse_options () func_append preserve_args " $_G_opt" ;; - # An option not handled by this hook function: - *) set dummy "$_G_opt" ${1+"$@"} ; shift - _G_match_lt_parse_options=false - break - ;; + # An option not handled by this hook function: + *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; esac - $_G_match_lt_parse_options && _G_rc_lt_parse_options=: done - if $_G_rc_lt_parse_options; then - # save modified positional parameters for caller - func_quote eval ${1+"$@"} - libtool_parse_options_result=$func_quote_result - fi + + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + libtool_parse_options_result=$func_quote_for_eval_result } func_add_hook func_parse_options libtool_parse_options @@ -2668,10 +2415,17 @@ libtool_validate_options () # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" - # Keeping compiler generated duplicates in $postdeps and $predeps is not - # harmful, and is necessary in a majority of systems that use it to satisfy - # symbol dependencies. - opt_duplicate_compiler_generated_deps=: + case $host in + # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 + # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 + *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + ;; + esac $opt_help || { # Sanity checks first: @@ -2697,8 +2451,8 @@ libtool_validate_options () } # Pass back the unparsed argument list - func_quote eval ${1+"$@"} - libtool_validate_options_result=$func_quote_result + func_quote_for_eval ${1+"$@"} + libtool_validate_options_result=$func_quote_for_eval_result } func_add_hook func_validate_options libtool_validate_options @@ -3664,8 +3418,8 @@ func_mode_compile () esac done - func_quote_arg pretty "$libobj" - test "X$libobj" != "X$func_quote_arg_result" \ + func_quote_for_eval "$libobj" + test "X$libobj" != "X$func_quote_for_eval_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" @@ -3738,8 +3492,8 @@ compiler." func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result - func_quote_arg pretty "$srcfile" - qsrcfile=$func_quote_arg_result + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then @@ -3894,8 +3648,7 @@ This mode accepts the following addition -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking - -Wc,FLAG - -Xcompiler FLAG pass FLAG directly to the compiler + -Wc,FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. @@ -4001,8 +3754,6 @@ The following components of LINK-COMMAND -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler - -Wa,FLAG - -Xassembler FLAG pass linker-specific FLAG directly to the assembler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) @@ -4345,8 +4096,8 @@ func_mode_install () case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. - func_quote_arg pretty "$nonopt" - install_prog="$func_quote_arg_result " + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " arg=$1 shift else @@ -4356,8 +4107,8 @@ func_mode_install () # The real first argument should be the name of the installation program. # Aesthetically quote it. - func_quote_arg pretty "$arg" - func_append install_prog "$func_quote_arg_result" + func_quote_for_eval "$arg" + func_append install_prog "$func_quote_for_eval_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; @@ -4414,12 +4165,12 @@ func_mode_install () esac # Aesthetically quote the argument. - func_quote_arg pretty "$arg" - func_append install_prog " $func_quote_arg_result" + func_quote_for_eval "$arg" + func_append install_prog " $func_quote_for_eval_result" if test -n "$arg2"; then - func_quote_arg pretty "$arg2" + func_quote_for_eval "$arg2" fi - func_append install_shared_prog " $func_quote_arg_result" + func_append install_shared_prog " $func_quote_for_eval_result" done test -z "$install_prog" && \ @@ -4430,8 +4181,8 @@ func_mode_install () if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else - func_quote_arg pretty "$install_override_mode" - func_append install_shared_prog " -m $func_quote_arg_result" + func_quote_for_eval "$install_override_mode" + func_append install_shared_prog " -m $func_quote_for_eval_result" fi fi @@ -4727,8 +4478,8 @@ func_mode_install () relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { - func_quote_arg expand,pretty "$relink_command" - eval "func_echo $func_quote_arg_result" + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else @@ -5507,8 +5258,7 @@ else if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" - func_quote_arg pretty "$ECHO" - qECHO=$func_quote_arg_result + qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` $ECHO "\ # A function that is used when there is no print builtin or printf. @@ -5518,7 +5268,7 @@ func_fallback_echo () \$1 _LTECHO_EOF' } - ECHO=$qECHO + ECHO=\"$qECHO\" fi # Very basic option parsing. These options are (a) specific to @@ -6861,9 +6611,9 @@ func_mode_link () while test "$#" -gt 0; do arg=$1 shift - func_quote_arg pretty,unquoted "$arg" - qarg=$func_quote_arg_unquoted_result - func_append libtool_args " $func_quote_arg_result" + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then @@ -7099,13 +6849,6 @@ func_mode_link () prev= continue ;; - xassembler) - func_append compiler_flags " -Xassembler $qarg" - prev= - func_append compile_command " -Xassembler $qarg" - func_append finalize_command " -Xassembler $qarg" - continue - ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" @@ -7276,7 +7019,7 @@ func_mode_link () # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig* | *-*-midnightbsd*) + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; @@ -7296,7 +7039,7 @@ func_mode_link () esac elif test X-lc_r = "X$arg"; then case $host in - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig* | *-*-midnightbsd*) + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc_r directly, use -pthread flag. continue ;; @@ -7326,20 +7069,8 @@ func_mode_link () prev=xcompiler continue ;; - # Solaris ld rejects as of 11.4. Refer to Oracle bug 22985199. - -pthread) - case $host in - *solaris2*) ;; - *) - case "$new_inherited_linker_flags " in - *" $arg "*) ;; - * ) func_append new_inherited_linker_flags " $arg" ;; - esac - ;; - esac - continue - ;; - -mt|-mthreads|-kthread|-Kthread|-pthreads|--thread-safe \ + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" @@ -7480,9 +7211,9 @@ func_mode_link () save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs - func_quote_arg pretty "$flag" - func_append arg " $func_quote_arg_result" - func_append compiler_flags " $func_quote_arg_result" + func_quote_for_eval "$flag" + func_append arg " $func_quote_for_eval_result" + func_append compiler_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" @@ -7496,21 +7227,16 @@ func_mode_link () save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs - func_quote_arg pretty "$flag" - func_append arg " $wl$func_quote_arg_result" - func_append compiler_flags " $wl$func_quote_arg_result" - func_append linker_flags " $func_quote_arg_result" + func_quote_for_eval "$flag" + func_append arg " $wl$func_quote_for_eval_result" + func_append compiler_flags " $wl$func_quote_for_eval_result" + func_append linker_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; - -Xassembler) - prev=xassembler - continue - ;; - -Xcompiler) prev=xcompiler continue @@ -7528,8 +7254,8 @@ func_mode_link () # -msg_* for osf cc -msg_*) - func_quote_arg pretty "$arg" - arg=$func_quote_arg_result + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result ;; # Flags to be passed through unchanged, with rationale: @@ -7548,15 +7274,12 @@ func_mode_link () # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang - # -fsanitize=* Clang/GCC memory and address sanitizer - # -fuse-ld=* Linker select flags for GCC - # -Wa,* Pass flags directly to the assembler -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ - -specs=*|-fsanitize=*|-fuse-ld=*|-Wa,*) - func_quote_arg pretty "$arg" - arg=$func_quote_arg_result + -specs=*) + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" @@ -7577,15 +7300,15 @@ func_mode_link () continue else # Otherwise treat like 'Some other compiler flag' below - func_quote_arg pretty "$arg" - arg=$func_quote_arg_result + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result fi ;; # Some other compiler flag. -* | +*) - func_quote_arg pretty "$arg" - arg=$func_quote_arg_result + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result ;; *.$objext) @@ -7705,8 +7428,8 @@ func_mode_link () *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. - func_quote_arg pretty "$arg" - arg=$func_quote_arg_result + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result ;; esac # arg @@ -8911,7 +8634,7 @@ func_mode_link () test CXX = "$tagname" && { case $host_os in linux*) - case `$CC -V 2>&1 | $SED 5q` in + case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 func_suncc_cstd_abi @@ -9084,7 +8807,7 @@ func_mode_link () # case $version_type in # correct linux to gnu/linux during the next big refactor - darwin|freebsd-elf|linux|midnightbsd-elf|osf|windows|none) + darwin|freebsd-elf|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor @@ -9175,7 +8898,7 @@ func_mode_link () versuffix=.$current.$revision ;; - freebsd-elf | midnightbsd-elf) + freebsd-elf) func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision @@ -9401,7 +9124,7 @@ func_mode_link () *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-midnightbsd*) + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) @@ -10212,8 +9935,8 @@ EOF for cmd in $concat_cmds; do IFS=$save_ifs $opt_quiet || { - func_quote_arg expand,pretty "$cmd" - eval "func_echo $func_quote_arg_result" + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? @@ -10306,8 +10029,8 @@ EOF eval cmd=\"$cmd\" IFS=$save_ifs $opt_quiet || { - func_quote_arg expand,pretty "$cmd" - eval "func_echo $func_quote_arg_result" + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? @@ -10781,13 +10504,12 @@ EOF elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else - func_quote_arg pretty "$var_value" - relink_command="$var=$func_quote_arg_result; export $var; $relink_command" + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done - func_quote eval cd "`pwd`" - func_quote_arg pretty,unquoted "($func_quote_result; $relink_command)" - relink_command=$func_quote_arg_unquoted_result + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. @@ -11027,15 +10749,13 @@ EOF elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else - func_quote_arg pretty,unquoted "$var_value" - relink_command="$var=$func_quote_arg_unquoted_result; export $var; $relink_command" + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. - func_quote eval cd "`pwd`" - relink_command="($func_quote_result; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" - func_quote_arg pretty,unquoted "$relink_command" - relink_command=$func_quote_arg_unquoted_result + relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` if test yes = "$hardcode_automatic"; then relink_command= fi Index: cachedb/cachedb.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/cachedb/cachedb.c,v diff -u -p -r1.20 cachedb.c --- cachedb/cachedb.c 4 Sep 2024 09:36:40 -0000 1.20 +++ cachedb/cachedb.c 7 Feb 2025 21:25:44 -0000 @@ -621,6 +621,9 @@ parse_data(struct module_qstate* qstate, } verbose(VERB_ALGO, "cachedb msg adjusted down by %d", (int)adjust); adjust_msg_ttl(qstate->return_msg, adjust); + if(qstate->env->cfg->aggressive_nsec) { + limit_nsec_ttl(qstate->return_msg); + } /* Similar to the unbound worker, if serve-expired is enabled and * the msg would be considered to be expired, mark the state so a @@ -828,8 +831,6 @@ cachedb_handle_query(struct module_qstat /* In case we have expired data but there is a client timer for expired * answers, pass execution to next module in order to try updating the * data first. - * TODO: this needs revisit. The expired data stored from cachedb has - * 0 TTL which is picked up by iterator later when looking in the cache. */ if(qstate->env->cfg->serve_expired && msg_expired) { qstate->return_msg = NULL; Index: cachedb/redis.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/cachedb/redis.c,v diff -u -p -r1.1.1.4 redis.c --- cachedb/redis.c 12 Apr 2024 15:44:28 -0000 1.1.1.4 +++ cachedb/redis.c 7 Feb 2025 21:25:44 -0000 @@ -58,7 +58,8 @@ struct redis_moddata { int server_port; /* server's TCP port */ const char* server_path; /* server's unix path, or "", NULL if unused */ const char* server_password; /* server's AUTH password, or "", NULL if unused */ - struct timeval timeout; /* timeout for connection setup and commands */ + struct timeval command_timeout; /* timeout for commands */ + struct timeval connect_timeout; /* timeout for connect */ int logical_db; /* the redis logical database to use */ }; @@ -88,10 +89,10 @@ redis_connect(const struct redis_moddata if(moddata->server_path && moddata->server_path[0]!=0) { ctx = redisConnectUnixWithTimeout(moddata->server_path, - moddata->timeout); + moddata->connect_timeout); } else { ctx = redisConnectWithTimeout(moddata->server_host, - moddata->server_port, moddata->timeout); + moddata->server_port, moddata->connect_timeout); } if(!ctx || ctx->err) { const char *errstr = "out of memory"; @@ -100,7 +101,7 @@ redis_connect(const struct redis_moddata log_err("failed to connect to redis server: %s", errstr); goto fail; } - if(redisSetTimeout(ctx, moddata->timeout) != REDIS_OK) { + if(redisSetTimeout(ctx, moddata->command_timeout) != REDIS_OK) { log_err("failed to set redis timeout"); goto fail; } @@ -159,8 +160,24 @@ redis_init(struct module_env* env, struc moddata->server_port = env->cfg->redis_server_port; moddata->server_path = env->cfg->redis_server_path; moddata->server_password = env->cfg->redis_server_password; - moddata->timeout.tv_sec = env->cfg->redis_timeout / 1000; - moddata->timeout.tv_usec = (env->cfg->redis_timeout % 1000) * 1000; + moddata->command_timeout.tv_sec = env->cfg->redis_timeout / 1000; + moddata->command_timeout.tv_usec = + (env->cfg->redis_timeout % 1000) * 1000; + moddata->connect_timeout.tv_sec = env->cfg->redis_timeout / 1000; + moddata->connect_timeout.tv_usec = + (env->cfg->redis_timeout % 1000) * 1000; + if(env->cfg->redis_command_timeout != 0) { + moddata->command_timeout.tv_sec = + env->cfg->redis_command_timeout / 1000; + moddata->command_timeout.tv_usec = + (env->cfg->redis_command_timeout % 1000) * 1000; + } + if(env->cfg->redis_connect_timeout != 0) { + moddata->connect_timeout.tv_sec = + env->cfg->redis_connect_timeout / 1000; + moddata->connect_timeout.tv_usec = + (env->cfg->redis_connect_timeout % 1000) * 1000; + } moddata->logical_db = env->cfg->redis_logical_db; for(i = 0; i < moddata->numctxs; i++) { redisContext* ctx = redis_connect(moddata); Index: daemon/daemon.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/daemon/daemon.c,v diff -u -p -r1.26 daemon.c --- daemon/daemon.c 4 Sep 2024 09:36:40 -0000 1.26 +++ daemon/daemon.c 7 Feb 2025 21:25:44 -0000 @@ -557,6 +557,12 @@ daemon_create_workers(struct daemon* dae fatal_exit("out of memory during daemon init"); numport = daemon_get_shufport(daemon, shufport); verbose(VERB_ALGO, "total of %d outgoing ports available", numport); + +#ifdef HAVE_NGTCP2 + daemon->doq_table = doq_table_create(daemon->cfg, daemon->rand); + if(!daemon->doq_table) + fatal_exit("could not create doq_table: out of memory"); +#endif daemon->num = (daemon->cfg->num_threads?daemon->cfg->num_threads:1); if(daemon->reuseport && (int)daemon->num < (int)daemon->num_ports) { @@ -906,6 +912,10 @@ daemon_cleanup(struct daemon* daemon) #ifdef USE_DNSCRYPT dnsc_delete(daemon->dnscenv); daemon->dnscenv = NULL; +#endif +#ifdef HAVE_NGTCP2 + doq_table_delete(daemon->doq_table); + daemon->doq_table = NULL; #endif daemon->cfg = NULL; } Index: daemon/daemon.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/daemon/daemon.h,v diff -u -p -r1.9 daemon.h --- daemon/daemon.h 4 Sep 2024 09:36:40 -0000 1.9 +++ daemon/daemon.h 7 Feb 2025 21:25:44 -0000 @@ -58,6 +58,7 @@ struct ub_randstate; struct daemon_remote; struct respip_set; struct shm_main_info; +struct doq_table; struct cookie_secrets; #include "dnstap/dnstap_config.h" @@ -147,6 +148,8 @@ struct daemon { /** the dnscrypt environment */ struct dnsc_env* dnscenv; #endif + /** the doq connection table */ + struct doq_table* doq_table; /** reuse existing cache on reload if other conditions allow it. */ int reuse_cache; /** the EDNS cookie secrets from the cookie-secret-file */ Index: daemon/remote.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/daemon/remote.c,v diff -u -p -r1.40 remote.c --- daemon/remote.c 4 Sep 2024 09:36:40 -0000 1.40 +++ daemon/remote.c 7 Feb 2025 21:25:44 -0000 @@ -302,7 +302,7 @@ add_open(const char* ip, int nr, struct /* open fd */ fd = create_tcp_accept_sock(res, 1, &noproto, 0, cfg->ip_transparent, 0, 0, cfg->ip_freebind, - cfg->use_systemd, cfg->ip_dscp); + cfg->use_systemd, cfg->ip_dscp, "unbound-control"); freeaddrinfo(res); } @@ -866,6 +866,10 @@ print_mem(RES* ssl, struct worker* worke if(!print_longnum(ssl, "mem.http.response_buffer"SQ, (size_t)s->svr.mem_http2_response_buffer)) return 0; +#ifdef HAVE_NGTCP2 + if(!print_longnum(ssl, "mem.quic"SQ, (size_t)s->svr.mem_quic)) + return 0; +#endif /* HAVE_NGTCP2 */ return 1; } @@ -996,6 +1000,10 @@ print_ext(RES* ssl, struct ub_stats_info (unsigned long)s->svr.qipv6)) return 0; if(!ssl_printf(ssl, "num.query.https"SQ"%lu\n", (unsigned long)s->svr.qhttps)) return 0; +#ifdef HAVE_NGTCP2 + if(!ssl_printf(ssl, "num.query.quic"SQ"%lu\n", + (unsigned long)s->svr.qquic)) return 0; +#endif /* HAVE_NGTCP2 */ /* flags */ if(!ssl_printf(ssl, "num.query.flags.QR"SQ"%lu\n", (unsigned long)s->svr.qbit_QR)) return 0; @@ -1953,6 +1961,8 @@ bogus_del_msg(struct lruhash_entry* e, v struct reply_info* d = (struct reply_info*)e->data; if(d->security == sec_status_bogus) { d->ttl = inf->expired; + d->prefetch_ttl = inf->expired; + d->serve_expired_ttl = inf->expired; inf->num_msgs++; #ifdef USE_CACHEDB if(inf->remcachedb && inf->worker->env.cachedb_enabled) @@ -2035,6 +2045,8 @@ negative_del_msg(struct lruhash_entry* e * or NOERROR rcode with ANCOUNT==0: a NODATA answer */ if(FLAGS_GET_RCODE(d->flags) != 0 || d->an_numrrsets == 0) { d->ttl = inf->expired; + d->prefetch_ttl = inf->expired; + d->serve_expired_ttl = inf->expired; inf->num_msgs++; #ifdef USE_CACHEDB if(inf->remcachedb && inf->worker->env.cachedb_enabled) Index: daemon/stats.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/daemon/stats.c,v diff -u -p -r1.16 stats.c --- daemon/stats.c 4 Sep 2024 09:36:40 -0000 1.16 +++ daemon/stats.c 7 Feb 2025 21:25:44 -0000 @@ -346,6 +346,12 @@ server_stats_compile(struct worker* work (long long)http2_get_query_buffer_size(); s->svr.mem_http2_response_buffer = (long long)http2_get_response_buffer_size(); +#ifdef HAVE_NGTCP2 + s->svr.mem_quic = (long long)doq_table_quic_size_get( + worker->daemon->doq_table); +#else + s->svr.mem_quic = 0; +#endif /* HAVE_NGTCP2 */ /* Set neg cache usage numbers */ set_neg_cache_stats(worker, &s->svr, reset); @@ -474,6 +480,7 @@ void server_stats_add(struct ub_stats_in total->svr.qtls += a->svr.qtls; total->svr.qtls_resume += a->svr.qtls_resume; total->svr.qhttps += a->svr.qhttps; + total->svr.qquic += a->svr.qquic; total->svr.qipv6 += a->svr.qipv6; total->svr.qbit_QR += a->svr.qbit_QR; total->svr.qbit_AA += a->svr.qbit_AA; @@ -533,7 +540,8 @@ void server_stats_insquery(struct ub_ser else stats->qclass_big++; stats->qopcode[ LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) ]++; if(c->type != comm_udp) { - stats->qtcp++; + if(c->type != comm_doq) + stats->qtcp++; if(c->ssl != NULL) { stats->qtls++; #ifdef HAVE_SSL @@ -542,6 +550,10 @@ void server_stats_insquery(struct ub_ser #endif if(c->type == comm_http) stats->qhttps++; +#ifdef HAVE_NGTCP2 + else if(c->type == comm_doq) + stats->qquic++; +#endif } } if(repinfo && addr_is_ip6(&repinfo->remote_addr, repinfo->remote_addrlen)) Index: daemon/worker.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/daemon/worker.c,v diff -u -p -r1.41 worker.c --- daemon/worker.c 4 Sep 2024 09:36:40 -0000 1.41 +++ daemon/worker.c 7 Feb 2025 21:25:44 -0000 @@ -661,22 +661,18 @@ answer_from_cache(struct worker* worker, if(rep->ttl < timenow) { /* Check if we need to serve expired now */ if(worker->env.cfg->serve_expired && - !worker->env.cfg->serve_expired_client_timeout + /* if serve-expired-client-timeout is set, serve + * an expired record without attempting recursion + * if the serve_expired_norec_ttl is set for the record + * as we know that recursion is currently failing. */ + (!worker->env.cfg->serve_expired_client_timeout || + timenow < rep->serve_expired_norec_ttl) #ifdef USE_CACHEDB && !(worker->env.cachedb_enabled && worker->env.cfg->cachedb_check_when_serve_expired) #endif ) { - if(worker->env.cfg->serve_expired_ttl && - rep->serve_expired_ttl < timenow) - return 0; - /* Ignore expired failure answers */ - if(FLAGS_GET_RCODE(rep->flags) != - LDNS_RCODE_NOERROR && - FLAGS_GET_RCODE(rep->flags) != - LDNS_RCODE_NXDOMAIN && - FLAGS_GET_RCODE(rep->flags) != - LDNS_RCODE_YXDOMAIN) + if(!reply_info_can_answer_expired(rep, timenow)) return 0; if(!rrset_array_lock(rep->ref, rep->rrset_count, 0)) return 0; @@ -2178,7 +2174,9 @@ worker_init(struct worker* worker, struc cfg->harden_large_queries, cfg->http_max_streams, cfg->http_endpoint, cfg->http_notls_downstream, worker->daemon->tcl, worker->daemon->listen_sslctx, - dtenv, worker_handle_request, worker); + dtenv, worker->daemon->doq_table, worker->env.rnd, + cfg->ssl_service_key, cfg->ssl_service_pem, cfg, + worker_handle_request, worker); if(!worker->front) { log_err("could not create listening sockets"); worker_delete(worker); @@ -2507,6 +2505,22 @@ void dtio_tap_callback(int ATTR_UNUSED(f #ifdef USE_DNSTAP void dtio_mainfdcallback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} +#endif + +#ifdef HAVE_NGTCP2 +void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} +#endif + +#ifdef HAVE_NGTCP2 +void doq_client_timer_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), void* ATTR_UNUSED(arg)) { log_assert(0); Index: dns64/dns64.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/dns64/dns64.c,v diff -u -p -r1.21 dns64.c --- dns64/dns64.c 4 Sep 2024 09:36:40 -0000 1.21 +++ dns64/dns64.c 7 Feb 2025 21:25:44 -0000 @@ -657,7 +657,7 @@ handle_event_moddone(struct module_qstat qstate->return_msg->rep && !dns_cache_store( qstate->env, &qstate->qinfo, qstate->return_msg->rep, - 0, 0, 0, NULL, + 0, qstate->prefetch_leeway, 0, NULL, qstate->query_flags, qstate->qstarttime)) log_err("out of memory"); @@ -847,6 +847,7 @@ dns64_adjust_a(int id, struct module_qst */ cp = construct_reply_info_base(super->region, rep->flags, rep->qdcount, rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl, + rep->serve_expired_norec_ttl, rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets, rep->rrset_count, rep->security, LDNS_EDE_NONE); if(!cp) @@ -1007,7 +1008,7 @@ dns64_inform_super(struct module_qstate* /* Store the generated response in cache. */ if ( (!super_dq || !super_dq->started_no_cache_store) && !dns_cache_store(super->env, &super->qinfo, super->return_msg->rep, - 0, 0, 0, NULL, super->query_flags, qstate->qstarttime)) + 0, super->prefetch_leeway, 0, NULL, super->query_flags, qstate->qstarttime)) log_err("out of memory"); } Index: dnstap/unbound-dnstap-socket.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/dnstap/unbound-dnstap-socket.c,v diff -u -p -r1.2 unbound-dnstap-socket.c --- dnstap/unbound-dnstap-socket.c 4 Sep 2024 09:36:40 -0000 1.2 +++ dnstap/unbound-dnstap-socket.c 7 Feb 2025 21:25:44 -0000 @@ -1151,7 +1151,9 @@ void dtio_mainfdcallback(int fd, short A char* id = NULL; struct sockaddr_storage addr; socklen_t addrlen = (socklen_t)sizeof(addr); - int s = accept(fd, (struct sockaddr*)&addr, &addrlen); + int s; + memset(&addr, 0, sizeof(addr)); + s = accept(fd, (struct sockaddr*)&addr, &addrlen); if(s == -1) { #ifndef USE_WINSOCK /* EINTR is signal interrupt. others are closed connection. */ @@ -1543,8 +1545,8 @@ int main(int argc, char** argv) usage(argv); } } - argc -= optind; - argv += optind; + /* argc -= optind; not using further arguments */ + /* argv += optind; not using further arguments */ if(usessl) { #ifdef HAVE_SSL @@ -1783,3 +1785,19 @@ void remote_get_opt_ssl(char* ATTR_UNUSE { log_assert(0); } + +#ifdef HAVE_NGTCP2 +void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} +#endif + +#ifdef HAVE_NGTCP2 +void doq_client_timer_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} +#endif Index: doc/README =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/README,v diff -u -p -r1.40 README --- doc/README 7 Oct 2024 15:38:22 -0000 1.40 +++ doc/README 7 Feb 2025 21:25:44 -0000 @@ -1,4 +1,4 @@ -README for Unbound 1.21.1 +README for Unbound 1.22.0 Copyright 2007 NLnet Labs http://unbound.net Index: doc/example.conf.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/example.conf.in,v diff -u -p -r1.45 example.conf.in --- doc/example.conf.in 7 Oct 2024 15:38:22 -0000 1.45 +++ doc/example.conf.in 7 Feb 2025 21:25:44 -0000 @@ -1,7 +1,7 @@ # # Example configuration file. # -# See unbound.conf(5) man page, version 1.21.1. +# See unbound.conf(5) man page, version 1.22.0. # # this is a comment. @@ -187,6 +187,15 @@ server: # query upon encountering a CNAME record. # max-query-restarts: 11 + # Limit on number of NS records in NS RRset for incoming packets. + # iter-scrub-ns: 20 + + # Limit on number of CNAME, DNAME records for incoming packets. + # iter-scrub-cname: 11 + + # Limit on upstream queries for an incoming query and its recursion. + # max-global-quota: 128 + # msec for waiting for an unknown server to reply. Increase if you # are behind a slow satellite link, to eg. 1128. # unknown-server-time-limit: 376 @@ -452,6 +461,10 @@ server: # print UTC timestamp in ascii to logfile, default is epoch in seconds. # log-time-ascii: no + # log timestamp in ISO8601 format if also log-time-ascii is enabled. + # (y-m-dTh:m:s.msec[+-]tzhours:tzminutes) + # log-time-iso: no + # print one line with time, IP, name, type, class for every query. # log-queries: no @@ -524,6 +537,9 @@ server: # Harden against out of zone rrsets, to avoid spoofing attempts. # harden-glue: yes + # Harden against unverified (outside-zone, including sibling zone) glue rrsets + # harden-unverified-glue: no + # Harden against receiving dnssec-stripped data. If you turn it # off, failing to validate dnskey data for a trustanchor will # trigger insecure mode for that zone (like without a trustanchor). @@ -904,6 +920,7 @@ server: # tls-service-pem: "path/to/publiccertfile.pem" # tls-port: 853 # https-port: 443 + # quic-port: 853 # cipher setting for TLSv1.2 # tls-ciphers: "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256" @@ -968,6 +985,9 @@ server: # Disable TLS for DNS-over-HTTP downstream service. # http-notls-downstream: no + # Maximum number of bytes used for QUIC buffers. + # quic-size: 8m + # 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. @@ -1289,6 +1309,10 @@ remote-control: # # redis-server-password: "" # # timeout (in ms) for communication with the redis server # redis-timeout: 100 +# # timeout (in ms) for commands, if 0, uses redis-timeout. +# redis-command-timeout: 0 +# # timeout (in ms) for connection set up, if 0, uses redis-timeout. +# redis-connect-timeout: 0 # # set timeout on redis records based on DNS response TTL # redis-expire-records: no # # redis logical database to use, 0 is the default database. Index: doc/libunbound.3.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/libunbound.3.in,v diff -u -p -r1.43 libunbound.3.in --- doc/libunbound.3.in 7 Oct 2024 15:38:22 -0000 1.43 +++ doc/libunbound.3.in 7 Feb 2025 21:25:44 -0000 @@ -1,4 +1,4 @@ -.TH "libunbound" "3" "Oct 3, 2024" "NLnet Labs" "unbound 1.21.1" +.TH "libunbound" "3" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0" .\" .\" libunbound.3 -- unbound library functions manual .\" @@ -44,7 +44,7 @@ .B ub_ctx_zone_remove, .B ub_ctx_data_add, .B ub_ctx_data_remove -\- Unbound DNS validating resolver 1.21.1 functions. +\- Unbound DNS validating resolver 1.22.0 functions. .SH "SYNOPSIS" .B #include .LP Index: doc/unbound-anchor.8.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound-anchor.8.in,v diff -u -p -r1.42 unbound-anchor.8.in --- doc/unbound-anchor.8.in 7 Oct 2024 15:38:22 -0000 1.42 +++ doc/unbound-anchor.8.in 7 Feb 2025 21:25:44 -0000 @@ -1,4 +1,4 @@ -.TH "unbound-anchor" "8" "Oct 3, 2024" "NLnet Labs" "unbound 1.21.1" +.TH "unbound-anchor" "8" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0" .\" .\" unbound-anchor.8 -- unbound anchor maintenance utility manual .\" Index: doc/unbound-checkconf.8.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound-checkconf.8.in,v diff -u -p -r1.42 unbound-checkconf.8.in --- doc/unbound-checkconf.8.in 7 Oct 2024 15:38:22 -0000 1.42 +++ doc/unbound-checkconf.8.in 7 Feb 2025 21:25:44 -0000 @@ -1,4 +1,4 @@ -.TH "unbound-checkconf" "8" "Oct 3, 2024" "NLnet Labs" "unbound 1.21.1" +.TH "unbound-checkconf" "8" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0" .\" .\" unbound-checkconf.8 -- unbound configuration checker manual .\" Index: doc/unbound-control.8.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound-control.8.in,v diff -u -p -r1.44 unbound-control.8.in --- doc/unbound-control.8.in 7 Oct 2024 15:38:22 -0000 1.44 +++ doc/unbound-control.8.in 7 Feb 2025 21:25:44 -0000 @@ -1,4 +1,4 @@ -.TH "unbound-control" "8" "Oct 3, 2024" "NLnet Labs" "unbound 1.21.1" +.TH "unbound-control" "8" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0" .\" .\" unbound-control.8 -- unbound remote control manual .\" @@ -606,6 +606,10 @@ queries waiting for request stream compl Memory in bytes used by the HTTP/2 response buffers. Containing DNS responses waiting to be written back to the clients. .TP +.I mem.quic +Memory in bytes used by QUIC. Containing connection information, stream +information, queries read and responses written back to the clients. +.TP .I histogram...to.. Shows a histogram, summed over all threads. Every element counts the recursive queries whose reply time fit between the lower and upper bound. @@ -653,6 +657,10 @@ the Unbound server where the client nego Number of queries that were made using HTTPS towards the Unbound server. These are also counted in num.query.tcp and num.query.tls, because HTTPS uses TLS and TCP. +.TP +.I num.query.quic +Number of queries that were made using QUIC towards the Unbound server. +These are also counted in num.query.tls, because TLS is used for these queries. .TP .I num.query.ipv6 Number of queries that were made using IPv6 towards the Unbound server. Index: doc/unbound-host.1.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound-host.1.in,v diff -u -p -r1.44 unbound-host.1.in --- doc/unbound-host.1.in 7 Oct 2024 15:38:22 -0000 1.44 +++ doc/unbound-host.1.in 7 Feb 2025 21:25:44 -0000 @@ -1,4 +1,4 @@ -.TH "unbound\-host" "1" "Oct 3, 2024" "NLnet Labs" "unbound 1.21.1" +.TH "unbound\-host" "1" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0" .\" .\" unbound-host.1 -- unbound DNS lookup utility .\" Index: doc/unbound.8.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound.8.in,v diff -u -p -r1.45 unbound.8.in --- doc/unbound.8.in 7 Oct 2024 15:38:22 -0000 1.45 +++ doc/unbound.8.in 7 Feb 2025 21:25:44 -0000 @@ -1,4 +1,4 @@ -.TH "unbound" "8" "Oct 3, 2024" "NLnet Labs" "unbound 1.21.1" +.TH "unbound" "8" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0" .\" .\" unbound.8 -- unbound manual .\" @@ -9,7 +9,7 @@ .\" .SH "NAME" .B unbound -\- Unbound DNS validating resolver 1.21.1. +\- Unbound DNS validating resolver 1.22.0. .SH "SYNOPSIS" .B unbound .RB [ \-h ] Index: doc/unbound.conf.5.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound.conf.5.in,v diff -u -p -r1.49 unbound.conf.5.in --- doc/unbound.conf.5.in 7 Oct 2024 15:38:22 -0000 1.49 +++ doc/unbound.conf.5.in 7 Feb 2025 21:25:44 -0000 @@ -1,4 +1,4 @@ -.TH "unbound.conf" "5" "Oct 3, 2024" "NLnet Labs" "unbound 1.21.1" +.TH "unbound.conf" "5" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0" .\" .\" unbound.conf.5 -- unbound.conf manual .\" @@ -530,6 +530,9 @@ tls\-system\-cert to load CA certs, othe authenticated. This option enables TLS for all of them, but if you do not set this you can configure TLS specifically for some forward zones with forward\-tls\-upstream. And also with stub\-tls\-upstream. +If the tls\-upstream option is enabled, it is for all the forwards and stubs, +where the forward\-tls\-upstream and stub\-tls\-upstream options are ignored, +as if they had been set to yes. .TP .B ssl\-upstream: \fI Alternate syntax for \fBtls\-upstream\fR. If both are present in the config @@ -680,6 +683,18 @@ PROXYv2 is supported for UDP and TCP/TLS There is no support for PROXYv2 on a DoH or DNSCrypt listening interface. Can list multiple, each on a new statement. .TP +.B quic\-port: \fI +The port number on which to provide DNS-over-QUIC service, default 853, only +interfaces configured with that port number as @number get the QUIC service. +The interface uses QUIC for the UDP traffic on that port number. +.TP +.B quic\-size: \fI +Maximum number of bytes for all QUIC buffers and data combined. Default is 8 +megabytes. A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, +megabytes or gigabytes (1024*1024 bytes in a megabyte). New connections receive +connection refused when the limit is exceeded. New streams are reset when the +limit is exceeded. +.TP .B use\-systemd: \fI Enable or disable systemd socket activation. Default is no. @@ -900,6 +915,10 @@ Sets logfile lines to use a timestamp in prints the seconds since 1970 in brackets. No effect if using syslog, in that case syslog formats the timestamp printed into the log files. .TP +.B log\-time\-iso:\fR +Log time in ISO8601 format, if \fBlog\-time\-ascii:\fR yes is also set. +Default is no. +.TP .B log\-queries: \fI Prints one line per query to the log, with the log timestamp and IP address, name, type and class. Default is no. Note that it takes time to print these @@ -1003,6 +1022,11 @@ payload is very large. .B harden\-glue: \fI Will trust glue only if it is within the servers authority. Default is yes. .TP +.B harden\-unverified\-glue: \fI +Will trust only in-zone glue. Will try to resolve all out of zone +(\fI) glue. Will fallback to the original glue if unable to resolve. +Default is no. +.TP .B harden\-dnssec\-stripped: \fI Require DNSSEC data for trust\-anchored zones, if such data is absent, the zone becomes bogus. If turned off, and no DNSSEC data is received @@ -1126,10 +1150,11 @@ IP6 ::1 and IP4 127.0.0.1/8. If no, then queries to. Default is yes. .TP .B prefetch: \fI -If yes, message cache elements are prefetched before they expire to -keep the cache up to date. Default is no. Turning it on gives about -10 percent more traffic and load on the machine, but popular items do -not expire from the cache. +If yes, cache hits on message cache elements that are on their last 10 percent +of their TTL value trigger a prefetch to keep the cache up to date. +Default is no. +Turning it on gives about 10 percent more traffic and load on the machine, but +popular items do not expire from the cache. .TP .B prefetch\-key: \fI If yes, fetch the DNSKEYs earlier in the validation process, when a DS @@ -1149,12 +1174,13 @@ from the query ID, for speed and thread .B minimal-responses: \fI If yes, Unbound does not insert authority/additional sections into response messages when those sections are not required. This reduces response -size significantly, and may avoid TCP fallback for some responses. -This may cause a slight speedup. The default is yes, even though the DNS +size significantly, and may avoid TCP fallback for some responses which may +cause a slight speedup. The default is yes, even though the DNS protocol RFCs mandate these sections, and the additional content could -be of use and save roundtrips for clients. Because they are not used, -and the saved roundtrips are easier saved with prefetch, whilst this is -faster. +save roundtrips for clients that use the additional content. +However these sections are hardly used by clients. +Enabling prefetch can benefit clients that need the additional content +by trying to keep that content fresh in the cache. .TP .B disable-dnssec-lame-check: \fI If true, disables the DNSSEC lameness check in the iterator. This check @@ -1912,6 +1938,23 @@ Changing this value needs caution as it accepted, where Unbound needs to verify (resolve) each link individually. Default is 11. .TP 5 +.B iter\-scrub\-ns: \fI +Limit on the number of NS records allowed in an rrset of type NS, from the +iterator scrubber. This protects the internals of the resolver from overly +large NS sets. Default is 20. +.TP 5 +.B iter\-scrub\-cname: \fI +Limit on the number of CNAME, DNAME records in an answer, from the iterator +scrubber. This protects the internals of the resolver from overly long +indirection chains. Clips off the remainder of the reply packet at that point. +Default is 11. +.TP 5 +.B max\-global\-quota: \fI +Limit on the number of upstream queries sent out for an incoming query and +its subqueries from recursion. It is not reset during the resolution. When +it is exceeded the query is failed and the lookup process stops. +Default is 128. +.TP 5 .B fast\-server\-permil: \fI Specify how many times out of 1000 to pick from the set of fastest servers. 0 turns the feature off. A value of 900 would pick from the fastest @@ -2742,6 +2785,14 @@ If this timeout expires Unbound closes t if the Redis server does not have the requested data, and will try to re-establish a new connection later. This option defaults to 100 milliseconds. +.TP +.B redis-command-timeout: \fI\fR +The timeout to use for redis commands, in milliseconds. If 0, it uses the +redis\-timeout value. The default is 0. +.TP +.B redis-connect-timeout: \fI\fR +The timeout to use for redis connection set up, in milliseconds. If 0, it +uses the redis\-timeout value. The default is 0. .TP .B redis-expire-records: \fI If Redis record expiration is enabled. If yes, Unbound sets timeout for Redis Index: doc/unbound.doxygen =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound.doxygen,v diff -u -p -r1.8 unbound.doxygen --- doc/unbound.doxygen 13 Jun 2024 14:30:28 -0000 1.8 +++ doc/unbound.doxygen 7 Feb 2025 21:25:44 -0000 @@ -1226,7 +1226,7 @@ VERBATIM_HEADERS = NO # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. -CLANG_ASSISTED_PARSING = NO +#CLANG_ASSISTED_PARSING = NO # If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS # tag is set to YES then doxygen will add the directory of each input to the @@ -1234,7 +1234,7 @@ CLANG_ASSISTED_PARSING = NO # The default value is: YES. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. -CLANG_ADD_INC_PATHS = YES +#CLANG_ADD_INC_PATHS = YES # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that @@ -1242,7 +1242,7 @@ CLANG_ADD_INC_PATHS = YES # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. -CLANG_OPTIONS = +#CLANG_OPTIONS = # If clang assisted parsing is enabled you can provide the clang parser with the # path to the directory containing a file called compile_commands.json. This @@ -1255,7 +1255,7 @@ CLANG_OPTIONS = # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. -CLANG_DATABASE_PATH = +#CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index Index: iterator/iter_scrub.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iter_scrub.c,v diff -u -p -r1.16 iter_scrub.c --- iterator/iter_scrub.c 4 Sep 2024 09:36:40 -0000 1.16 +++ iterator/iter_scrub.c 7 Feb 2025 21:25:44 -0000 @@ -443,7 +443,7 @@ scrub_normalize(sldns_buffer* pkt, struc prev = NULL; rrset = msg->rrset_first; while(rrset && rrset->section == LDNS_SECTION_ANSWER) { - if(cname_length > 11 /* env->cfg.iter_scrub_cname */) { + if(cname_length > env->cfg->iter_scrub_cname) { /* Too many CNAMEs, or DNAMEs, from the authority * server, scrub down the length to something * shorter. This deletes everything after the limit @@ -562,8 +562,8 @@ scrub_normalize(sldns_buffer* pkt, struc dname_pkt_compare(pkt, oldsname, rrset->dname) == 0) { if(rrset->type == LDNS_RR_TYPE_NS && - rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) { - shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */); + rrset->rr_count > env->cfg->iter_scrub_ns) { + shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns); } prev = rrset; rrset = rrset->rrset_all_next; @@ -581,8 +581,8 @@ scrub_normalize(sldns_buffer* pkt, struc } if(rrset->type == LDNS_RR_TYPE_NS && - rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) { - shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */); + rrset->rr_count > env->cfg->iter_scrub_ns) { + shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns); } /* Mark the additional names from relevant rrset as OK. */ @@ -641,7 +641,7 @@ scrub_normalize(sldns_buffer* pkt, struc "RRset:", pkt, msg, prev, &rrset); continue; } - if(rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) { + if(rrset->rr_count > env->cfg->iter_scrub_ns) { /* If this is not a referral, and the NS RRset * is signed, then remove it entirely, so * that when it becomes bogus it does not @@ -657,7 +657,7 @@ scrub_normalize(sldns_buffer* pkt, struc "RRset:", pkt, msg, prev, &rrset); continue; } else { - shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */); + shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns); } } } @@ -871,6 +871,7 @@ scrub_sanitize(sldns_buffer* pkt, struct { int del_addi = 0; /* if additional-holding rrsets are deleted, we do not trust the normalized additional-A-AAAA any more */ + uint8_t* ns_rrset_dname = NULL; int added_rrlen_ede = 0; struct rrset_parse* rrset, *prev; prev = NULL; @@ -976,6 +977,16 @@ scrub_sanitize(sldns_buffer* pkt, struct continue; } } + if(rrset->type == LDNS_RR_TYPE_NS && + (rrset->section == LDNS_SECTION_AUTHORITY || + rrset->section == LDNS_SECTION_ANSWER)) { + /* If the type is NS, and we're in the + * answer or authority section, then + * store the dname so we can check + * against the glue records + * further down */ + ns_rrset_dname = rrset->dname; + } if(del_addi && rrset->section == LDNS_SECTION_ADDITIONAL) { remove_rrset("sanitize: removing potential " "poison reference RRset:", pkt, msg, prev, &rrset); @@ -986,6 +997,26 @@ scrub_sanitize(sldns_buffer* pkt, struct sanitize_nsec_is_overreach(pkt, rrset, zonename)) { remove_rrset("sanitize: removing overreaching NSEC " "RRset:", pkt, msg, prev, &rrset); + continue; + } + if(env->cfg->harden_unverified_glue && ns_rrset_dname && + rrset->section == LDNS_SECTION_ADDITIONAL && + (rrset->type == LDNS_RR_TYPE_A || rrset->type == LDNS_RR_TYPE_AAAA) && + !pkt_strict_sub(pkt, rrset->dname, ns_rrset_dname)) { + /* We're in the additional section, looking + * at an A/AAAA rrset, have a previous + * delegation point and we notice that + * the glue records are NOT for strict + * subdomains of the delegation. So set a + * flag, recompute the hash for the rrset + * and write the A/AAAA record to cache. + * It'll be retrieved if we can't separately + * resolve the glue */ + rrset->flags = PACKED_RRSET_UNVERIFIED_GLUE; + rrset->hash = pkt_hash_rrset(pkt, rrset->dname, rrset->type, rrset->rrset_class, rrset->flags); + store_rrset(pkt, msg, env, rrset); + remove_rrset("sanitize: storing potential " + "unverified glue reference RRset:", pkt, msg, prev, &rrset); continue; } prev = rrset; Index: iterator/iter_utils.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iter_utils.c,v diff -u -p -r1.23 iter_utils.c --- iterator/iter_utils.c 4 Sep 2024 09:36:40 -0000 1.23 +++ iterator/iter_utils.c 7 Feb 2025 21:25:44 -0000 @@ -1564,3 +1564,45 @@ void iterator_set_ip46_support(struct mo if(outnet->num_ip6 == 0) ie->supports_ipv6 = 0; } + +void +limit_nsec_ttl(struct dns_msg* msg) +{ + /* Limit NSEC and NSEC3 TTL in response, RFC9077 */ + size_t i; + int found = 0; + time_t soa_ttl = 0; + /* Limit the NSEC and NSEC3 TTL values to the SOA TTL and SOA minimum + * TTL. That has already been applied to the SOA record ttl. */ + for(i=0; irep->rrset_count; i++) { + struct ub_packed_rrset_key* s = msg->rep->rrsets[i]; + if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA) { + struct packed_rrset_data* soadata = (struct packed_rrset_data*)s->entry.data; + found = 1; + soa_ttl = soadata->ttl; + break; + } + } + if(!found) + return; + for(i=0; irep->rrset_count; i++) { + struct ub_packed_rrset_key* s = msg->rep->rrsets[i]; + if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC || + ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { + struct packed_rrset_data* data = (struct packed_rrset_data*)s->entry.data; + /* Limit the negative TTL. */ + if(data->ttl > soa_ttl) { + if(verbosity >= VERB_ALGO) { + char buf[256]; + snprintf(buf, sizeof(buf), + "limiting TTL %d of %s record to the SOA TTL of %d for", + (int)data->ttl, ((ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC)?"NSEC":"NSEC3"), (int)soa_ttl); + log_nametypeclass(VERB_ALGO, buf, + s->rk.dname, ntohs(s->rk.type), + ntohs(s->rk.rrset_class)); + } + data->ttl = soa_ttl; + } + } + } +} Index: iterator/iter_utils.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iter_utils.h,v diff -u -p -r1.16 iter_utils.h --- iterator/iter_utils.h 13 Jun 2024 14:30:28 -0000 1.16 +++ iterator/iter_utils.h 7 Feb 2025 21:25:44 -0000 @@ -428,4 +428,11 @@ int iter_stub_fwd_no_cache(struct module void iterator_set_ip46_support(struct module_stack* mods, struct module_env* env, struct outside_network* outnet); +/** + * Limit NSEC and NSEC3 TTL in response, RFC9077 + * @param msg: dns message, the SOA record ttl is used to restrict ttls + * of NSEC and NSEC3 RRsets. If no SOA record, nothing happens. + */ +void limit_nsec_ttl(struct dns_msg* msg); + #endif /* ITERATOR_ITER_UTILS_H */ Index: iterator/iterator.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iterator.c,v diff -u -p -r1.38 iterator.c --- iterator/iterator.c 4 Sep 2024 09:36:40 -0000 1.38 +++ iterator/iterator.c 7 Feb 2025 21:25:44 -0000 @@ -70,6 +70,8 @@ #include "sldns/parseutil.h" #include "sldns/sbuffer.h" +/* number of packets */ +int MAX_GLOBAL_QUOTA = 128; /* in msec */ int UNKNOWN_SERVER_NICENESS = 376; /* in msec */ @@ -252,7 +254,7 @@ error_supers(struct module_qstate* qstat } else { /* see if the failure did get (parent-lame) info */ if(!cache_fill_missing(super->env, super_iq->qchase.qclass, - super->region, super_iq->dp)) + super->region, super_iq->dp, 0)) log_err("out of memory adding missing"); } delegpt_mark_neg(dpns, qstate->qinfo.qtype); @@ -320,16 +322,21 @@ error_response_cache(struct module_qstat qstate->qinfo.qname, qstate->qinfo.qname_len, qstate->qinfo.qtype, qstate->qinfo.qclass, qstate->query_flags, 0, - qstate->env->cfg->serve_expired_ttl_reset)) != NULL) { + qstate->env->cfg->serve_expired)) != NULL) { struct reply_info* rep = (struct reply_info*)msg->entry.data; - if(qstate->env->cfg->serve_expired && - qstate->env->cfg->serve_expired_ttl_reset && rep && - *qstate->env->now + qstate->env->cfg->serve_expired_ttl - > rep->serve_expired_ttl) { - verbose(VERB_ALGO, "reset serve-expired-ttl for " + if(qstate->env->cfg->serve_expired && rep) { + if(qstate->env->cfg->serve_expired_ttl_reset && + *qstate->env->now + qstate->env->cfg->serve_expired_ttl + > rep->serve_expired_ttl) { + verbose(VERB_ALGO, "reset serve-expired-ttl for " + "response in cache"); + rep->serve_expired_ttl = *qstate->env->now + + qstate->env->cfg->serve_expired_ttl; + } + verbose(VERB_ALGO, "set serve-expired-norec-ttl for " "response in cache"); - rep->serve_expired_ttl = *qstate->env->now + - qstate->env->cfg->serve_expired_ttl; + rep->serve_expired_norec_ttl = NORR_TTL + + *qstate->env->now; } if(rep && (FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR || @@ -407,8 +414,11 @@ iter_prepend(struct iter_qstate* iq, str num_an = 0; for(p = iq->an_prepend_list; p; p = p->next) { sets[num_an++] = p->rrset; - if(ub_packed_rrset_ttl(p->rrset) < msg->rep->ttl) + if(ub_packed_rrset_ttl(p->rrset) < msg->rep->ttl) { msg->rep->ttl = ub_packed_rrset_ttl(p->rrset); + msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); + msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; + } } memcpy(sets+num_an, msg->rep->rrsets, msg->rep->an_numrrsets * sizeof(struct ub_packed_rrset_key*)); @@ -421,8 +431,11 @@ iter_prepend(struct iter_qstate* iq, str msg->rep->ns_numrrsets, p->rrset)) continue; sets[msg->rep->an_numrrsets + num_an + num_ns++] = p->rrset; - if(ub_packed_rrset_ttl(p->rrset) < msg->rep->ttl) + if(ub_packed_rrset_ttl(p->rrset) < msg->rep->ttl) { msg->rep->ttl = ub_packed_rrset_ttl(p->rrset); + msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); + msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; + } } memcpy(sets + num_an + msg->rep->an_numrrsets + num_ns, msg->rep->rrsets + msg->rep->an_numrrsets, @@ -1569,7 +1582,7 @@ processInitRequest(struct module_qstate* return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } if(!cache_fill_missing(qstate->env, iq->qchase.qclass, - qstate->region, iq->dp)) { + qstate->region, iq->dp, 0)) { errinf(qstate, "malloc failure, copy extra info into delegation point"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } @@ -2150,6 +2163,15 @@ processLastResort(struct module_qstate* verbose(VERB_QUERY, "configured stub or forward servers failed -- returning SERVFAIL"); return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL); } + if(qstate->env->cfg->harden_unverified_glue) { + if(!cache_fill_missing(qstate->env, iq->qchase.qclass, + qstate->region, iq->dp, PACKED_RRSET_UNVERIFIED_GLUE)) + log_err("out of memory in cache_fill_missing"); + if(iq->dp->usable_list) { + verbose(VERB_ALGO, "try unverified glue from cache"); + return next_state(iq, QUERYTARGETS_STATE); + } + } if(!iq->dp->has_parent_side_NS && dname_is_root(iq->dp->name)) { struct delegpt* dp; int nolock = 0; @@ -2192,7 +2214,7 @@ processLastResort(struct module_qstate* } /* see if that makes new names available */ if(!cache_fill_missing(qstate->env, iq->qchase.qclass, - qstate->region, iq->dp)) + qstate->region, iq->dp, 0)) log_err("out of memory in cache_fill_missing"); if(iq->dp->usable_list) { verbose(VERB_ALGO, "try parent-side-name, w. glue from cache"); @@ -3424,7 +3446,7 @@ processQueryResponse(struct module_qstat old_dp->name, old_dp->namelen); } if(!cache_fill_missing(qstate->env, iq->qchase.qclass, - qstate->region, iq->dp)) { + qstate->region, iq->dp, 0)) { errinf(qstate, "malloc failure, copy extra info into delegation point"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } @@ -3993,6 +4015,8 @@ processClassResponse(struct module_qstat to->rep->prefetch_ttl = from->rep->prefetch_ttl; if(from->rep->serve_expired_ttl < to->rep->serve_expired_ttl) to->rep->serve_expired_ttl = from->rep->serve_expired_ttl; + if(from->rep->serve_expired_norec_ttl < to->rep->serve_expired_norec_ttl) + to->rep->serve_expired_norec_ttl = from->rep->serve_expired_norec_ttl; } /* are we done? */ foriq->num_current_queries --; @@ -4355,7 +4379,10 @@ process_response(struct module_qstate* q if(verbosity >= VERB_ALGO) log_dns_msg("incoming scrubbed packet:", &iq->response->qinfo, iq->response->rep); - + + if(qstate->env->cfg->aggressive_nsec) { + limit_nsec_ttl(iq->response); + } if(event == module_event_capsfail || iq->caps_fallback) { if(qstate->env->cfg->qname_minimisation && iq->minimisation_state != DONOT_MINIMISE_STATE) { Index: iterator/iterator.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iterator.h,v diff -u -p -r1.22 iterator.h --- iterator/iterator.h 4 Sep 2024 09:36:40 -0000 1.22 +++ iterator/iterator.h 7 Feb 2025 21:25:44 -0000 @@ -57,7 +57,7 @@ struct rbtree_type; #define MAX_TARGET_COUNT 64 /** max number of upstream queries for a query and its subqueries, it is * never reset. */ -#define MAX_GLOBAL_QUOTA 128 +extern int MAX_GLOBAL_QUOTA; /** max number of target lookups per qstate, per delegation point */ #define MAX_DP_TARGET_COUNT 16 /** max number of nxdomains allowed for target lookups for a query and Index: libunbound/context.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/libunbound/context.c,v diff -u -p -r1.15 context.c --- libunbound/context.c 4 Sep 2024 09:36:40 -0000 1.15 +++ libunbound/context.c 7 Feb 2025 21:25:44 -0000 @@ -395,7 +395,7 @@ context_serialize_cancel(struct ctx_quer /* format of cancel: * o uint32 cmd * o uint32 async-id */ - uint8_t* p = (uint8_t*)reallocarray(NULL, sizeof(uint32_t), 2); + uint8_t* p = (uint8_t*)reallocarray(NULL, 2, sizeof(uint32_t)); if(!p) return NULL; *len = 2*sizeof(uint32_t); sldns_write_uint32(p, UB_LIBCMD_CANCEL); Index: libunbound/libworker.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/libunbound/libworker.c,v diff -u -p -r1.33 libworker.c --- libunbound/libworker.c 4 Sep 2024 09:36:40 -0000 1.33 +++ libunbound/libworker.c 7 Feb 2025 21:25:44 -0000 @@ -1058,3 +1058,19 @@ void dtio_mainfdcallback(int ATTR_UNUSED log_assert(0); } #endif + +#ifdef HAVE_NGTCP2 +void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} +#endif + +#ifdef HAVE_NGTCP2 +void doq_client_timer_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} +#endif Index: libunbound/unbound.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/libunbound/unbound.h,v diff -u -p -r1.15 unbound.h --- libunbound/unbound.h 5 Sep 2023 11:12:10 -0000 1.15 +++ libunbound/unbound.h 7 Feb 2025 21:25:44 -0000 @@ -845,6 +845,10 @@ struct ub_server_stats { long long qtls_resume; /** RPZ action stats */ long long rpz_action[UB_STATS_RPZ_ACTION_NUM]; + /** number of bytes in QUIC buffers */ + long long mem_quic; + /** number of queries over (DNS over) QUIC */ + long long qquic; }; /** Index: services/authzone.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/authzone.c,v diff -u -p -r1.28 authzone.c --- services/authzone.c 4 Sep 2024 09:36:40 -0000 1.28 +++ services/authzone.c 7 Feb 2025 21:25:44 -0000 @@ -3684,6 +3684,29 @@ auth_zone_parse_notify_serial(sldns_buff return 1; } +/** print addr to str, and if not 53, append "@port_number", for logs. */ +static void addr_port_to_str(struct sockaddr_storage* addr, socklen_t addrlen, + char* buf, size_t len) +{ + uint16_t port = 0; + if(addr_is_ip6(addr, addrlen)) { + struct sockaddr_in6* sa = (struct sockaddr_in6*)addr; + port = ntohs((uint16_t)sa->sin6_port); + } else { + struct sockaddr_in* sa = (struct sockaddr_in*)addr; + port = ntohs((uint16_t)sa->sin_port); + } + if(port == UNBOUND_DNS_PORT) { + /* If it is port 53, print it plainly. */ + addr_to_str(addr, addrlen, buf, len); + } else { + char a[256]; + a[0]=0; + addr_to_str(addr, addrlen, a, sizeof(a)); + snprintf(buf, len, "%s@%d", a, (int)port); + } +} + /** see if addr appears in the list */ static int addr_in_list(struct auth_addr* list, struct sockaddr_storage* addr, @@ -5516,7 +5539,7 @@ xfr_transfer_init_fetch(struct auth_xfer if(!xfr->task_transfer->cp) { char zname[255+1], as[256]; dname_str(xfr->name, zname); - addr_to_str(&addr, addrlen, as, sizeof(as)); + addr_port_to_str(&addr, addrlen, as, sizeof(as)); verbose(VERB_ALGO, "cannot create http cp " "connection for %s to %s", zname, as); return 0; @@ -5525,7 +5548,7 @@ xfr_transfer_init_fetch(struct auth_xfer if(verbosity >= VERB_ALGO) { char zname[255+1], as[256]; dname_str(xfr->name, zname); - addr_to_str(&addr, addrlen, as, sizeof(as)); + addr_port_to_str(&addr, addrlen, as, sizeof(as)); verbose(VERB_ALGO, "auth zone %s transfer next HTTP fetch from %s started", zname, as); } /* Create or refresh the list of allow_notify addrs */ @@ -5548,7 +5571,7 @@ xfr_transfer_init_fetch(struct auth_xfer if(!xfr->task_transfer->cp) { char zname[255+1], as[256]; dname_str(xfr->name, zname); - addr_to_str(&addr, addrlen, as, sizeof(as)); + addr_port_to_str(&addr, addrlen, as, sizeof(as)); verbose(VERB_ALGO, "cannot create tcp cp connection for " "xfr %s to %s", zname, as); return 0; @@ -5557,7 +5580,7 @@ xfr_transfer_init_fetch(struct auth_xfer if(verbosity >= VERB_ALGO) { char zname[255+1], as[256]; dname_str(xfr->name, zname); - addr_to_str(&addr, addrlen, as, sizeof(as)); + addr_port_to_str(&addr, addrlen, as, sizeof(as)); verbose(VERB_ALGO, "auth zone %s transfer next %s fetch from %s started", zname, (xfr->task_transfer->on_ixfr?"IXFR":"AXFR"), as); } @@ -5660,7 +5683,7 @@ xfr_master_add_addrs(struct auth_master* } if(verbosity >= VERB_ALGO) { char s[64]; - addr_to_str(&a->addr, a->addrlen, s, sizeof(s)); + addr_port_to_str(&a->addr, a->addrlen, s, sizeof(s)); verbose(VERB_ALGO, "auth host %s lookup %s", m->host, s); } @@ -6406,7 +6429,7 @@ xfr_probe_send_probe(struct auth_xfer* x if(!xfr->task_probe->cp) { char zname[255+1], as[256]; dname_str(xfr->name, zname); - addr_to_str(&addr, addrlen, as, sizeof(as)); + addr_port_to_str(&addr, addrlen, as, sizeof(as)); verbose(VERB_ALGO, "cannot create udp cp for " "probe %s to %s", zname, as); return 0; @@ -6426,7 +6449,7 @@ xfr_probe_send_probe(struct auth_xfer* x (struct sockaddr*)&addr, addrlen, 0)) { char zname[255+1], as[256]; dname_str(xfr->name, zname); - addr_to_str(&addr, addrlen, as, sizeof(as)); + addr_port_to_str(&addr, addrlen, as, sizeof(as)); verbose(VERB_ALGO, "failed to send soa probe for %s to %s", zname, as); return 0; @@ -6434,7 +6457,7 @@ xfr_probe_send_probe(struct auth_xfer* x if(verbosity >= VERB_ALGO) { char zname[255+1], as[256]; dname_str(xfr->name, zname); - addr_to_str(&addr, addrlen, as, sizeof(as)); + addr_port_to_str(&addr, addrlen, as, sizeof(as)); verbose(VERB_ALGO, "auth zone %s soa probe sent to %s", zname, as); } Index: services/listen_dnsport.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/listen_dnsport.c,v diff -u -p -r1.37 listen_dnsport.c --- services/listen_dnsport.c 4 Sep 2024 09:36:40 -0000 1.37 +++ services/listen_dnsport.c 7 Feb 2025 21:25:44 -0000 @@ -56,9 +56,11 @@ #include "util/net_help.h" #include "sldns/sbuffer.h" #include "sldns/parseutil.h" +#include "sldns/wire2str.h" #include "services/mesh.h" #include "util/fptr_wlist.h" #include "util/locks.h" +#include "util/timeval_func.h" #ifdef HAVE_NETDB_H #include @@ -79,9 +81,30 @@ #ifdef HAVE_NET_IF_H #include #endif + +#ifdef HAVE_TIME_H +#include +#endif +#include + +#ifdef HAVE_NGTCP2 +#include +#include +#ifdef HAVE_NGTCP2_NGTCP2_CRYPTO_QUICTLS_H +#include +#else +#include +#endif +#endif + +#ifdef HAVE_OPENSSL_SSL_H +#include +#endif + #ifdef HAVE_LINUX_NET_TSTAMP_H #include #endif + /** number of queued TCP connections for listen() */ #define TCP_BACKLOG 256 @@ -109,9 +132,11 @@ static int http2_response_buffer_lock_in /** * Debug print of the getaddrinfo returned address. * @param addr: the address returned. + * @param additional: additional text that describes the type of socket, + * or NULL for no text. */ static void -verbose_print_addr(struct addrinfo *addr) +verbose_print_addr(struct addrinfo *addr, const char* additional) { if(verbosity >= VERB_ALGO) { char buf[100]; @@ -126,13 +151,14 @@ verbose_print_addr(struct addrinfo *addr (void)strlcpy(buf, "(null)", sizeof(buf)); } buf[sizeof(buf)-1] = 0; - verbose(VERB_ALGO, "creating %s%s socket %s %d", + verbose(VERB_ALGO, "creating %s%s socket %s %d%s%s", addr->ai_socktype==SOCK_DGRAM?"udp": addr->ai_socktype==SOCK_STREAM?"tcp":"otherproto", addr->ai_family==AF_INET?"4": addr->ai_family==AF_INET6?"6": "_otherfam", buf, - ntohs(((struct sockaddr_in*)addr->ai_addr)->sin_port)); + ntohs(((struct sockaddr_in*)addr->ai_addr)->sin_port), + (additional?" ":""), (additional?additional:"")); } } @@ -673,7 +699,7 @@ create_udp_sock(int family, int socktype int create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto, int* reuseport, int transparent, int mss, int nodelay, int freebind, - int use_systemd, int dscp) + int use_systemd, int dscp, const char* additional) { int s = -1; char* err; @@ -692,7 +718,7 @@ create_tcp_accept_sock(struct addrinfo * #if !defined(IP_FREEBIND) (void)freebind; #endif - verbose_print_addr(addr); + verbose_print_addr(addr, additional); *noproto = 0; #ifdef HAVE_SYSTEMD if (!use_systemd || @@ -1008,7 +1034,8 @@ static int make_sock(int stype, const char* ifname, const char* port, struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd, int* reuseport, int transparent, int tcp_mss, int nodelay, int freebind, - int use_systemd, int dscp, struct unbound_socket* ub_sock) + int use_systemd, int dscp, struct unbound_socket* ub_sock, + const char* additional) { struct addrinfo *res = NULL; int r, s, inuse, noproto; @@ -1032,7 +1059,7 @@ make_sock(int stype, const char* ifname, return -1; } if(stype == SOCK_DGRAM) { - verbose_print_addr(res); + verbose_print_addr(res, additional); s = create_udp_sock(res->ai_family, res->ai_socktype, (struct sockaddr*)res->ai_addr, res->ai_addrlen, v6only, &inuse, &noproto, (int)rcv, (int)snd, 1, @@ -1045,7 +1072,7 @@ make_sock(int stype, const char* ifname, } else { s = create_tcp_accept_sock(res, v6only, &noproto, reuseport, transparent, tcp_mss, nodelay, freebind, use_systemd, - dscp); + dscp, additional); if(s == -1 && noproto && hints->ai_family == AF_INET6){ *noip6 = 1; } @@ -1079,7 +1106,8 @@ static int make_sock_port(int stype, const char* ifname, const char* port, struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd, int* reuseport, int transparent, int tcp_mss, int nodelay, int freebind, - int use_systemd, int dscp, struct unbound_socket* ub_sock) + int use_systemd, int dscp, struct unbound_socket* ub_sock, + const char* additional) { char* s = strchr(ifname, '@'); if(s) { @@ -1102,11 +1130,11 @@ make_sock_port(int stype, const char* if p[strlen(s+1)]=0; return make_sock(stype, newif, p, hints, v6only, noip6, rcv, snd, reuseport, transparent, tcp_mss, nodelay, freebind, - use_systemd, dscp, ub_sock); + use_systemd, dscp, ub_sock, additional); } return make_sock(stype, ifname, port, hints, v6only, noip6, rcv, snd, reuseport, transparent, tcp_mss, nodelay, freebind, use_systemd, - dscp, ub_sock); + dscp, ub_sock, additional); } /** @@ -1254,6 +1282,8 @@ if_is_ssl(const char* ifname, const char * @param use_systemd: if true, fetch sockets from systemd. * @param dnscrypt_port: dnscrypt service port number * @param dscp: DSCP to use. + * @param quic_port: dns over quic port number. + * @param http_notls_downstream: if no tls is used for https downstream. * @param sock_queue_timeout: the sock_queue_timeout from config. Seconds to * wait to discard if UDP packets have waited for long in the socket * buffer. @@ -1267,7 +1297,7 @@ ports_create_if(const char* ifname, int struct config_strlist* proxy_protocol_port, int* reuseport, int transparent, int tcp_mss, int freebind, int http2_nodelay, int use_systemd, int dnscrypt_port, int dscp, - int sock_queue_timeout) + int quic_port, int http_notls_downstream, int sock_queue_timeout) { int s, noip6=0; int is_https = if_is_https(ifname, port, https_port); @@ -1275,6 +1305,8 @@ ports_create_if(const char* ifname, int int is_pp2 = if_is_pp2(ifname, port, proxy_protocol_port); int nodelay = is_https && http2_nodelay; struct unbound_socket* ub_sock; + int is_doq = if_is_quic(ifname, port, quic_port); + const char* add = NULL; if(!do_udp && !do_tcp) return 0; @@ -1286,6 +1318,9 @@ ports_create_if(const char* ifname, int } else if(is_https) { fatal_exit("PROXYv2 and DoH combination not " "supported!"); + } else if(is_doq) { + fatal_exit("PROXYv2 and DoQ combination not " + "supported!"); } } @@ -1295,7 +1330,8 @@ ports_create_if(const char* ifname, int return 0; if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, &noip6, rcv, snd, reuseport, transparent, - tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) { + tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock, + (is_dnscrypt?"udpancil_dnscrypt":"udpancil"))) == -1) { free(ub_sock->addr); free(ub_sock); if(noip6) { @@ -1323,13 +1359,36 @@ ports_create_if(const char* ifname, int return 0; } } else if(do_udp) { + enum listen_type udp_port_type; ub_sock = calloc(1, sizeof(struct unbound_socket)); if(!ub_sock) return 0; + if(is_dnscrypt) { + udp_port_type = listen_type_udp_dnscrypt; + add = "dnscrypt"; + } else if(is_doq) { + udp_port_type = listen_type_doq; + add = "doq"; + if(((strchr(ifname, '@') && + atoi(strchr(ifname, '@')+1) == 53) || + (!strchr(ifname, '@') && atoi(port) == 53))) { + log_err("DNS over QUIC is not allowed on " + "port 53. Port 53 is for DNS " + "datagrams. Error for " + "interface '%s'.", ifname); + free(ub_sock->addr); + free(ub_sock); + return 0; + } + } else { + udp_port_type = listen_type_udp; + add = NULL; + } /* regular udp socket */ if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, &noip6, rcv, snd, reuseport, transparent, - tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) { + tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock, + add)) == -1) { free(ub_sock->addr); free(ub_sock); if(noip6) { @@ -1338,14 +1397,25 @@ ports_create_if(const char* ifname, int } return 0; } - if (sock_queue_timeout && !set_recvtimestamp(s)) { - log_warn("socket timestamping is not available"); + if(udp_port_type == listen_type_doq) { + if(!set_recvpktinfo(s, hints->ai_family)) { + sock_close(s); + free(ub_sock->addr); + free(ub_sock); + return 0; + } } - if(!port_insert(list, s, is_dnscrypt - ?listen_type_udp_dnscrypt : - (sock_queue_timeout ? - listen_type_udpancil:listen_type_udp), - is_pp2, ub_sock)) { + if(udp_port_type == listen_type_udp && sock_queue_timeout) + udp_port_type = listen_type_udpancil; + if (sock_queue_timeout) { + if(!set_recvtimestamp(s)) { + log_warn("socket timestamping is not available"); + } else { + if(udp_port_type == listen_type_udp) + udp_port_type = listen_type_udpancil; + } + } + if(!port_insert(list, s, udp_port_type, is_pp2, ub_sock)) { sock_close(s); free(ub_sock->addr); free(ub_sock); @@ -1359,17 +1429,24 @@ ports_create_if(const char* ifname, int ub_sock = calloc(1, sizeof(struct unbound_socket)); if(!ub_sock) return 0; - if(is_ssl) + if(is_ssl) { port_type = listen_type_ssl; - else if(is_https) + add = "tls"; + } else if(is_https) { port_type = listen_type_http; - else if(is_dnscrypt) + add = "https"; + if(http_notls_downstream) + add = "http"; + } else if(is_dnscrypt) { port_type = listen_type_tcp_dnscrypt; - else + add = "dnscrypt"; + } else { port_type = listen_type_tcp; + add = NULL; + } if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1, &noip6, 0, 0, reuseport, transparent, tcp_mss, nodelay, - freebind, use_systemd, dscp, ub_sock)) == -1) { + freebind, use_systemd, dscp, ub_sock, add)) == -1) { free(ub_sock->addr); free(ub_sock); if(noip6) { @@ -1446,8 +1523,10 @@ listen_create(struct comm_base* base, st size_t bufsize, int tcp_accept_count, int tcp_idle_timeout, int harden_large_queries, uint32_t http_max_streams, char* http_endpoint, int http_notls, struct tcl_list* tcp_conn_limit, - void* sslctx, struct dt_env* dtenv, comm_point_callback_type* cb, - void *cb_arg) + void* sslctx, struct dt_env* dtenv, struct doq_table* doq_table, + struct ub_randstate* rnd, const char* ssl_service_key, + const char* ssl_service_pem, struct config_file* cfg, + comm_point_callback_type* cb, void *cb_arg) { struct listen_dnsport* front = (struct listen_dnsport*) malloc(sizeof(struct listen_dnsport)); @@ -1471,6 +1550,16 @@ listen_create(struct comm_base* base, st cp = comm_point_create_udp(base, ports->fd, front->udp_buff, ports->pp2_enabled, cb, cb_arg, ports->socket); + } else if(ports->ftype == listen_type_doq) { +#ifndef HAVE_NGTCP2 + log_warn("Unbound is not compiled with " + "ngtcp2. This is required to use DNS " + "over QUIC."); +#endif + cp = comm_point_create_doq(base, ports->fd, + front->udp_buff, cb, cb_arg, ports->socket, + doq_table, rnd, ssl_service_key, + ssl_service_pem, cfg); } else if(ports->ftype == listen_type_tcp || ports->ftype == listen_type_tcp_dnscrypt) { cp = comm_point_create_tcp(base, ports->fd, @@ -1858,7 +1947,9 @@ listening_ports_open(struct config_file* reuseport, cfg->ip_transparent, cfg->tcp_mss, cfg->ip_freebind, cfg->http_nodelay, cfg->use_systemd, - cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) { + cfg->dnscrypt_port, cfg->ip_dscp, + cfg->quic_port, cfg->http_notls_downstream, + cfg->sock_queue_timeout)) { listening_ports_free(list); return NULL; } @@ -1875,7 +1966,9 @@ listening_ports_open(struct config_file* reuseport, cfg->ip_transparent, cfg->tcp_mss, cfg->ip_freebind, cfg->http_nodelay, cfg->use_systemd, - cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) { + cfg->dnscrypt_port, cfg->ip_dscp, + cfg->quic_port, cfg->http_notls_downstream, + cfg->sock_queue_timeout)) { listening_ports_free(list); return NULL; } @@ -1894,7 +1987,9 @@ listening_ports_open(struct config_file* reuseport, cfg->ip_transparent, cfg->tcp_mss, cfg->ip_freebind, cfg->http_nodelay, cfg->use_systemd, - cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) { + cfg->dnscrypt_port, cfg->ip_dscp, + cfg->quic_port, cfg->http_notls_downstream, + cfg->sock_queue_timeout)) { listening_ports_free(list); return NULL; } @@ -1910,7 +2005,9 @@ listening_ports_open(struct config_file* reuseport, cfg->ip_transparent, cfg->tcp_mss, cfg->ip_freebind, cfg->http_nodelay, cfg->use_systemd, - cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) { + cfg->dnscrypt_port, cfg->ip_dscp, + cfg->quic_port, cfg->http_notls_downstream, + cfg->sock_queue_timeout)) { listening_ports_free(list); return NULL; } @@ -1928,7 +2025,9 @@ listening_ports_open(struct config_file* reuseport, cfg->ip_transparent, cfg->tcp_mss, cfg->ip_freebind, cfg->http_nodelay, cfg->use_systemd, - cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) { + cfg->dnscrypt_port, cfg->ip_dscp, + cfg->quic_port, cfg->http_notls_downstream, + cfg->sock_queue_timeout)) { listening_ports_free(list); return NULL; } @@ -1944,7 +2043,9 @@ listening_ports_open(struct config_file* reuseport, cfg->ip_transparent, cfg->tcp_mss, cfg->ip_freebind, cfg->http_nodelay, cfg->use_systemd, - cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) { + cfg->dnscrypt_port, cfg->ip_dscp, + cfg->quic_port, cfg->http_notls_downstream, + cfg->sock_queue_timeout)) { listening_ports_free(list); return NULL; } @@ -3154,3 +3255,2368 @@ nghttp2_session_callbacks* http2_req_cal return callbacks; } #endif /* HAVE_NGHTTP2 */ + +#ifdef HAVE_NGTCP2 +struct doq_table* +doq_table_create(struct config_file* cfg, struct ub_randstate* rnd) +{ + struct doq_table* table = calloc(1, sizeof(*table)); + if(!table) + return NULL; + table->idle_timeout = ((uint64_t)cfg->tcp_idle_timeout)* + NGTCP2_MILLISECONDS; + table->sv_scidlen = 16; + table->static_secret_len = 16; + table->static_secret = malloc(table->static_secret_len); + if(!table->static_secret) { + free(table); + return NULL; + } + doq_fill_rand(rnd, table->static_secret, table->static_secret_len); + table->conn_tree = rbtree_create(doq_conn_cmp); + if(!table->conn_tree) { + free(table->static_secret); + free(table); + return NULL; + } + table->conid_tree = rbtree_create(doq_conid_cmp); + if(!table->conid_tree) { + free(table->static_secret); + free(table->conn_tree); + free(table); + return NULL; + } + table->timer_tree = rbtree_create(doq_timer_cmp); + if(!table->timer_tree) { + free(table->static_secret); + free(table->conn_tree); + free(table->conid_tree); + free(table); + return NULL; + } + lock_rw_init(&table->lock); + lock_rw_init(&table->conid_lock); + lock_basic_init(&table->size_lock); + lock_protect(&table->lock, &table->static_secret, + sizeof(table->static_secret)); + lock_protect(&table->lock, &table->static_secret_len, + sizeof(table->static_secret_len)); + lock_protect(&table->lock, table->static_secret, + table->static_secret_len); + lock_protect(&table->lock, &table->sv_scidlen, + sizeof(table->sv_scidlen)); + lock_protect(&table->lock, &table->idle_timeout, + sizeof(table->idle_timeout)); + lock_protect(&table->lock, &table->conn_tree, sizeof(table->conn_tree)); + lock_protect(&table->lock, table->conn_tree, sizeof(*table->conn_tree)); + lock_protect(&table->conid_lock, table->conid_tree, + sizeof(*table->conid_tree)); + lock_protect(&table->lock, table->timer_tree, + sizeof(*table->timer_tree)); + lock_protect(&table->size_lock, &table->current_size, + sizeof(table->current_size)); + return table; +} + +/** delete elements from the connection tree */ +static void +conn_tree_del(rbnode_type* node, void* arg) +{ + struct doq_table* table = (struct doq_table*)arg; + struct doq_conn* conn; + if(!node) + return; + conn = (struct doq_conn*)node->key; + if(conn->timer.timer_in_list) { + /* Remove timer from list first, because finding the rbnode + * element of the setlist of same timeouts needs tree lookup. + * Edit the tree structure after that lookup. */ + doq_timer_list_remove(conn->table, &conn->timer); + } + if(conn->timer.timer_in_tree) + doq_timer_tree_remove(conn->table, &conn->timer); + doq_table_quic_size_subtract(table, sizeof(*conn)+conn->key.dcidlen); + doq_conn_delete(conn, table); +} + +/** delete elements from the connection id tree */ +static void +conid_tree_del(rbnode_type* node, void* ATTR_UNUSED(arg)) +{ + if(!node) + return; + doq_conid_delete((struct doq_conid*)node->key); +} + +void +doq_table_delete(struct doq_table* table) +{ + if(!table) + return; + lock_rw_destroy(&table->lock); + free(table->static_secret); + if(table->conn_tree) { + traverse_postorder(table->conn_tree, conn_tree_del, table); + free(table->conn_tree); + } + lock_rw_destroy(&table->conid_lock); + if(table->conid_tree) { + /* The tree should be empty, because the doq_conn_delete calls + * above should have also removed their conid elements. */ + traverse_postorder(table->conid_tree, conid_tree_del, NULL); + free(table->conid_tree); + } + lock_basic_destroy(&table->size_lock); + if(table->timer_tree) { + /* The tree should be empty, because the conn_tree_del calls + * above should also have removed them. Also the doq_timer + * is part of the doq_conn struct, so is already freed. */ + free(table->timer_tree); + } + table->write_list_first = NULL; + table->write_list_last = NULL; + free(table); +} + +struct doq_timer* +doq_timer_find_time(struct doq_table* table, struct timeval* tv) +{ + struct doq_timer key; + struct rbnode_type* node; + memset(&key, 0, sizeof(key)); + key.time.tv_sec = tv->tv_sec; + key.time.tv_usec = tv->tv_usec; + node = rbtree_search(table->timer_tree, &key); + if(node) + return (struct doq_timer*)node->key; + return NULL; +} + +void +doq_timer_tree_remove(struct doq_table* table, struct doq_timer* timer) +{ + if(!timer->timer_in_tree) + return; + rbtree_delete(table->timer_tree, timer); + timer->timer_in_tree = 0; + /* This item could have more timers in the same set. */ + if(timer->setlist_first) { + struct doq_timer* rb_timer = timer->setlist_first; + /* del first element from setlist */ + if(rb_timer->setlist_next) + rb_timer->setlist_next->setlist_prev = NULL; + else + timer->setlist_last = NULL; + timer->setlist_first = rb_timer->setlist_next; + rb_timer->setlist_prev = NULL; + rb_timer->setlist_next = NULL; + rb_timer->timer_in_list = 0; + /* insert it into the tree as new rb element */ + memset(&rb_timer->node, 0, sizeof(rb_timer->node)); + rb_timer->node.key = rb_timer; + rbtree_insert(table->timer_tree, &rb_timer->node); + rb_timer->timer_in_tree = 1; + /* the setlist, if any remainder, moves to the rb element */ + rb_timer->setlist_first = timer->setlist_first; + rb_timer->setlist_last = timer->setlist_last; + timer->setlist_first = NULL; + timer->setlist_last = NULL; + rb_timer->worker_doq_socket = timer->worker_doq_socket; + } + timer->worker_doq_socket = NULL; +} + +void +doq_timer_list_remove(struct doq_table* table, struct doq_timer* timer) +{ + struct doq_timer* rb_timer; + if(!timer->timer_in_list) + return; + /* The item in the rbtree has the list start and end. */ + rb_timer = doq_timer_find_time(table, &timer->time); + if(rb_timer) { + if(timer->setlist_prev) + timer->setlist_prev->setlist_next = timer->setlist_next; + else + rb_timer->setlist_first = timer->setlist_next; + if(timer->setlist_next) + timer->setlist_next->setlist_prev = timer->setlist_prev; + else + rb_timer->setlist_last = timer->setlist_prev; + timer->setlist_prev = NULL; + timer->setlist_next = NULL; + } + timer->timer_in_list = 0; +} + +/** doq append timer to setlist */ +static void +doq_timer_list_append(struct doq_timer* rb_timer, struct doq_timer* timer) +{ + log_assert(timer->timer_in_list == 0); + timer->timer_in_list = 1; + timer->setlist_next = NULL; + timer->setlist_prev = rb_timer->setlist_last; + if(rb_timer->setlist_last) + rb_timer->setlist_last->setlist_next = timer; + else + rb_timer->setlist_first = timer; + rb_timer->setlist_last = timer; +} + +void +doq_timer_unset(struct doq_table* table, struct doq_timer* timer) +{ + if(timer->timer_in_list) { + /* Remove timer from list first, because finding the rbnode + * element of the setlist of same timeouts needs tree lookup. + * Edit the tree structure after that lookup. */ + doq_timer_list_remove(table, timer); + } + if(timer->timer_in_tree) + doq_timer_tree_remove(table, timer); + timer->worker_doq_socket = NULL; +} + +void doq_timer_set(struct doq_table* table, struct doq_timer* timer, + struct doq_server_socket* worker_doq_socket, struct timeval* tv) +{ + struct doq_timer* rb_timer; + if(verbosity >= VERB_ALGO && timer->conn) { + char a[256]; + struct timeval rel; + addr_to_str((void*)&timer->conn->key.paddr.addr, + timer->conn->key.paddr.addrlen, a, sizeof(a)); + timeval_subtract(&rel, tv, worker_doq_socket->now_tv); + verbose(VERB_ALGO, "doq %s timer set %d.%6.6d in %d.%6.6d", + a, (int)tv->tv_sec, (int)tv->tv_usec, + (int)rel.tv_sec, (int)rel.tv_usec); + } + if(timer->timer_in_tree || timer->timer_in_list) { + if(timer->time.tv_sec == tv->tv_sec && + timer->time.tv_usec == tv->tv_usec) + return; /* already set on that time */ + doq_timer_unset(table, timer); + } + timer->time.tv_sec = tv->tv_sec; + timer->time.tv_usec = tv->tv_usec; + rb_timer = doq_timer_find_time(table, tv); + if(rb_timer) { + /* There is a timeout already with this value. Timer is + * added to the setlist. */ + doq_timer_list_append(rb_timer, timer); + } else { + /* There is no timeout with this value. Make timer a new + * tree element. */ + memset(&timer->node, 0, sizeof(timer->node)); + timer->node.key = timer; + rbtree_insert(table->timer_tree, &timer->node); + timer->timer_in_tree = 1; + timer->setlist_first = NULL; + timer->setlist_last = NULL; + timer->worker_doq_socket = worker_doq_socket; + } +} + +struct doq_conn* +doq_conn_create(struct comm_point* c, struct doq_pkt_addr* paddr, + const uint8_t* dcid, size_t dcidlen, uint32_t version) +{ + struct doq_conn* conn = calloc(1, sizeof(*conn)); + if(!conn) + return NULL; + conn->node.key = conn; + conn->doq_socket = c->doq_socket; + conn->table = c->doq_socket->table; + memmove(&conn->key.paddr.addr, &paddr->addr, paddr->addrlen); + conn->key.paddr.addrlen = paddr->addrlen; + memmove(&conn->key.paddr.localaddr, &paddr->localaddr, + paddr->localaddrlen); + conn->key.paddr.localaddrlen = paddr->localaddrlen; + conn->key.paddr.ifindex = paddr->ifindex; + conn->key.dcid = memdup((void*)dcid, dcidlen); + if(!conn->key.dcid) { + free(conn); + return NULL; + } + conn->key.dcidlen = dcidlen; + conn->version = version; +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + ngtcp2_ccerr_default(&conn->ccerr); +#else + ngtcp2_connection_close_error_default(&conn->last_error); +#endif + rbtree_init(&conn->stream_tree, &doq_stream_cmp); + conn->timer.conn = conn; + lock_basic_init(&conn->lock); + lock_protect(&conn->lock, &conn->key, sizeof(conn->key)); + lock_protect(&conn->lock, &conn->doq_socket, sizeof(conn->doq_socket)); + lock_protect(&conn->lock, &conn->table, sizeof(conn->table)); + lock_protect(&conn->lock, &conn->is_deleted, sizeof(conn->is_deleted)); + lock_protect(&conn->lock, &conn->version, sizeof(conn->version)); + lock_protect(&conn->lock, &conn->conn, sizeof(conn->conn)); + lock_protect(&conn->lock, &conn->conid_list, sizeof(conn->conid_list)); +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + lock_protect(&conn->lock, &conn->ccerr, sizeof(conn->ccerr)); +#else + lock_protect(&conn->lock, &conn->last_error, sizeof(conn->last_error)); +#endif + lock_protect(&conn->lock, &conn->tls_alert, sizeof(conn->tls_alert)); + lock_protect(&conn->lock, &conn->ssl, sizeof(conn->ssl)); + lock_protect(&conn->lock, &conn->close_pkt, sizeof(conn->close_pkt)); + lock_protect(&conn->lock, &conn->close_pkt_len, sizeof(conn->close_pkt_len)); + lock_protect(&conn->lock, &conn->close_ecn, sizeof(conn->close_ecn)); + lock_protect(&conn->lock, &conn->stream_tree, sizeof(conn->stream_tree)); + lock_protect(&conn->lock, &conn->stream_write_first, sizeof(conn->stream_write_first)); + lock_protect(&conn->lock, &conn->stream_write_last, sizeof(conn->stream_write_last)); + lock_protect(&conn->lock, &conn->write_interest, sizeof(conn->write_interest)); + lock_protect(&conn->lock, &conn->on_write_list, sizeof(conn->on_write_list)); + lock_protect(&conn->lock, &conn->write_prev, sizeof(conn->write_prev)); + lock_protect(&conn->lock, &conn->write_next, sizeof(conn->write_next)); + return conn; +} + +/** delete stream tree node */ +static void +stream_tree_del(rbnode_type* node, void* arg) +{ + struct doq_table* table = (struct doq_table*)arg; + struct doq_stream* stream; + if(!node) + return; + stream = (struct doq_stream*)node; + if(stream->in) + doq_table_quic_size_subtract(table, stream->inlen); + if(stream->out) + doq_table_quic_size_subtract(table, stream->outlen); + doq_table_quic_size_subtract(table, sizeof(*stream)); + doq_stream_delete(stream); +} + +void +doq_conn_delete(struct doq_conn* conn, struct doq_table* table) +{ + if(!conn) + return; + lock_basic_destroy(&conn->lock); + lock_rw_wrlock(&conn->table->conid_lock); + doq_conn_clear_conids(conn); + lock_rw_unlock(&conn->table->conid_lock); + ngtcp2_conn_del(conn->conn); + if(conn->stream_tree.count != 0) { + traverse_postorder(&conn->stream_tree, stream_tree_del, table); + } + free(conn->key.dcid); + SSL_free(conn->ssl); + free(conn->close_pkt); + free(conn); +} + +int +doq_conn_cmp(const void* key1, const void* key2) +{ + struct doq_conn* c = (struct doq_conn*)key1; + struct doq_conn* d = (struct doq_conn*)key2; + int r; + /* Compared in the order destination address, then + * local address, ifindex and then dcid. + * So that for a search for findlessorequal for the destination + * address will find connections to that address, with different + * dcids. + * Also a printout in sorted order prints the connections by IP + * address of destination, and then a number of them depending on the + * dcids. */ + if(c->key.paddr.addrlen != d->key.paddr.addrlen) { + if(c->key.paddr.addrlen < d->key.paddr.addrlen) + return -1; + return 1; + } + if((r=memcmp(&c->key.paddr.addr, &d->key.paddr.addr, + c->key.paddr.addrlen))!=0) + return r; + if(c->key.paddr.localaddrlen != d->key.paddr.localaddrlen) { + if(c->key.paddr.localaddrlen < d->key.paddr.localaddrlen) + return -1; + return 1; + } + if((r=memcmp(&c->key.paddr.localaddr, &d->key.paddr.localaddr, + c->key.paddr.localaddrlen))!=0) + return r; + if(c->key.paddr.ifindex != d->key.paddr.ifindex) { + if(c->key.paddr.ifindex < d->key.paddr.ifindex) + return -1; + return 1; + } + if(c->key.dcidlen != d->key.dcidlen) { + if(c->key.dcidlen < d->key.dcidlen) + return -1; + return 1; + } + if((r=memcmp(c->key.dcid, d->key.dcid, c->key.dcidlen))!=0) + return r; + return 0; +} + +int doq_conid_cmp(const void* key1, const void* key2) +{ + struct doq_conid* c = (struct doq_conid*)key1; + struct doq_conid* d = (struct doq_conid*)key2; + if(c->cidlen != d->cidlen) { + if(c->cidlen < d->cidlen) + return -1; + return 1; + } + return memcmp(c->cid, d->cid, c->cidlen); +} + +int doq_timer_cmp(const void* key1, const void* key2) +{ + struct doq_timer* e = (struct doq_timer*)key1; + struct doq_timer* f = (struct doq_timer*)key2; + if(e->time.tv_sec < f->time.tv_sec) + return -1; + if(e->time.tv_sec > f->time.tv_sec) + return 1; + if(e->time.tv_usec < f->time.tv_usec) + return -1; + if(e->time.tv_usec > f->time.tv_usec) + return 1; + return 0; +} + +int doq_stream_cmp(const void* key1, const void* key2) +{ + struct doq_stream* c = (struct doq_stream*)key1; + struct doq_stream* d = (struct doq_stream*)key2; + if(c->stream_id != d->stream_id) { + if(c->stream_id < d->stream_id) + return -1; + return 1; + } + return 0; +} + +/** doq store a local address in repinfo */ +static void +doq_repinfo_store_localaddr(struct comm_reply* repinfo, + struct doq_addr_storage* localaddr, socklen_t localaddrlen) +{ + /* use the pktinfo that we have for ancillary udp data otherwise, + * this saves space for a sockaddr */ + memset(&repinfo->pktinfo, 0, sizeof(repinfo->pktinfo)); + if(addr_is_ip6((void*)localaddr, localaddrlen)) { +#ifdef IPV6_PKTINFO + struct sockaddr_in6* sa6 = (struct sockaddr_in6*)localaddr; + memmove(&repinfo->pktinfo.v6info.ipi6_addr, + &sa6->sin6_addr, sizeof(struct in6_addr)); + repinfo->doq_srcport = sa6->sin6_port; +#endif + repinfo->srctype = 6; + } else { +#ifdef IP_PKTINFO + struct sockaddr_in* sa = (struct sockaddr_in*)localaddr; + memmove(&repinfo->pktinfo.v4info.ipi_addr, + &sa->sin_addr, sizeof(struct in_addr)); + repinfo->doq_srcport = sa->sin_port; +#elif defined(IP_RECVDSTADDR) + struct sockaddr_in* sa = (struct sockaddr_in*)localaddr; + memmove(&repinfo->pktinfo.v4addr, &sa->sin_addr, + sizeof(struct in_addr)); + repinfo->doq_srcport = sa->sin_port; +#endif + repinfo->srctype = 4; + } +} + +/** doq retrieve localaddr from repinfo */ +static void +doq_repinfo_retrieve_localaddr(struct comm_reply* repinfo, + struct doq_addr_storage* localaddr, socklen_t* localaddrlen) +{ + if(repinfo->srctype == 6) { +#ifdef IPV6_PKTINFO + struct sockaddr_in6* sa6 = (struct sockaddr_in6*)localaddr; + *localaddrlen = (socklen_t)sizeof(struct sockaddr_in6); + memset(sa6, 0, *localaddrlen); + sa6->sin6_family = AF_INET6; + memmove(&sa6->sin6_addr, &repinfo->pktinfo.v6info.ipi6_addr, + *localaddrlen); + sa6->sin6_port = repinfo->doq_srcport; +#endif + } else { +#ifdef IP_PKTINFO + struct sockaddr_in* sa = (struct sockaddr_in*)localaddr; + *localaddrlen = (socklen_t)sizeof(struct sockaddr_in); + memset(sa, 0, *localaddrlen); + sa->sin_family = AF_INET; + memmove(&sa->sin_addr, &repinfo->pktinfo.v4info.ipi_addr, + *localaddrlen); + sa->sin_port = repinfo->doq_srcport; +#elif defined(IP_RECVDSTADDR) + struct sockaddr_in* sa = (struct sockaddr_in*)localaddr; + *localaddrlen = (socklen_t)sizeof(struct sockaddr_in); + memset(sa, 0, *localaddrlen); + sa->sin_family = AF_INET; + memmove(&sa->sin_addr, &repinfo->pktinfo.v4addr, + sizeof(struct in_addr)); + sa->sin_port = repinfo->doq_srcport; +#endif + } +} + +/** doq write a connection key into repinfo, false if it does not fit */ +static int +doq_conn_key_store_repinfo(struct doq_conn_key* key, + struct comm_reply* repinfo) +{ + repinfo->is_proxied = 0; + repinfo->doq_ifindex = key->paddr.ifindex; + repinfo->remote_addrlen = key->paddr.addrlen; + memmove(&repinfo->remote_addr, &key->paddr.addr, + repinfo->remote_addrlen); + repinfo->client_addrlen = key->paddr.addrlen; + memmove(&repinfo->client_addr, &key->paddr.addr, + repinfo->client_addrlen); + doq_repinfo_store_localaddr(repinfo, &key->paddr.localaddr, + key->paddr.localaddrlen); + if(key->dcidlen > sizeof(repinfo->doq_dcid)) + return 0; + repinfo->doq_dcidlen = key->dcidlen; + memmove(repinfo->doq_dcid, key->dcid, key->dcidlen); + return 1; +} + +void +doq_conn_key_from_repinfo(struct doq_conn_key* key, struct comm_reply* repinfo) +{ + key->paddr.ifindex = repinfo->doq_ifindex; + key->paddr.addrlen = repinfo->remote_addrlen; + memmove(&key->paddr.addr, &repinfo->remote_addr, + repinfo->remote_addrlen); + doq_repinfo_retrieve_localaddr(repinfo, &key->paddr.localaddr, + &key->paddr.localaddrlen); + key->dcidlen = repinfo->doq_dcidlen; + key->dcid = repinfo->doq_dcid; +} + +/** doq add a stream to the connection */ +static void +doq_conn_add_stream(struct doq_conn* conn, struct doq_stream* stream) +{ + (void)rbtree_insert(&conn->stream_tree, &stream->node); +} + +/** doq delete a stream from the connection */ +static void +doq_conn_del_stream(struct doq_conn* conn, struct doq_stream* stream) +{ + (void)rbtree_delete(&conn->stream_tree, &stream->node); +} + +/** doq create new stream */ +static struct doq_stream* +doq_stream_create(int64_t stream_id) +{ + struct doq_stream* stream = calloc(1, sizeof(*stream)); + if(!stream) + return NULL; + stream->node.key = stream; + stream->stream_id = stream_id; + return stream; +} + +void doq_stream_delete(struct doq_stream* stream) +{ + if(!stream) + return; + free(stream->in); + free(stream->out); + free(stream); +} + +struct doq_stream* +doq_stream_find(struct doq_conn* conn, int64_t stream_id) +{ + rbnode_type* node; + struct doq_stream key; + key.node.key = &key; + key.stream_id = stream_id; + node = rbtree_search(&conn->stream_tree, &key); + if(node) + return (struct doq_stream*)node->key; + return NULL; +} + +/** doq put stream on the conn write list */ +static void +doq_stream_on_write_list(struct doq_conn* conn, struct doq_stream* stream) +{ + if(stream->on_write_list) + return; + stream->write_prev = conn->stream_write_last; + if(conn->stream_write_last) + conn->stream_write_last->write_next = stream; + else + conn->stream_write_first = stream; + conn->stream_write_last = stream; + stream->write_next = NULL; + stream->on_write_list = 1; +} + +/** doq remove stream from the conn write list */ +static void +doq_stream_off_write_list(struct doq_conn* conn, struct doq_stream* stream) +{ + if(!stream->on_write_list) + return; + if(stream->write_next) + stream->write_next->write_prev = stream->write_prev; + else conn->stream_write_last = stream->write_prev; + if(stream->write_prev) + stream->write_prev->write_next = stream->write_next; + else conn->stream_write_first = stream->write_next; + stream->write_prev = NULL; + stream->write_next = NULL; + stream->on_write_list = 0; +} + +/** doq stream remove in buffer */ +static void +doq_stream_remove_in_buffer(struct doq_stream* stream, struct doq_table* table) +{ + if(stream->in) { + doq_table_quic_size_subtract(table, stream->inlen); + free(stream->in); + stream->in = NULL; + stream->inlen = 0; + } +} + +/** doq stream remove out buffer */ +static void +doq_stream_remove_out_buffer(struct doq_stream* stream, + struct doq_table* table) +{ + if(stream->out) { + doq_table_quic_size_subtract(table, stream->outlen); + free(stream->out); + stream->out = NULL; + stream->outlen = 0; + } +} + +int +doq_stream_close(struct doq_conn* conn, struct doq_stream* stream, + int send_shutdown) +{ + int ret; + if(stream->is_closed) + return 1; + stream->is_closed = 1; + doq_stream_off_write_list(conn, stream); + if(send_shutdown) { + verbose(VERB_ALGO, "doq: shutdown stream_id %d with app_error_code %d", + (int)stream->stream_id, (int)DOQ_APP_ERROR_CODE); + ret = ngtcp2_conn_shutdown_stream(conn->conn, +#ifdef HAVE_NGTCP2_CONN_SHUTDOWN_STREAM4 + 0, +#endif + stream->stream_id, DOQ_APP_ERROR_CODE); + if(ret != 0) { + log_err("doq ngtcp2_conn_shutdown_stream %d failed: %s", + (int)stream->stream_id, ngtcp2_strerror(ret)); + return 0; + } + doq_conn_write_enable(conn); + } + verbose(VERB_ALGO, "doq: conn extend max streams bidi by 1"); + ngtcp2_conn_extend_max_streams_bidi(conn->conn, 1); + doq_conn_write_enable(conn); + doq_stream_remove_in_buffer(stream, conn->doq_socket->table); + doq_stream_remove_out_buffer(stream, conn->doq_socket->table); + doq_table_quic_size_subtract(conn->doq_socket->table, sizeof(*stream)); + doq_conn_del_stream(conn, stream); + doq_stream_delete(stream); + return 1; +} + +/** doq stream pick up answer data from buffer */ +static int +doq_stream_pickup_answer(struct doq_stream* stream, struct sldns_buffer* buf) +{ + stream->is_answer_available = 1; + if(stream->out) { + free(stream->out); + stream->out = NULL; + stream->outlen = 0; + } + stream->nwrite = 0; + stream->outlen = sldns_buffer_limit(buf); + /* For quic the output bytes have to stay allocated and available, + * for potential resends, until the remote end has acknowledged them. + * This includes the tcplen start uint16_t, in outlen_wire. */ + stream->outlen_wire = htons(stream->outlen); + stream->out = memdup(sldns_buffer_begin(buf), sldns_buffer_limit(buf)); + if(!stream->out) { + log_err("doq could not send answer: out of memory"); + return 0; + } + return 1; +} + +int +doq_stream_send_reply(struct doq_conn* conn, struct doq_stream* stream, + struct sldns_buffer* buf) +{ + if(verbosity >= VERB_ALGO) { + char* s = sldns_wire2str_pkt(sldns_buffer_begin(buf), + sldns_buffer_limit(buf)); + verbose(VERB_ALGO, "doq stream %d response\n%s", + (int)stream->stream_id, (s?s:"null")); + free(s); + } + if(stream->out) + doq_table_quic_size_subtract(conn->doq_socket->table, + stream->outlen); + if(!doq_stream_pickup_answer(stream, buf)) + return 0; + doq_table_quic_size_add(conn->doq_socket->table, stream->outlen); + doq_stream_on_write_list(conn, stream); + doq_conn_write_enable(conn); + return 1; +} + +/** doq stream data length has completed, allocations can be done. False on + * allocation failure. */ +static int +doq_stream_datalen_complete(struct doq_stream* stream, struct doq_table* table) +{ + if(stream->inlen > 1024*1024) { + log_err("doq stream in length too large %d", + (int)stream->inlen); + return 0; + } + stream->in = calloc(1, stream->inlen); + if(!stream->in) { + log_err("doq could not read stream, calloc failed: " + "out of memory"); + return 0; + } + doq_table_quic_size_add(table, stream->inlen); + return 1; +} + +/** doq stream data is complete, the input data has been received. */ +static int +doq_stream_data_complete(struct doq_conn* conn, struct doq_stream* stream) +{ + struct comm_point* c; + if(verbosity >= VERB_ALGO) { + char* s = sldns_wire2str_pkt(stream->in, stream->inlen); + char a[128]; + addr_to_str((void*)&conn->key.paddr.addr, + conn->key.paddr.addrlen, a, sizeof(a)); + verbose(VERB_ALGO, "doq %s stream %d incoming query\n%s", + a, (int)stream->stream_id, (s?s:"null")); + free(s); + } + stream->is_query_complete = 1; + c = conn->doq_socket->cp; + if(!stream->in) { + verbose(VERB_ALGO, "doq_stream_data_complete: no in buffer"); + return 0; + } + if(stream->inlen > sldns_buffer_capacity(c->buffer)) { + verbose(VERB_ALGO, "doq_stream_data_complete: query too long"); + return 0; + } + sldns_buffer_clear(c->buffer); + sldns_buffer_write(c->buffer, stream->in, stream->inlen); + sldns_buffer_flip(c->buffer); + c->repinfo.c = c; + if(!doq_conn_key_store_repinfo(&conn->key, &c->repinfo)) { + verbose(VERB_ALGO, "doq_stream_data_complete: connection " + "DCID too long"); + return 0; + } + c->repinfo.doq_streamid = stream->stream_id; + conn->doq_socket->current_conn = conn; + fptr_ok(fptr_whitelist_comm_point(c->callback)); + if( (*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, &c->repinfo)) { + conn->doq_socket->current_conn = NULL; + if(!doq_stream_send_reply(conn, stream, c->buffer)) { + verbose(VERB_ALGO, "doq: failed to send_reply"); + return 0; + } + return 1; + } + conn->doq_socket->current_conn = NULL; + return 1; +} + +/** doq receive data for a stream, more bytes of the incoming data */ +static int +doq_stream_recv_data(struct doq_stream* stream, const uint8_t* data, + size_t datalen, int* recv_done, struct doq_table* table) +{ + int got_data = 0; + /* read the tcplength uint16_t at the start */ + if(stream->nread < 2) { + uint16_t tcplen = 0; + size_t todolen = 2 - stream->nread; + + if(stream->nread > 0) { + /* put in the already read byte if there is one */ + tcplen = stream->inlen; + } + if(datalen < todolen) + todolen = datalen; + memmove(((uint8_t*)&tcplen)+stream->nread, data, todolen); + stream->nread += todolen; + data += todolen; + datalen -= todolen; + if(stream->nread == 2) { + /* the initial length value is completed */ + stream->inlen = ntohs(tcplen); + if(!doq_stream_datalen_complete(stream, table)) + return 0; + } else { + /* store for later */ + stream->inlen = tcplen; + return 1; + } + } + /* if there are more data bytes */ + if(datalen > 0) { + size_t to_write = datalen; + if(stream->nread-2 > stream->inlen) { + verbose(VERB_ALGO, "doq stream buffer too small"); + return 0; + } + if(datalen > stream->inlen - (stream->nread-2)) + to_write = stream->inlen - (stream->nread-2); + if(to_write > 0) { + if(!stream->in) { + verbose(VERB_ALGO, "doq: stream has " + "no buffer"); + return 0; + } + memmove(stream->in+(stream->nread-2), data, to_write); + stream->nread += to_write; + data += to_write; + datalen -= to_write; + got_data = 1; + } + } + /* Are there extra bytes received after the end? If so, log them. */ + if(datalen > 0) { + if(verbosity >= VERB_ALGO) + log_hex("doq stream has extra bytes received after end", + (void*)data, datalen); + } + /* Is the input data complete? */ + if(got_data && stream->nread >= stream->inlen+2) { + if(!stream->in) { + verbose(VERB_ALGO, "doq: completed stream has " + "no buffer"); + return 0; + } + *recv_done = 1; + } + return 1; +} + +/** doq receive FIN for a stream. No more bytes are going to arrive. */ +static int +doq_stream_recv_fin(struct doq_conn* conn, struct doq_stream* stream, int + recv_done) +{ + if(!stream->is_query_complete && !recv_done) { + verbose(VERB_ALGO, "doq: stream recv FIN, but is " + "not complete, have %d of %d bytes", + ((int)stream->nread)-2, (int)stream->inlen); + if(!doq_stream_close(conn, stream, 1)) + return 0; + } + return 1; +} + +void doq_fill_rand(struct ub_randstate* rnd, uint8_t* buf, size_t len) +{ + size_t i; + for(i=0; idoq_socket->rnd, data, datalen); + if(!doq_conid_find(conn->table, data, datalen)) { + /* Found an unused connection id. */ + return 1; + } + } + verbose(VERB_ALGO, "doq_conn_generate_new_conid failed: could not " + "generate random unused connection id value in %d attempts.", + max_try); + return 0; +} + +/** ngtcp2 rand callback function */ +static void +doq_rand_cb(uint8_t* dest, size_t destlen, const ngtcp2_rand_ctx* rand_ctx) +{ + struct ub_randstate* rnd = (struct ub_randstate*) + rand_ctx->native_handle; + doq_fill_rand(rnd, dest, destlen); +} + +/** ngtcp2 get_new_connection_id callback function */ +static int +doq_get_new_connection_id_cb(ngtcp2_conn* ATTR_UNUSED(conn), ngtcp2_cid* cid, + uint8_t* token, size_t cidlen, void* user_data) +{ + struct doq_conn* doq_conn = (struct doq_conn*)user_data; + /* Lock the conid tree, so we can check for duplicates while + * generating the id, and then insert it, whilst keeping the tree + * locked against other modifications, guaranteeing uniqueness. */ + lock_rw_wrlock(&doq_conn->table->conid_lock); + if(!doq_conn_generate_new_conid(doq_conn, cid->data, cidlen)) { + lock_rw_unlock(&doq_conn->table->conid_lock); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + cid->datalen = cidlen; + if(ngtcp2_crypto_generate_stateless_reset_token(token, + doq_conn->doq_socket->static_secret, + doq_conn->doq_socket->static_secret_len, cid) != 0) { + lock_rw_unlock(&doq_conn->table->conid_lock); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + if(!doq_conn_associate_conid(doq_conn, cid->data, cid->datalen)) { + lock_rw_unlock(&doq_conn->table->conid_lock); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + lock_rw_unlock(&doq_conn->table->conid_lock); + return 0; +} + +/** ngtcp2 remove_connection_id callback function */ +static int +doq_remove_connection_id_cb(ngtcp2_conn* ATTR_UNUSED(conn), + const ngtcp2_cid* cid, void* user_data) +{ + struct doq_conn* doq_conn = (struct doq_conn*)user_data; + lock_rw_wrlock(&doq_conn->table->conid_lock); + doq_conn_dissociate_conid(doq_conn, cid->data, cid->datalen); + lock_rw_unlock(&doq_conn->table->conid_lock); + return 0; +} + +/** doq submit a new token */ +static int +doq_submit_new_token(struct doq_conn* conn) +{ + uint8_t token[NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN]; + ngtcp2_ssize tokenlen; + int ret; + const ngtcp2_path* path = ngtcp2_conn_get_path(conn->conn); + ngtcp2_tstamp ts = doq_get_timestamp_nanosec(); + + tokenlen = ngtcp2_crypto_generate_regular_token(token, + conn->doq_socket->static_secret, + conn->doq_socket->static_secret_len, path->remote.addr, + path->remote.addrlen, ts); + if(tokenlen < 0) { + log_err("doq ngtcp2_crypto_generate_regular_token failed"); + return 1; + } + + verbose(VERB_ALGO, "doq submit new token"); + ret = ngtcp2_conn_submit_new_token(conn->conn, token, tokenlen); + if(ret != 0) { + log_err("doq ngtcp2_conn_submit_new_token failed: %s", + ngtcp2_strerror(ret)); + return 0; + } + return 1; +} + +/** ngtcp2 handshake_completed callback function */ +static int +doq_handshake_completed_cb(ngtcp2_conn* ATTR_UNUSED(conn), void* user_data) +{ + struct doq_conn* doq_conn = (struct doq_conn*)user_data; + verbose(VERB_ALGO, "doq handshake_completed callback"); + verbose(VERB_ALGO, "ngtcp2_conn_get_max_data_left is %d", + (int)ngtcp2_conn_get_max_data_left(doq_conn->conn)); +#ifdef HAVE_NGTCP2_CONN_GET_MAX_LOCAL_STREAMS_UNI + verbose(VERB_ALGO, "ngtcp2_conn_get_max_local_streams_uni is %d", + (int)ngtcp2_conn_get_max_local_streams_uni(doq_conn->conn)); +#endif + verbose(VERB_ALGO, "ngtcp2_conn_get_streams_uni_left is %d", + (int)ngtcp2_conn_get_streams_uni_left(doq_conn->conn)); + verbose(VERB_ALGO, "ngtcp2_conn_get_streams_bidi_left is %d", + (int)ngtcp2_conn_get_streams_bidi_left(doq_conn->conn)); + verbose(VERB_ALGO, "negotiated cipher name is %s", + SSL_get_cipher_name(doq_conn->ssl)); + if(verbosity > VERB_ALGO) { + const unsigned char* alpn = NULL; + unsigned int alpnlen = 0; + char alpnstr[128]; + SSL_get0_alpn_selected(doq_conn->ssl, &alpn, &alpnlen); + if(alpnlen > sizeof(alpnstr)-1) + alpnlen = sizeof(alpnstr)-1; + memmove(alpnstr, alpn, alpnlen); + alpnstr[alpnlen]=0; + verbose(VERB_ALGO, "negotiated ALPN is '%s'", alpnstr); + } + + if(!doq_submit_new_token(doq_conn)) + return -1; + return 0; +} + +/** ngtcp2 stream_open callback function */ +static int +doq_stream_open_cb(ngtcp2_conn* ATTR_UNUSED(conn), int64_t stream_id, + void* user_data) +{ + struct doq_conn* doq_conn = (struct doq_conn*)user_data; + struct doq_stream* stream; + verbose(VERB_ALGO, "doq new stream %x", (int)stream_id); + if(doq_stream_find(doq_conn, stream_id)) { + verbose(VERB_ALGO, "doq: stream with this id already exists"); + return 0; + } + if(stream_id != 0 && stream_id != 4 && /* allow one stream on a new connection */ + !doq_table_quic_size_available(doq_conn->doq_socket->table, + doq_conn->doq_socket->cfg, sizeof(*stream) + + 100 /* estimated query in */ + + 512 /* estimated response out */ + )) { + int rv; + verbose(VERB_ALGO, "doq: no mem for new stream"); + rv = ngtcp2_conn_shutdown_stream(doq_conn->conn, +#ifdef HAVE_NGTCP2_CONN_SHUTDOWN_STREAM4 + 0, +#endif + stream_id, NGTCP2_CONNECTION_REFUSED); + if(rv != 0) { + log_err("ngtcp2_conn_shutdown_stream failed: %s", + ngtcp2_strerror(rv)); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; + } + stream = doq_stream_create(stream_id); + if(!stream) { + log_err("doq: could not doq_stream_create: out of memory"); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + doq_table_quic_size_add(doq_conn->doq_socket->table, sizeof(*stream)); + doq_conn_add_stream(doq_conn, stream); + return 0; +} + +/** ngtcp2 recv_stream_data callback function */ +static int +doq_recv_stream_data_cb(ngtcp2_conn* ATTR_UNUSED(conn), uint32_t flags, + int64_t stream_id, uint64_t offset, const uint8_t* data, + size_t datalen, void* user_data, void* ATTR_UNUSED(stream_user_data)) +{ + int recv_done = 0; + struct doq_conn* doq_conn = (struct doq_conn*)user_data; + struct doq_stream* stream; + verbose(VERB_ALGO, "doq recv stream data stream id %d offset %d " + "datalen %d%s%s", (int)stream_id, (int)offset, (int)datalen, + ((flags&NGTCP2_STREAM_DATA_FLAG_FIN)!=0?" FIN":""), +#ifdef NGTCP2_STREAM_DATA_FLAG_0RTT + ((flags&NGTCP2_STREAM_DATA_FLAG_0RTT)!=0?" 0RTT":"") +#else + ((flags&NGTCP2_STREAM_DATA_FLAG_EARLY)!=0?" EARLY":"") +#endif + ); + stream = doq_stream_find(doq_conn, stream_id); + if(!stream) { + verbose(VERB_ALGO, "doq: received stream data for " + "unknown stream %d", (int)stream_id); + return 0; + } + if(stream->is_closed) { + verbose(VERB_ALGO, "doq: stream is closed, ignore recv data"); + return 0; + } + if(datalen != 0) { + if(!doq_stream_recv_data(stream, data, datalen, &recv_done, + doq_conn->doq_socket->table)) + return NGTCP2_ERR_CALLBACK_FAILURE; + } + if((flags&NGTCP2_STREAM_DATA_FLAG_FIN)!=0) { + if(!doq_stream_recv_fin(doq_conn, stream, recv_done)) + return NGTCP2_ERR_CALLBACK_FAILURE; + } + ngtcp2_conn_extend_max_stream_offset(doq_conn->conn, stream_id, + datalen); + ngtcp2_conn_extend_max_offset(doq_conn->conn, datalen); + if(recv_done) { + if(!doq_stream_data_complete(doq_conn, stream)) + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +/** ngtcp2 stream_close callback function */ +static int +doq_stream_close_cb(ngtcp2_conn* ATTR_UNUSED(conn), uint32_t flags, + int64_t stream_id, uint64_t app_error_code, void* user_data, + void* ATTR_UNUSED(stream_user_data)) +{ + struct doq_conn* doq_conn = (struct doq_conn*)user_data; + struct doq_stream* stream; + if((flags&NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)!=0) + verbose(VERB_ALGO, "doq stream close for stream id %d %sapp_error_code %d", + (int)stream_id, + (((flags&NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)!=0)? + "APP_ERROR_CODE_SET ":""), + (int)app_error_code); + else + verbose(VERB_ALGO, "doq stream close for stream id %d", + (int)stream_id); + + stream = doq_stream_find(doq_conn, stream_id); + if(!stream) { + verbose(VERB_ALGO, "doq: stream close for " + "unknown stream %d", (int)stream_id); + return 0; + } + if(!doq_stream_close(doq_conn, stream, 0)) + return NGTCP2_ERR_CALLBACK_FAILURE; + return 0; +} + +/** ngtcp2 stream_reset callback function */ +static int +doq_stream_reset_cb(ngtcp2_conn* ATTR_UNUSED(conn), int64_t stream_id, + uint64_t final_size, uint64_t app_error_code, void* user_data, + void* ATTR_UNUSED(stream_user_data)) +{ + struct doq_conn* doq_conn = (struct doq_conn*)user_data; + struct doq_stream* stream; + verbose(VERB_ALGO, "doq stream reset for stream id %d final_size %d " + "app_error_code %d", (int)stream_id, (int)final_size, + (int)app_error_code); + + stream = doq_stream_find(doq_conn, stream_id); + if(!stream) { + verbose(VERB_ALGO, "doq: stream reset for " + "unknown stream %d", (int)stream_id); + return 0; + } + if(!doq_stream_close(doq_conn, stream, 0)) + return NGTCP2_ERR_CALLBACK_FAILURE; + return 0; +} + +/** ngtcp2 acked_stream_data_offset callback function */ +static int +doq_acked_stream_data_offset_cb(ngtcp2_conn* ATTR_UNUSED(conn), + int64_t stream_id, uint64_t offset, uint64_t datalen, void* user_data, + void* ATTR_UNUSED(stream_user_data)) +{ + struct doq_conn* doq_conn = (struct doq_conn*)user_data; + struct doq_stream* stream; + verbose(VERB_ALGO, "doq stream acked data for stream id %d offset %d " + "datalen %d", (int)stream_id, (int)offset, (int)datalen); + + stream = doq_stream_find(doq_conn, stream_id); + if(!stream) { + verbose(VERB_ALGO, "doq: stream acked data for " + "unknown stream %d", (int)stream_id); + return 0; + } + /* Acked the data from [offset .. offset+datalen). */ + if(stream->is_closed) + return 0; + if(offset+datalen >= stream->outlen) { + doq_stream_remove_in_buffer(stream, + doq_conn->doq_socket->table); + doq_stream_remove_out_buffer(stream, + doq_conn->doq_socket->table); + } + return 0; +} + +/** ngtc2p log_printf callback function */ +static void +doq_log_printf_cb(void* ATTR_UNUSED(user_data), const char* fmt, ...) +{ + char buf[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + verbose(VERB_ALGO, "libngtcp2: %s", buf); + va_end(ap); +} + +#ifndef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT +/** the doq application tx key callback, false on failure */ +static int +doq_application_tx_key_cb(struct doq_conn* conn) +{ + verbose(VERB_ALGO, "doq application tx key cb"); + /* The server does not want to open streams to the client, + * the client instead initiates by opening bidi streams. */ + verbose(VERB_ALGO, "doq ngtcp2_conn_get_max_data_left is %d", + (int)ngtcp2_conn_get_max_data_left(conn->conn)); +#ifdef HAVE_NGTCP2_CONN_GET_MAX_LOCAL_STREAMS_UNI + verbose(VERB_ALGO, "doq ngtcp2_conn_get_max_local_streams_uni is %d", + (int)ngtcp2_conn_get_max_local_streams_uni(conn->conn)); +#endif + verbose(VERB_ALGO, "doq ngtcp2_conn_get_streams_uni_left is %d", + (int)ngtcp2_conn_get_streams_uni_left(conn->conn)); + verbose(VERB_ALGO, "doq ngtcp2_conn_get_streams_bidi_left is %d", + (int)ngtcp2_conn_get_streams_bidi_left(conn->conn)); + return 1; +} + +/** quic_method set_encryption_secrets function */ +static int +doq_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t *read_secret, const uint8_t *write_secret, + size_t secret_len) +{ + struct doq_conn* doq_conn = (struct doq_conn*)SSL_get_app_data(ssl); +#ifdef HAVE_NGTCP2_ENCRYPTION_LEVEL + ngtcp2_encryption_level +#else + ngtcp2_crypto_level +#endif + level = +#ifdef HAVE_NGTCP2_CRYPTO_QUICTLS_FROM_OSSL_ENCRYPTION_LEVEL + ngtcp2_crypto_quictls_from_ossl_encryption_level(ossl_level); +#else + ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level); +#endif + + if(read_secret) { + verbose(VERB_ALGO, "doq: ngtcp2_crypto_derive_and_install_rx_key for level %d ossl %d", (int)level, (int)ossl_level); + if(ngtcp2_crypto_derive_and_install_rx_key(doq_conn->conn, + NULL, NULL, NULL, level, read_secret, secret_len) + != 0) { + log_err("ngtcp2_crypto_derive_and_install_rx_key " + "failed"); + return 0; + } + } + + if(write_secret) { + verbose(VERB_ALGO, "doq: ngtcp2_crypto_derive_and_install_tx_key for level %d ossl %d", (int)level, (int)ossl_level); + if(ngtcp2_crypto_derive_and_install_tx_key(doq_conn->conn, + NULL, NULL, NULL, level, write_secret, secret_len) + != 0) { + log_err("ngtcp2_crypto_derive_and_install_tx_key " + "failed"); + return 0; + } + if(level == NGTCP2_CRYPTO_LEVEL_APPLICATION) { + if(!doq_application_tx_key_cb(doq_conn)) + return 0; + } + } + return 1; +} + +/** quic_method add_handshake_data function */ +static int +doq_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t *data, size_t len) +{ + struct doq_conn* doq_conn = (struct doq_conn*)SSL_get_app_data(ssl); +#ifdef HAVE_NGTCP2_ENCRYPTION_LEVEL + ngtcp2_encryption_level +#else + ngtcp2_crypto_level +#endif + level = +#ifdef HAVE_NGTCP2_CRYPTO_QUICTLS_FROM_OSSL_ENCRYPTION_LEVEL + ngtcp2_crypto_quictls_from_ossl_encryption_level(ossl_level); +#else + ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level); +#endif + int rv; + + verbose(VERB_ALGO, "doq_add_handshake_data: " + "ngtcp2_con_submit_crypto_data level %d", (int)level); + rv = ngtcp2_conn_submit_crypto_data(doq_conn->conn, level, data, len); + if(rv != 0) { + log_err("ngtcp2_conn_submit_crypto_data failed: %s", + ngtcp2_strerror(rv)); + ngtcp2_conn_set_tls_error(doq_conn->conn, rv); + return 0; + } + return 1; +} + +/** quic_method flush_flight function */ +static int +doq_flush_flight(SSL* ATTR_UNUSED(ssl)) +{ + return 1; +} + +/** quic_method send_alert function */ +static int +doq_send_alert(SSL *ssl, enum ssl_encryption_level_t ATTR_UNUSED(level), + uint8_t alert) +{ + struct doq_conn* doq_conn = (struct doq_conn*)SSL_get_app_data(ssl); + doq_conn->tls_alert = alert; + return 1; +} +#endif /* HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT */ + +/** ALPN select callback for the doq SSL context */ +static int +doq_alpn_select_cb(SSL* ATTR_UNUSED(ssl), const unsigned char** out, + unsigned char* outlen, const unsigned char* in, unsigned int inlen, + void* ATTR_UNUSED(arg)) +{ + /* select "doq" */ + int ret = SSL_select_next_proto((void*)out, outlen, + (const unsigned char*)"\x03""doq", 4, in, inlen); + if(ret == OPENSSL_NPN_NEGOTIATED) + return SSL_TLSEXT_ERR_OK; + verbose(VERB_ALGO, "doq alpn_select_cb: ALPN from client does " + "not have 'doq'"); + return SSL_TLSEXT_ERR_ALERT_FATAL; +} + +/** create new tls session for server doq connection */ +static SSL_CTX* +doq_ctx_server_setup(struct doq_server_socket* doq_socket) +{ + char* sid_ctx = "unbound server"; +#ifndef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT + SSL_QUIC_METHOD* quic_method; +#endif + SSL_CTX* ctx = SSL_CTX_new(TLS_server_method()); + if(!ctx) { + log_crypto_err("Could not SSL_CTX_new"); + return NULL; + } + SSL_CTX_set_options(ctx, + (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | + SSL_OP_SINGLE_ECDH_USE | + SSL_OP_CIPHER_SERVER_PREFERENCE | + SSL_OP_NO_ANTI_REPLAY); + SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS); + SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); +#ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB + SSL_CTX_set_alpn_select_cb(ctx, doq_alpn_select_cb, NULL); +#endif + SSL_CTX_set_default_verify_paths(ctx); + if(!SSL_CTX_use_certificate_chain_file(ctx, + doq_socket->ssl_service_pem)) { + log_err("doq: error for cert file: %s", + doq_socket->ssl_service_pem); + log_crypto_err("doq: error in " + "SSL_CTX_use_certificate_chain_file"); + SSL_CTX_free(ctx); + return NULL; + } + if(!SSL_CTX_use_PrivateKey_file(ctx, doq_socket->ssl_service_key, + SSL_FILETYPE_PEM)) { + log_err("doq: error for private key file: %s", + doq_socket->ssl_service_key); + log_crypto_err("doq: error in SSL_CTX_use_PrivateKey_file"); + SSL_CTX_free(ctx); + return NULL; + } + if(!SSL_CTX_check_private_key(ctx)) { + log_err("doq: error for key file: %s", + doq_socket->ssl_service_key); + log_crypto_err("doq: error in SSL_CTX_check_private_key"); + SSL_CTX_free(ctx); + return NULL; + } + SSL_CTX_set_session_id_context(ctx, (void*)sid_ctx, strlen(sid_ctx)); + if(doq_socket->ssl_verify_pem && doq_socket->ssl_verify_pem[0]) { + if(!SSL_CTX_load_verify_locations(ctx, + doq_socket->ssl_verify_pem, NULL)) { + log_err("doq: error for verify pem file: %s", + doq_socket->ssl_verify_pem); + log_crypto_err("doq: error in " + "SSL_CTX_load_verify_locations"); + SSL_CTX_free(ctx); + return NULL; + } + SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file( + doq_socket->ssl_verify_pem)); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER| + SSL_VERIFY_CLIENT_ONCE| + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + } + + SSL_CTX_set_max_early_data(ctx, 0xffffffff); +#ifdef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT + if(ngtcp2_crypto_quictls_configure_server_context(ctx) != 0) { + log_err("ngtcp2_crypto_quictls_configure_server_context failed"); + SSL_CTX_free(ctx); + return NULL; + } +#else + /* The quic_method needs to remain valid during the SSL_CTX + * lifetime, so we allocate it. It is freed with the + * doq_server_socket. */ + quic_method = calloc(1, sizeof(SSL_QUIC_METHOD)); + if(!quic_method) { + log_err("calloc failed: out of memory"); + SSL_CTX_free(ctx); + return NULL; + } + doq_socket->quic_method = quic_method; + quic_method->set_encryption_secrets = doq_set_encryption_secrets; + quic_method->add_handshake_data = doq_add_handshake_data; + quic_method->flush_flight = doq_flush_flight; + quic_method->send_alert = doq_send_alert; + SSL_CTX_set_quic_method(ctx, doq_socket->quic_method); +#endif + return ctx; +} + +/** Get the ngtcp2_conn from ssl userdata of type ngtcp2_conn_ref */ +static ngtcp2_conn* doq_conn_ref_get_conn(ngtcp2_crypto_conn_ref* conn_ref) +{ + struct doq_conn* conn = (struct doq_conn*)conn_ref->user_data; + return conn->conn; +} + +/** create new SSL session for server connection */ +static SSL* +doq_ssl_server_setup(SSL_CTX* ctx, struct doq_conn* conn) +{ + SSL* ssl = SSL_new(ctx); + if(!ssl) { + log_crypto_err("doq: SSL_new failed"); + return NULL; + } +#ifdef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT + conn->conn_ref.get_conn = &doq_conn_ref_get_conn; + conn->conn_ref.user_data = conn; + SSL_set_app_data(ssl, &conn->conn_ref); +#else + SSL_set_app_data(ssl, conn); +#endif + SSL_set_accept_state(ssl); + SSL_set_quic_early_data_enabled(ssl, 1); + return ssl; +} + +/** setup the doq_socket server tls context */ +int +doq_socket_setup_ctx(struct doq_server_socket* doq_socket) +{ + doq_socket->ctx = doq_ctx_server_setup(doq_socket); + if(!doq_socket->ctx) + return 0; + return 1; +} + +int +doq_conn_setup(struct doq_conn* conn, uint8_t* scid, size_t scidlen, + uint8_t* ocid, size_t ocidlen, const uint8_t* token, size_t tokenlen) +{ + int rv; + struct ngtcp2_cid dcid, sv_scid, scid_cid; + struct ngtcp2_path path; + struct ngtcp2_callbacks callbacks; + struct ngtcp2_settings settings; + struct ngtcp2_transport_params params; + memset(&dcid, 0, sizeof(dcid)); + memset(&sv_scid, 0, sizeof(sv_scid)); + memset(&scid_cid, 0, sizeof(scid_cid)); + memset(&path, 0, sizeof(path)); + memset(&callbacks, 0, sizeof(callbacks)); + memset(&settings, 0, sizeof(settings)); + memset(¶ms, 0, sizeof(params)); + + ngtcp2_cid_init(&scid_cid, scid, scidlen); + ngtcp2_cid_init(&dcid, conn->key.dcid, conn->key.dcidlen); + + path.remote.addr = (struct sockaddr*)&conn->key.paddr.addr; + path.remote.addrlen = conn->key.paddr.addrlen; + path.local.addr = (struct sockaddr*)&conn->key.paddr.localaddr; + path.local.addrlen = conn->key.paddr.localaddrlen; + + callbacks.recv_client_initial = ngtcp2_crypto_recv_client_initial_cb; + callbacks.recv_crypto_data = ngtcp2_crypto_recv_crypto_data_cb; + callbacks.encrypt = ngtcp2_crypto_encrypt_cb; + callbacks.decrypt = ngtcp2_crypto_decrypt_cb; + callbacks.hp_mask = ngtcp2_crypto_hp_mask; + callbacks.update_key = ngtcp2_crypto_update_key_cb; + callbacks.delete_crypto_aead_ctx = + ngtcp2_crypto_delete_crypto_aead_ctx_cb; + callbacks.delete_crypto_cipher_ctx = + ngtcp2_crypto_delete_crypto_cipher_ctx_cb; + callbacks.get_path_challenge_data = + ngtcp2_crypto_get_path_challenge_data_cb; + callbacks.version_negotiation = ngtcp2_crypto_version_negotiation_cb; + callbacks.rand = doq_rand_cb; + callbacks.get_new_connection_id = doq_get_new_connection_id_cb; + callbacks.remove_connection_id = doq_remove_connection_id_cb; + callbacks.handshake_completed = doq_handshake_completed_cb; + callbacks.stream_open = doq_stream_open_cb; + callbacks.stream_close = doq_stream_close_cb; + callbacks.stream_reset = doq_stream_reset_cb; + callbacks.acked_stream_data_offset = doq_acked_stream_data_offset_cb; + callbacks.recv_stream_data = doq_recv_stream_data_cb; + + ngtcp2_settings_default(&settings); + if(verbosity >= VERB_ALGO) { + settings.log_printf = doq_log_printf_cb; + } + settings.rand_ctx.native_handle = conn->doq_socket->rnd; + settings.initial_ts = doq_get_timestamp_nanosec(); + settings.max_stream_window = 6*1024*1024; + settings.max_window = 6*1024*1024; +#ifdef HAVE_STRUCT_NGTCP2_SETTINGS_TOKENLEN + settings.token = (void*)token; + settings.tokenlen = tokenlen; +#else + settings.token.base = (void*)token; + settings.token.len = tokenlen; +#endif + + ngtcp2_transport_params_default(¶ms); + params.max_idle_timeout = conn->doq_socket->idle_timeout; + params.active_connection_id_limit = 7; + params.initial_max_stream_data_bidi_local = 256*1024; + params.initial_max_stream_data_bidi_remote = 256*1024; + params.initial_max_data = 1024*1024; + /* DoQ uses bidi streams, so we allow 0 uni streams. */ + params.initial_max_streams_uni = 0; + /* Initial max on number of bidi streams the remote end can open. + * That is the number of queries it can make, at first. */ + params.initial_max_streams_bidi = 10; + if(ocid) { + ngtcp2_cid_init(¶ms.original_dcid, ocid, ocidlen); + ngtcp2_cid_init(¶ms.retry_scid, conn->key.dcid, + conn->key.dcidlen); + params.retry_scid_present = 1; + } else { + ngtcp2_cid_init(¶ms.original_dcid, conn->key.dcid, + conn->key.dcidlen); + } +#ifdef HAVE_STRUCT_NGTCP2_TRANSPORT_PARAMS_ORIGINAL_DCID_PRESENT + params.original_dcid_present = 1; +#endif + doq_fill_rand(conn->doq_socket->rnd, params.stateless_reset_token, + sizeof(params.stateless_reset_token)); + sv_scid.datalen = conn->doq_socket->sv_scidlen; + lock_rw_wrlock(&conn->table->conid_lock); + if(!doq_conn_generate_new_conid(conn, sv_scid.data, sv_scid.datalen)) { + lock_rw_unlock(&conn->table->conid_lock); + return 0; + } + + rv = ngtcp2_conn_server_new(&conn->conn, &scid_cid, &sv_scid, &path, + conn->version, &callbacks, &settings, ¶ms, NULL, conn); + if(rv != 0) { + lock_rw_unlock(&conn->table->conid_lock); + log_err("ngtcp2_conn_server_new failed: %s", + ngtcp2_strerror(rv)); + return 0; + } + if(!doq_conn_setup_conids(conn)) { + lock_rw_unlock(&conn->table->conid_lock); + log_err("doq_conn_setup_conids failed: out of memory"); + return 0; + } + lock_rw_unlock(&conn->table->conid_lock); + conn->ssl = doq_ssl_server_setup((SSL_CTX*)conn->doq_socket->ctx, + conn); + if(!conn->ssl) { + log_err("doq_ssl_server_setup failed"); + return 0; + } + ngtcp2_conn_set_tls_native_handle(conn->conn, conn->ssl); + doq_conn_write_enable(conn); + return 1; +} + +struct doq_conid* +doq_conid_find(struct doq_table* table, const uint8_t* data, size_t datalen) +{ + struct rbnode_type* node; + struct doq_conid key; + key.node.key = &key; + key.cid = (void*)data; + key.cidlen = datalen; + node = rbtree_search(table->conid_tree, &key); + if(node) + return (struct doq_conid*)node->key; + return NULL; +} + +/** insert conid in the conid list */ +static void +doq_conid_list_insert(struct doq_conn* conn, struct doq_conid* conid) +{ + conid->prev = NULL; + conid->next = conn->conid_list; + if(conn->conid_list) + conn->conid_list->prev = conid; + conn->conid_list = conid; +} + +/** remove conid from the conid list */ +static void +doq_conid_list_remove(struct doq_conn* conn, struct doq_conid* conid) +{ + if(conid->prev) + conid->prev->next = conid->next; + else conn->conid_list = conid->next; + if(conid->next) + conid->next->prev = conid->prev; +} + +/** create a doq_conid */ +static struct doq_conid* +doq_conid_create(uint8_t* data, size_t datalen, struct doq_conn_key* key) +{ + struct doq_conid* conid; + conid = calloc(1, sizeof(*conid)); + if(!conid) + return NULL; + conid->cid = memdup(data, datalen); + if(!conid->cid) { + free(conid); + return NULL; + } + conid->cidlen = datalen; + conid->node.key = conid; + conid->key = *key; + conid->key.dcid = memdup(key->dcid, key->dcidlen); + if(!conid->key.dcid) { + free(conid->cid); + free(conid); + return NULL; + } + return conid; +} + +void +doq_conid_delete(struct doq_conid* conid) +{ + if(!conid) + return; + free(conid->key.dcid); + free(conid->cid); + free(conid); +} + +/** return true if the conid is for the conn. */ +static int +conid_is_for_conn(struct doq_conn* conn, struct doq_conid* conid) +{ + if(conid->key.dcidlen == conn->key.dcidlen && + memcmp(conid->key.dcid, conn->key.dcid, conid->key.dcidlen)==0 + && conid->key.paddr.addrlen == conn->key.paddr.addrlen && + memcmp(&conid->key.paddr.addr, &conn->key.paddr.addr, + conid->key.paddr.addrlen) == 0 && + conid->key.paddr.localaddrlen == conn->key.paddr.localaddrlen && + memcmp(&conid->key.paddr.localaddr, &conn->key.paddr.localaddr, + conid->key.paddr.localaddrlen) == 0 && + conid->key.paddr.ifindex == conn->key.paddr.ifindex) + return 1; + return 0; +} + +int +doq_conn_associate_conid(struct doq_conn* conn, uint8_t* data, size_t datalen) +{ + struct doq_conid* conid; + conid = doq_conid_find(conn->table, data, datalen); + if(conid && !conid_is_for_conn(conn, conid)) { + verbose(VERB_ALGO, "doq connection id already exists for " + "another doq_conn. Ignoring second connection id."); + /* Already exists to another conn, ignore it. + * This works, in that the conid is listed in the doq_conn + * conid_list element, and removed from there. So our conid + * tree and list are fine, when created and removed. + * The tree now does not have the lookup element pointing + * to this connection. */ + return 1; + } + if(conid) + return 1; /* already inserted */ + conid = doq_conid_create(data, datalen, &conn->key); + if(!conid) + return 0; + doq_conid_list_insert(conn, conid); + (void)rbtree_insert(conn->table->conid_tree, &conid->node); + return 1; +} + +void +doq_conn_dissociate_conid(struct doq_conn* conn, const uint8_t* data, + size_t datalen) +{ + struct doq_conid* conid; + conid = doq_conid_find(conn->table, data, datalen); + if(conid && !conid_is_for_conn(conn, conid)) + return; + if(conid) { + (void)rbtree_delete(conn->table->conid_tree, + conid->node.key); + doq_conid_list_remove(conn, conid); + doq_conid_delete(conid); + } +} + +/** associate the scid array and also the dcid. + * caller must hold the locks on conn and doq_table.conid_lock. */ +static int +doq_conn_setup_id_array_and_dcid(struct doq_conn* conn, + struct ngtcp2_cid* scids, size_t num_scid) +{ + size_t i; + for(i=0; ikey.dcid, conn->key.dcidlen)) + return 0; + return 1; +} + +int +doq_conn_setup_conids(struct doq_conn* conn) +{ + size_t num_scid = +#ifndef HAVE_NGTCP2_CONN_GET_NUM_SCID + ngtcp2_conn_get_scid(conn->conn, NULL); +#else + ngtcp2_conn_get_num_scid(conn->conn); +#endif + if(num_scid <= 4) { + struct ngtcp2_cid ids[4]; + /* Usually there are not that many scids when just accepted, + * like only 2. */ + ngtcp2_conn_get_scid(conn->conn, ids); + return doq_conn_setup_id_array_and_dcid(conn, ids, num_scid); + } else { + struct ngtcp2_cid *scids = calloc(num_scid, + sizeof(struct ngtcp2_cid)); + if(!scids) + return 0; + ngtcp2_conn_get_scid(conn->conn, scids); + if(!doq_conn_setup_id_array_and_dcid(conn, scids, num_scid)) { + free(scids); + return 0; + } + free(scids); + } + return 1; +} + +void +doq_conn_clear_conids(struct doq_conn* conn) +{ + struct doq_conid* p, *next; + if(!conn) + return; + p = conn->conid_list; + while(p) { + next = p->next; + (void)rbtree_delete(conn->table->conid_tree, p->node.key); + doq_conid_delete(p); + p = next; + } + conn->conid_list = NULL; +} + +ngtcp2_tstamp doq_get_timestamp_nanosec(void) +{ +#ifdef CLOCK_REALTIME + struct timespec tp; + memset(&tp, 0, sizeof(tp)); + /* Get a nanosecond time, that can be compared with the event base. */ + if(clock_gettime(CLOCK_REALTIME, &tp) == -1) { + log_err("clock_gettime failed: %s", strerror(errno)); + } + return ((uint64_t)tp.tv_sec)*((uint64_t)1000000000) + + ((uint64_t)tp.tv_nsec); +#else + struct timeval tv; + if(gettimeofday(&tv, NULL) < 0) { + log_err("gettimeofday failed: %s", strerror(errno)); + } + return ((uint64_t)tv.tv_sec)*((uint64_t)1000000000) + + ((uint64_t)tv.tv_usec)*((uint64_t)1000); +#endif /* CLOCK_REALTIME */ +} + +/** doq start the closing period for the connection. */ +static int +doq_conn_start_closing_period(struct comm_point* c, struct doq_conn* conn) +{ + struct ngtcp2_path_storage ps; + struct ngtcp2_pkt_info pi; + ngtcp2_ssize ret; + if(!conn) + return 1; + if( +#ifdef HAVE_NGTCP2_CONN_IN_CLOSING_PERIOD + ngtcp2_conn_in_closing_period(conn->conn) +#else + ngtcp2_conn_is_in_closing_period(conn->conn) +#endif + ) + return 1; + if( +#ifdef HAVE_NGTCP2_CONN_IN_DRAINING_PERIOD + ngtcp2_conn_in_draining_period(conn->conn) +#else + ngtcp2_conn_is_in_draining_period(conn->conn) +#endif + ) { + doq_conn_write_disable(conn); + return 1; + } + ngtcp2_path_storage_zero(&ps); + sldns_buffer_clear(c->doq_socket->pkt_buf); + /* the call to ngtcp2_conn_write_connection_close causes the + * conn to be closed. It is now in the closing period. */ + ret = ngtcp2_conn_write_connection_close(conn->conn, &ps.path, + &pi, sldns_buffer_begin(c->doq_socket->pkt_buf), + sldns_buffer_remaining(c->doq_socket->pkt_buf), +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + &conn->ccerr +#else + &conn->last_error +#endif + , doq_get_timestamp_nanosec()); + if(ret < 0) { + log_err("doq ngtcp2_conn_write_connection_close failed: %s", + ngtcp2_strerror(ret)); + return 0; + } + if(ret == 0) { + return 0; + } + sldns_buffer_set_position(c->doq_socket->pkt_buf, ret); + sldns_buffer_flip(c->doq_socket->pkt_buf); + + /* The close packet is allocated, because it may have to be repeated. + * When incoming packets have this connection dcid. */ + conn->close_pkt = memdup(sldns_buffer_begin(c->doq_socket->pkt_buf), + sldns_buffer_limit(c->doq_socket->pkt_buf)); + if(!conn->close_pkt) { + log_err("doq: could not allocate close packet: out of memory"); + return 0; + } + conn->close_pkt_len = sldns_buffer_limit(c->doq_socket->pkt_buf); + conn->close_ecn = pi.ecn; + return 1; +} + +/** doq send the close packet for the connection, perhaps again. */ +int +doq_conn_send_close(struct comm_point* c, struct doq_conn* conn) +{ + if(!conn) + return 0; + if(!conn->close_pkt) + return 0; + if(conn->close_pkt_len > sldns_buffer_capacity(c->doq_socket->pkt_buf)) + return 0; + sldns_buffer_clear(c->doq_socket->pkt_buf); + sldns_buffer_write(c->doq_socket->pkt_buf, conn->close_pkt, conn->close_pkt_len); + sldns_buffer_flip(c->doq_socket->pkt_buf); + verbose(VERB_ALGO, "doq send connection close"); + doq_send_pkt(c, &conn->key.paddr, conn->close_ecn); + doq_conn_write_disable(conn); + return 1; +} + +/** doq close the connection on error. If it returns a failure, it + * does not wait to send a close, and the connection can be dropped. */ +static int +doq_conn_close_error(struct comm_point* c, struct doq_conn* conn) +{ +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + if(conn->ccerr.type == NGTCP2_CCERR_TYPE_IDLE_CLOSE) + return 0; +#else + if(conn->last_error.type == + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE) + return 0; +#endif + if(!doq_conn_start_closing_period(c, conn)) + return 0; + if( +#ifdef HAVE_NGTCP2_CONN_IN_DRAINING_PERIOD + ngtcp2_conn_in_draining_period(conn->conn) +#else + ngtcp2_conn_is_in_draining_period(conn->conn) +#endif + ) { + doq_conn_write_disable(conn); + return 1; + } + doq_conn_write_enable(conn); + if(!doq_conn_send_close(c, conn)) + return 0; + return 1; +} + +int +doq_conn_recv(struct comm_point* c, struct doq_pkt_addr* paddr, + struct doq_conn* conn, struct ngtcp2_pkt_info* pi, int* err_retry, + int* err_drop) +{ + int ret; + ngtcp2_tstamp ts; + struct ngtcp2_path path; + memset(&path, 0, sizeof(path)); + path.remote.addr = (struct sockaddr*)&paddr->addr; + path.remote.addrlen = paddr->addrlen; + path.local.addr = (struct sockaddr*)&paddr->localaddr; + path.local.addrlen = paddr->localaddrlen; + ts = doq_get_timestamp_nanosec(); + + ret = ngtcp2_conn_read_pkt(conn->conn, &path, pi, + sldns_buffer_begin(c->doq_socket->pkt_buf), + sldns_buffer_limit(c->doq_socket->pkt_buf), ts); + if(ret != 0) { + if(err_retry) + *err_retry = 0; + if(err_drop) + *err_drop = 0; + if(ret == NGTCP2_ERR_DRAINING) { + verbose(VERB_ALGO, "ngtcp2_conn_read_pkt returned %s", + ngtcp2_strerror(ret)); + doq_conn_write_disable(conn); + return 0; + } else if(ret == NGTCP2_ERR_DROP_CONN) { + verbose(VERB_ALGO, "ngtcp2_conn_read_pkt returned %s", + ngtcp2_strerror(ret)); + if(err_drop) + *err_drop = 1; + return 0; + } else if(ret == NGTCP2_ERR_RETRY) { + verbose(VERB_ALGO, "ngtcp2_conn_read_pkt returned %s", + ngtcp2_strerror(ret)); + if(err_retry) + *err_retry = 1; + if(err_drop) + *err_drop = 1; + return 0; + } else if(ret == NGTCP2_ERR_CRYPTO) { + if( +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + !conn->ccerr.error_code +#else + !conn->last_error.error_code +#endif + ) { + /* in picotls the tls alert may need to be + * copied, but this is with openssl. And there + * is conn->tls_alert. */ +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + ngtcp2_ccerr_set_tls_alert(&conn->ccerr, + conn->tls_alert, NULL, 0); +#else + ngtcp2_connection_close_error_set_transport_error_tls_alert( + &conn->last_error, conn->tls_alert, + NULL, 0); +#endif + } + } else { + if( +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + !conn->ccerr.error_code +#else + !conn->last_error.error_code +#endif + ) { +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + ngtcp2_ccerr_set_liberr(&conn->ccerr, ret, + NULL, 0); +#else + ngtcp2_connection_close_error_set_transport_error_liberr( + &conn->last_error, ret, NULL, 0); +#endif + } + } + log_err("ngtcp2_conn_read_pkt failed: %s", + ngtcp2_strerror(ret)); + if(!doq_conn_close_error(c, conn)) { + if(err_drop) + *err_drop = 1; + } + return 0; + } + doq_conn_write_enable(conn); + return 1; +} + +/** doq stream write is done */ +static void +doq_stream_write_is_done(struct doq_conn* conn, struct doq_stream* stream) +{ + /* Cannot deallocate, the buffer may be needed for resends. */ + doq_stream_off_write_list(conn, stream); +} + +int +doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn, + int* err_drop) +{ + struct doq_stream* stream = conn->stream_write_first; + ngtcp2_path_storage ps; + ngtcp2_tstamp ts = doq_get_timestamp_nanosec(); + size_t num_packets = 0, max_packets = 65535; + ngtcp2_path_storage_zero(&ps); + + for(;;) { + int64_t stream_id; + uint32_t flags = 0; + ngtcp2_pkt_info pi; + ngtcp2_vec datav[2]; + size_t datav_count = 0; + ngtcp2_ssize ret, ndatalen = 0; + int fin; + + if(stream) { + /* data to send */ + verbose(VERB_ALGO, "doq: doq_conn write stream %d", + (int)stream->stream_id); + stream_id = stream->stream_id; + fin = 1; + if(stream->nwrite < 2) { + datav[0].base = ((uint8_t*)&stream-> + outlen_wire) + stream->nwrite; + datav[0].len = 2 - stream->nwrite; + datav[1].base = stream->out; + datav[1].len = stream->outlen; + datav_count = 2; + } else { + datav[0].base = stream->out + + (stream->nwrite-2); + datav[0].len = stream->outlen - + (stream->nwrite-2); + datav_count = 1; + } + } else { + /* no data to send */ + verbose(VERB_ALGO, "doq: doq_conn write stream -1"); + stream_id = -1; + fin = 0; + datav[0].base = NULL; + datav[0].len = 0; + datav_count = 1; + } + + /* if more streams, set it to write more */ + if(stream && stream->write_next) + flags |= NGTCP2_WRITE_STREAM_FLAG_MORE; + if(fin) + flags |= NGTCP2_WRITE_STREAM_FLAG_FIN; + + sldns_buffer_clear(c->doq_socket->pkt_buf); + ret = ngtcp2_conn_writev_stream(conn->conn, &ps.path, &pi, + sldns_buffer_begin(c->doq_socket->pkt_buf), + sldns_buffer_remaining(c->doq_socket->pkt_buf), + &ndatalen, flags, stream_id, datav, datav_count, ts); + if(ret < 0) { + if(ret == NGTCP2_ERR_WRITE_MORE) { + verbose(VERB_ALGO, "doq: write more, ndatalen %d", (int)ndatalen); + if(stream) { + if(ndatalen >= 0) + stream->nwrite += ndatalen; + if(stream->nwrite >= stream->outlen+2) + doq_stream_write_is_done( + conn, stream); + stream = stream->write_next; + } + continue; + } else if(ret == NGTCP2_ERR_STREAM_DATA_BLOCKED) { + verbose(VERB_ALGO, "doq: ngtcp2_conn_writev_stream returned NGTCP2_ERR_STREAM_DATA_BLOCKED"); +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + ngtcp2_ccerr_set_application_error( + &conn->ccerr, -1, NULL, 0); +#else + ngtcp2_connection_close_error_set_application_error(&conn->last_error, -1, NULL, 0); +#endif + if(err_drop) + *err_drop = 0; + if(!doq_conn_close_error(c, conn)) { + if(err_drop) + *err_drop = 1; + } + return 0; + } else if(ret == NGTCP2_ERR_STREAM_SHUT_WR) { + verbose(VERB_ALGO, "doq: ngtcp2_conn_writev_stream returned NGTCP2_ERR_STREAM_SHUT_WR"); +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + ngtcp2_ccerr_set_application_error( + &conn->ccerr, -1, NULL, 0); +#else + ngtcp2_connection_close_error_set_application_error(&conn->last_error, -1, NULL, 0); +#endif + if(err_drop) + *err_drop = 0; + if(!doq_conn_close_error(c, conn)) { + if(err_drop) + *err_drop = 1; + } + return 0; + } + + log_err("doq: ngtcp2_conn_writev_stream failed: %s", + ngtcp2_strerror(ret)); +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + ngtcp2_ccerr_set_liberr(&conn->ccerr, ret, NULL, 0); +#else + ngtcp2_connection_close_error_set_transport_error_liberr( + &conn->last_error, ret, NULL, 0); +#endif + if(err_drop) + *err_drop = 0; + if(!doq_conn_close_error(c, conn)) { + if(err_drop) + *err_drop = 1; + } + return 0; + } + verbose(VERB_ALGO, "doq: writev_stream pkt size %d ndatawritten %d", + (int)ret, (int)ndatalen); + + if(ndatalen >= 0 && stream) { + stream->nwrite += ndatalen; + if(stream->nwrite >= stream->outlen+2) + doq_stream_write_is_done(conn, stream); + } + if(ret == 0) { + /* congestion limited */ + doq_conn_write_disable(conn); + ngtcp2_conn_update_pkt_tx_time(conn->conn, ts); + return 1; + } + sldns_buffer_set_position(c->doq_socket->pkt_buf, ret); + sldns_buffer_flip(c->doq_socket->pkt_buf); + doq_send_pkt(c, &conn->key.paddr, pi.ecn); + + if(c->doq_socket->have_blocked_pkt) + break; + if(++num_packets == max_packets) + break; + if(stream) + stream = stream->write_next; + } + ngtcp2_conn_update_pkt_tx_time(conn->conn, ts); + return 1; +} + +void +doq_conn_write_enable(struct doq_conn* conn) +{ + conn->write_interest = 1; +} + +void +doq_conn_write_disable(struct doq_conn* conn) +{ + conn->write_interest = 0; +} + +/** doq append the connection to the write list */ +static void +doq_conn_write_list_append(struct doq_table* table, struct doq_conn* conn) +{ + if(conn->on_write_list) + return; + conn->write_prev = table->write_list_last; + if(table->write_list_last) + table->write_list_last->write_next = conn; + else table->write_list_first = conn; + conn->write_next = NULL; + table->write_list_last = conn; + conn->on_write_list = 1; +} + +void +doq_conn_write_list_remove(struct doq_table* table, struct doq_conn* conn) +{ + if(!conn->on_write_list) + return; + if(conn->write_next) + conn->write_next->write_prev = conn->write_prev; + else table->write_list_last = conn->write_prev; + if(conn->write_prev) + conn->write_prev->write_next = conn->write_next; + else table->write_list_first = conn->write_next; + conn->write_prev = NULL; + conn->write_next = NULL; + conn->on_write_list = 0; +} + +void +doq_conn_set_write_list(struct doq_table* table, struct doq_conn* conn) +{ + if(conn->write_interest && conn->on_write_list) + return; + if(!conn->write_interest && !conn->on_write_list) + return; + if(conn->write_interest) + doq_conn_write_list_append(table, conn); + else doq_conn_write_list_remove(table, conn); +} + +struct doq_conn* +doq_table_pop_first(struct doq_table* table) +{ + struct doq_conn* conn = table->write_list_first; + if(!conn) + return NULL; + lock_basic_lock(&conn->lock); + table->write_list_first = conn->write_next; + if(conn->write_next) + conn->write_next->write_prev = NULL; + else table->write_list_last = NULL; + conn->write_next = NULL; + conn->write_prev = NULL; + conn->on_write_list = 0; + return conn; +} + +int +doq_conn_check_timer(struct doq_conn* conn, struct timeval* tv) +{ + ngtcp2_tstamp expiry = ngtcp2_conn_get_expiry(conn->conn); + ngtcp2_tstamp now = doq_get_timestamp_nanosec(); + ngtcp2_tstamp t; + + if(expiry <= now) { + /* The timer has already expired, add with zero timeout. + * This should call the callback straight away. Calling it + * from the event callbacks is cleaner than calling it here, + * because then it is always called with the same locks and + * so on. This routine only has the conn.lock. */ + t = now; + } else { + t = expiry; + } + + /* convert to timeval */ + memset(tv, 0, sizeof(*tv)); + tv->tv_sec = t / NGTCP2_SECONDS; + tv->tv_usec = (t / NGTCP2_MICROSECONDS)%1000000; + + /* If we already have a timer, is it the right value? */ + if(conn->timer.timer_in_tree || conn->timer.timer_in_list) { + if(conn->timer.time.tv_sec == tv->tv_sec && + conn->timer.time.tv_usec == tv->tv_usec) + return 0; + } + return 1; +} + +/* doq print connection log */ +static void +doq_conn_log_line(struct doq_conn* conn, char* s) +{ + char remotestr[256], localstr[256]; + addr_to_str((void*)&conn->key.paddr.addr, conn->key.paddr.addrlen, + remotestr, sizeof(remotestr)); + addr_to_str((void*)&conn->key.paddr.localaddr, + conn->key.paddr.localaddrlen, localstr, sizeof(localstr)); + log_info("doq conn %s %s %s", remotestr, localstr, s); +} + +int +doq_conn_handle_timeout(struct doq_conn* conn) +{ + ngtcp2_tstamp now = doq_get_timestamp_nanosec(); + int rv; + + if(verbosity >= VERB_ALGO) + doq_conn_log_line(conn, "timeout"); + + rv = ngtcp2_conn_handle_expiry(conn->conn, now); + if(rv != 0) { + verbose(VERB_ALGO, "ngtcp2_conn_handle_expiry failed: %s", + ngtcp2_strerror(rv)); +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + ngtcp2_ccerr_set_liberr(&conn->ccerr, rv, NULL, 0); +#else + ngtcp2_connection_close_error_set_transport_error_liberr( + &conn->last_error, rv, NULL, 0); +#endif + if(!doq_conn_close_error(conn->doq_socket->cp, conn)) { + /* failed, return for deletion */ + return 0; + } + return 1; + } + doq_conn_write_enable(conn); + if(!doq_conn_write_streams(conn->doq_socket->cp, conn, NULL)) { + /* failed, return for deletion. */ + return 0; + } + return 1; +} + +void +doq_table_quic_size_add(struct doq_table* table, size_t add) +{ + lock_basic_lock(&table->size_lock); + table->current_size += add; + lock_basic_unlock(&table->size_lock); +} + +void +doq_table_quic_size_subtract(struct doq_table* table, size_t subtract) +{ + lock_basic_lock(&table->size_lock); + if(table->current_size < subtract) + table->current_size = 0; + else table->current_size -= subtract; + lock_basic_unlock(&table->size_lock); +} + +int +doq_table_quic_size_available(struct doq_table* table, + struct config_file* cfg, size_t mem) +{ + size_t cur; + lock_basic_lock(&table->size_lock); + cur = table->current_size; + lock_basic_unlock(&table->size_lock); + + if(cur + mem > cfg->quic_size) + return 0; + return 1; +} + +size_t doq_table_quic_size_get(struct doq_table* table) +{ + size_t sz; + if(!table) + return 0; + lock_basic_lock(&table->size_lock); + sz = table->current_size; + lock_basic_unlock(&table->size_lock); + return sz; +} +#endif /* HAVE_NGTCP2 */ Index: services/listen_dnsport.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/listen_dnsport.h,v diff -u -p -r1.20 listen_dnsport.h --- services/listen_dnsport.h 13 Jun 2024 14:30:28 -0000 1.20 +++ services/listen_dnsport.h 7 Feb 2025 21:25:44 -0000 @@ -43,10 +43,16 @@ #define LISTEN_DNSPORT_H #include "util/netevent.h" +#include "util/rbtree.h" +#include "util/locks.h" #include "daemon/acl_list.h" #ifdef HAVE_NGHTTP2_NGHTTP2_H #include #endif +#ifdef HAVE_NGTCP2 +#include +#include +#endif struct listen_list; struct config_file; struct addrinfo; @@ -100,7 +106,9 @@ enum listen_type { /** udp ipv6 (v4mapped) for use with ancillary data + dnscrypt*/ listen_type_udpancil_dnscrypt, /** HTTP(2) over TLS over TCP */ - listen_type_http + listen_type_http, + /** DNS over QUIC */ + listen_type_doq }; /* @@ -188,6 +196,11 @@ int resolve_interface_names(char** ifs, * @param tcp_conn_limit: TCP connection limit info. * @param sslctx: nonNULL if ssl context. * @param dtenv: nonNULL if dnstap enabled. + * @param doq_table: the doq connection table, with shared information. + * @param rnd: random state. + * @param ssl_service_key: the SSL service key file. + * @param ssl_service_pem: the SSL service pem file. + * @param cfg: config file struct. * @param cb: callback function when a request arrives. It is passed * the packet and user argument. Return true to send a reply. * @param cb_arg: user data argument for callback function. @@ -198,8 +211,10 @@ listen_create(struct comm_base* base, st size_t bufsize, int tcp_accept_count, int tcp_idle_timeout, int harden_large_queries, uint32_t http_max_streams, char* http_endpoint, int http_notls, struct tcl_list* tcp_conn_limit, - void* sslctx, struct dt_env* dtenv, comm_point_callback_type* cb, - void *cb_arg); + void* sslctx, struct dt_env* dtenv, struct doq_table* doq_table, + struct ub_randstate* rnd, const char* ssl_service_key, + const char* ssl_service_pem, struct config_file* cfg, + comm_point_callback_type* cb, void *cb_arg); /** * delete the listening structure @@ -278,11 +293,12 @@ int create_udp_sock(int family, int sock * @param freebind: set IP_FREEBIND socket option. * @param use_systemd: if true, fetch sockets from systemd. * @param dscp: DSCP to use. + * @param additional: additional log information for the socket type. * @return: the socket. -1 on error. */ int create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto, int* reuseport, int transparent, int mss, int nodelay, int freebind, - int use_systemd, int dscp); + int use_systemd, int dscp, const char* additional); /** * Create and bind local listening socket @@ -452,6 +468,377 @@ int http2_submit_dns_response(struct htt int http2_submit_dns_response(void* v); #endif /* HAVE_NGHTTP2 */ +#ifdef HAVE_NGTCP2 +struct doq_conid; +struct doq_server_socket; + +/** + * DoQ shared connection table. This is the connections for the host. + * And some config parameter values for connections. The host has to + * respond on that ip,port for those connections, so they are shared + * between threads. + */ +struct doq_table { + /** the lock on the tree and config elements. insert and deletion, + * also lookup in the tree needs to hold the lock. */ + lock_rw_type lock; + /** rbtree of doq_conn, the connections to different destination + * addresses, and can be found by dcid. */ + struct rbtree_type* conn_tree; + /** lock for the conid tree, needed for the conid tree and also + * the conid elements */ + lock_rw_type conid_lock; + /** rbtree of doq_conid, connections can be found by their + * connection ids. Lookup by connection id, finds doq_conn. */ + struct rbtree_type* conid_tree; + /** the server scid length */ + int sv_scidlen; + /** the static secret for the server */ + uint8_t* static_secret; + /** length of the static secret */ + size_t static_secret_len; + /** the idle timeout in nanoseconds */ + uint64_t idle_timeout; + /** the list of write interested connections, hold the doq_table.lock + * to change them */ + struct doq_conn* write_list_first, *write_list_last; + /** rbtree of doq_timer. */ + struct rbtree_type* timer_tree; + /** lock on the current_size counter. */ + lock_basic_type size_lock; + /** current use, in bytes, of QUIC buffers. + * The doq_conn ngtcp2_conn structure, SSL structure and conid structs + * are not counted. */ + size_t current_size; +}; + +/** create doq table */ +struct doq_table* doq_table_create(struct config_file* cfg, + struct ub_randstate* rnd); + +/** delete doq table */ +void doq_table_delete(struct doq_table* table); + +/** + * Timer information for doq timer. + */ +struct doq_timer { + /** The rbnode in the tree sorted by timeout value. Key this struct. */ + struct rbnode_type node; + /** The timeout value. Absolute time value. */ + struct timeval time; + /** If the timer is in the time tree, with the node. */ + int timer_in_tree; + /** If there are more timers with the exact same timeout value, + * they form a set of timers. The rbnode timer has a link to the list + * with the other timers in the set. The rbnode timer is not a + * member of the list with the other timers. The other timers are not + * linked into the tree. */ + struct doq_timer* setlist_first, *setlist_last; + /** If the timer is on the setlist. */ + int timer_in_list; + /** If in the setlist, the next and prev element. */ + struct doq_timer* setlist_next, *setlist_prev; + /** The connection that is timeouted. */ + struct doq_conn* conn; + /** The worker that is waiting for the timeout event. + * Set for the rbnode tree linked element. If a worker is waiting + * for the event. If NULL, no worker is waiting for this timeout. */ + struct doq_server_socket* worker_doq_socket; +}; + +/** + * Key information that makes a doq_conn node in the tree lookup. + */ +struct doq_conn_key { + /** the remote endpoint and local endpoint and ifindex */ + struct doq_pkt_addr paddr; + /** the doq connection dcid */ + uint8_t* dcid; + /** length of dcid */ + size_t dcidlen; +}; + +/** + * DoQ connection, for DNS over QUIC. One connection to a remote endpoint + * with a number of streams in it. Every stream is like a tcp stream with + * a uint16_t length, query read, and a uint16_t length and answer written. + */ +struct doq_conn { + /** rbtree node, key is addresses and dcid */ + struct rbnode_type node; + /** lock on the connection */ + lock_basic_type lock; + /** the key information, with dcid and address endpoint */ + struct doq_conn_key key; + /** the doq server socket for inside callbacks */ + struct doq_server_socket* doq_socket; + /** the doq table this connection is part of */ + struct doq_table* table; + /** if the connection is about to be deleted. */ + uint8_t is_deleted; + /** the version, the client chosen version of QUIC */ + uint32_t version; + /** the ngtcp2 connection, a server connection */ + struct ngtcp2_conn* conn; + /** the connection ids that are associated with this doq_conn. + * There can be a number, that can change. They are linked here, + * so that upon removal, the list of actually associated conid + * elements can be removed as well. */ + struct doq_conid* conid_list; + /** the ngtcp2 last error for the connection */ +#ifdef HAVE_NGTCP2_CCERR_DEFAULT + struct ngtcp2_ccerr ccerr; +#else + struct ngtcp2_connection_close_error last_error; +#endif + /** the recent tls alert error code */ + uint8_t tls_alert; + /** the ssl context, SSL* */ + void* ssl; +#ifdef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT + /** the connection reference for ngtcp2_conn and userdata in ssl */ + struct ngtcp2_crypto_conn_ref conn_ref; +#endif + /** closure packet, if any */ + uint8_t* close_pkt; + /** length of closure packet. */ + size_t close_pkt_len; + /** closure ecn */ + uint32_t close_ecn; + /** the streams for this connection, of type doq_stream */ + struct rbtree_type stream_tree; + /** the streams that want write, they have something to write. + * The list is ordered, the last have to wait for the first to + * get their data written. */ + struct doq_stream* stream_write_first, *stream_write_last; + /** the conn has write interest if true, no write interest if false. */ + uint8_t write_interest; + /** if the conn is on the connection write list */ + uint8_t on_write_list; + /** the connection write list prev and next, if on the write list */ + struct doq_conn* write_prev, *write_next; + /** The timer for the connection. If unused, it is not in the tree + * and not in the list. It is alloced here, so that it is prealloced. + * It has to be set after every read and write on the connection, so + * this improves performance, but also the allocation does not fail. */ + struct doq_timer timer; +}; + +/** + * Connection ID and the doq_conn that is that connection. A connection + * has an original dcid, and then more connection ids associated. + */ +struct doq_conid { + /** rbtree node, key is the connection id. */ + struct rbnode_type node; + /** the next and prev in the list of conids for the doq_conn */ + struct doq_conid* next, *prev; + /** key to the doq_conn that is the connection */ + struct doq_conn_key key; + /** the connection id, byte string */ + uint8_t* cid; + /** the length of cid */ + size_t cidlen; +}; + +/** + * DoQ stream, for DNS over QUIC. + */ +struct doq_stream { + /** the rbtree node for the stream, key is the stream_id */ + rbnode_type node; + /** the stream id */ + int64_t stream_id; + /** if the stream is closed */ + uint8_t is_closed; + /** if the query is complete */ + uint8_t is_query_complete; + /** the number of bytes read on the stream, up to querylen+2. */ + size_t nread; + /** the length of the input query bytes */ + size_t inlen; + /** the input bytes */ + uint8_t* in; + /** does the stream have an answer to send */ + uint8_t is_answer_available; + /** the answer bytes sent, up to outlen+2. */ + size_t nwrite; + /** the length of the output answer bytes */ + size_t outlen; + /** the output length in network wireformat */ + uint16_t outlen_wire; + /** the output packet bytes */ + uint8_t* out; + /** if the stream is on the write list */ + uint8_t on_write_list; + /** the prev and next on the write list, if on the list */ + struct doq_stream* write_prev, *write_next; +}; + +/** doq application error code that is sent when a stream is closed */ +#define DOQ_APP_ERROR_CODE 1 + +/** + * Create the doq connection. + * @param c: the comm point for the listening doq socket. + * @param paddr: with remote and local address and ifindex for the + * connection destination. This is where packets are sent. + * @param dcid: the dcid, Destination Connection ID. + * @param dcidlen: length of dcid. + * @param version: client chosen version. + * @return new doq connection or NULL on allocation failure. + */ +struct doq_conn* doq_conn_create(struct comm_point* c, + struct doq_pkt_addr* paddr, const uint8_t* dcid, size_t dcidlen, + uint32_t version); + +/** + * Delete the doq connection structure. + * @param conn: to delete. + * @param table: with memory size. + */ +void doq_conn_delete(struct doq_conn* conn, struct doq_table* table); + +/** compare function of doq_conn */ +int doq_conn_cmp(const void* key1, const void* key2); + +/** compare function of doq_conid */ +int doq_conid_cmp(const void* key1, const void* key2); + +/** compare function of doq_timer */ +int doq_timer_cmp(const void* key1, const void* key2); + +/** compare function of doq_stream */ +int doq_stream_cmp(const void* key1, const void* key2); + +/** setup the doq_socket server tls context */ +int doq_socket_setup_ctx(struct doq_server_socket* doq_socket); + +/** setup the doq connection callbacks, and settings. */ +int doq_conn_setup(struct doq_conn* conn, uint8_t* scid, size_t scidlen, + uint8_t* ocid, size_t ocidlen, const uint8_t* token, size_t tokenlen); + +/** fill a buffer with random data */ +void doq_fill_rand(struct ub_randstate* rnd, uint8_t* buf, size_t len); + +/** delete a doq_conid */ +void doq_conid_delete(struct doq_conid* conid); + +/** add a connection id to the doq_conn. + * caller must hold doq_table.conid_lock. */ +int doq_conn_associate_conid(struct doq_conn* conn, uint8_t* data, + size_t datalen); + +/** remove a connection id from the doq_conn. + * caller must hold doq_table.conid_lock. */ +void doq_conn_dissociate_conid(struct doq_conn* conn, const uint8_t* data, + size_t datalen); + +/** initial setup to link current connection ids to the doq_conn */ +int doq_conn_setup_conids(struct doq_conn* conn); + +/** remove the connection ids from the doq_conn. + * caller must hold doq_table.conid_lock. */ +void doq_conn_clear_conids(struct doq_conn* conn); + +/** find a conid in the doq_conn connection. + * caller must hold table.conid_lock. */ +struct doq_conid* doq_conid_find(struct doq_table* doq_table, + const uint8_t* data, size_t datalen); + +/** receive a packet for a connection */ +int doq_conn_recv(struct comm_point* c, struct doq_pkt_addr* paddr, + struct doq_conn* conn, struct ngtcp2_pkt_info* pi, int* err_retry, + int* err_drop); + +/** send packets for a connection */ +int doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn, + int* err_drop); + +/** send the close packet for the connection, perhaps again. */ +int doq_conn_send_close(struct comm_point* c, struct doq_conn* conn); + +/** delete doq stream */ +void doq_stream_delete(struct doq_stream* stream); + +/** doq read a connection key from repinfo. It is not malloced, but points + * into the repinfo for the dcid. */ +void doq_conn_key_from_repinfo(struct doq_conn_key* key, + struct comm_reply* repinfo); + +/** doq find a stream in the connection */ +struct doq_stream* doq_stream_find(struct doq_conn* conn, int64_t stream_id); + +/** doq shutdown the stream. */ +int doq_stream_close(struct doq_conn* conn, struct doq_stream* stream, + int send_shutdown); + +/** send reply for a connection */ +int doq_stream_send_reply(struct doq_conn* conn, struct doq_stream* stream, + struct sldns_buffer* buf); + +/** the connection has write interest, wants to write packets */ +void doq_conn_write_enable(struct doq_conn* conn); + +/** the connection has no write interest, does not want to write packets */ +void doq_conn_write_disable(struct doq_conn* conn); + +/** set the connection on or off the write list, depending on write interest */ +void doq_conn_set_write_list(struct doq_table* table, struct doq_conn* conn); + +/** doq remove the connection from the write list */ +void doq_conn_write_list_remove(struct doq_table* table, + struct doq_conn* conn); + +/** doq get the first conn from the write list, if any, popped from list. + * Locks the conn that is returned. */ +struct doq_conn* doq_table_pop_first(struct doq_table* table); + +/** + * doq check if the timer for the conn needs to be changed. + * @param conn: connection, caller must hold lock on it. + * @param tv: time value, absolute time, returned. + * @return true if timer needs to be set to tv, false if no change is needed + * to the timer. The timer is already set to the right time in that case. + */ +int doq_conn_check_timer(struct doq_conn* conn, struct timeval* tv); + +/** doq remove timer from tree */ +void doq_timer_tree_remove(struct doq_table* table, struct doq_timer* timer); + +/** doq remove timer from list */ +void doq_timer_list_remove(struct doq_table* table, struct doq_timer* timer); + +/** doq unset the timer if it was set. */ +void doq_timer_unset(struct doq_table* table, struct doq_timer* timer); + +/** doq set the timer and add it. */ +void doq_timer_set(struct doq_table* table, struct doq_timer* timer, + struct doq_server_socket* worker_doq_socket, struct timeval* tv); + +/** doq find a timeout in the timer tree */ +struct doq_timer* doq_timer_find_time(struct doq_table* table, + struct timeval* tv); + +/** doq handle timeout for a connection. Pass conn locked. Returns false for + * deletion. */ +int doq_conn_handle_timeout(struct doq_conn* conn); + +/** doq add size to the current quic buffer counter */ +void doq_table_quic_size_add(struct doq_table* table, size_t add); + +/** doq subtract size from the current quic buffer counter */ +void doq_table_quic_size_subtract(struct doq_table* table, size_t subtract); + +/** doq check if mem is available for quic. */ +int doq_table_quic_size_available(struct doq_table* table, + struct config_file* cfg, size_t mem); + +/** doq get the quic size value */ +size_t doq_table_quic_size_get(struct doq_table* table); +#endif /* HAVE_NGTCP2 */ + char* set_ip_dscp(int socket, int addrfamily, int ds); /** for debug and profiling purposes only @@ -459,4 +846,14 @@ char* set_ip_dscp(int socket, int addrfa */ void verbose_print_unbound_socket(struct unbound_socket* ub_sock); +/** event callback for testcode/doqclient */ +void doq_client_event_cb(int fd, short event, void* arg); + +/** timer event callback for testcode/doqclient */ +void doq_client_timer_cb(int fd, short event, void* arg); + +#ifdef HAVE_NGTCP2 +/** get a timestamp in nanoseconds */ +ngtcp2_tstamp doq_get_timestamp_nanosec(void); +#endif #endif /* LISTEN_DNSPORT_H */ Index: services/mesh.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/mesh.c,v diff -u -p -r1.30 mesh.c --- services/mesh.c 4 Sep 2024 09:36:40 -0000 1.30 +++ services/mesh.c 7 Feb 2025 21:25:44 -0000 @@ -311,7 +311,7 @@ int mesh_make_new_space(struct mesh_area struct dns_msg* mesh_serve_expired_lookup(struct module_qstate* qstate, - struct query_info* lookup_qinfo) + struct query_info* lookup_qinfo, int* is_expired) { hashvalue_type h; struct lruhash_entry* e; @@ -321,6 +321,7 @@ mesh_serve_expired_lookup(struct module_ time_t timenow = *qstate->env->now; int must_validate = (!(qstate->query_flags&BIT_CD) || qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate; + *is_expired = 0; /* Lookup cache */ h = query_info_hash(lookup_qinfo, qstate->query_flags); e = slabhash_lookup(qstate->env->msg_cache, h, lookup_qinfo, 0); @@ -328,6 +329,7 @@ mesh_serve_expired_lookup(struct module_ key = (struct msgreply_entry*)e->key; data = (struct reply_info*)e->data; + if(data->ttl < timenow) *is_expired = 1; msg = tomsg(qstate->env, &key->key, data, qstate->region, timenow, qstate->env->cfg->serve_expired, qstate->env->scratch); if(!msg) @@ -2176,6 +2178,7 @@ mesh_serve_expired_callback(void* arg) int must_validate = (!(qstate->query_flags&BIT_CD) || qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate; int i = 0; + int is_expired; if(!qstate->serve_expired_data) return; verbose(VERB_ALGO, "Serve expired: Trying to reply with expired data"); comm_timer_delete(qstate->serve_expired_data->timer); @@ -2193,7 +2196,7 @@ mesh_serve_expired_callback(void* arg) fptr_ok(fptr_whitelist_serve_expired_lookup( qstate->serve_expired_data->get_cached_answer)); msg = (*qstate->serve_expired_data->get_cached_answer)(qstate, - lookup_qinfo); + lookup_qinfo, &is_expired); if(!msg) return; /* Reset these in case we pass a second time from here. */ @@ -2285,8 +2288,10 @@ mesh_serve_expired_callback(void* arg) /* Add EDE Stale Answer (RCF8914). Ignore global ede as this is * warning instead of an error */ - if (r->edns.edns_present && qstate->env->cfg->ede_serve_expired && - qstate->env->cfg->ede) { + if(r->edns.edns_present && + qstate->env->cfg->ede_serve_expired && + qstate->env->cfg->ede && + is_expired) { edns_opt_list_append_ede(&r->edns.opt_list_out, mstate->s.region, LDNS_EDE_STALE_ANSWER, NULL); } Index: services/mesh.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/mesh.h,v diff -u -p -r1.12 mesh.h --- services/mesh.h 13 Jun 2024 14:30:28 -0000 1.12 +++ services/mesh.h 7 Feb 2025 21:25:44 -0000 @@ -673,11 +673,12 @@ void mesh_serve_expired_callback(void* a * the same behavior as when replying from cache. * @param qstate: the module qstate. * @param lookup_qinfo: the query info to look for in the cache. + * @param is_expired: set if the cached answer is expired. * @return dns_msg if a cached answer was found, otherwise NULL. */ struct dns_msg* mesh_serve_expired_lookup(struct module_qstate* qstate, - struct query_info* lookup_qinfo); + struct query_info* lookup_qinfo, int* is_expired); /** * See if the mesh has space for more queries. You can allocate queries Index: services/modstack.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/modstack.c,v diff -u -p -r1.9 modstack.c --- services/modstack.c 4 Sep 2024 09:36:40 -0000 1.9 +++ services/modstack.c 7 Feb 2025 21:25:44 -0000 @@ -265,7 +265,7 @@ modstack_call_init(struct module_stack* int i, changed = 0; env->need_to_validate = 0; /* set by module init below */ for(i=0; inum; i++) { - while(*module_conf && isspace(*module_conf)) + while(*module_conf && isspace((unsigned char)*module_conf)) module_conf++; if(strncmp(stack->mod[i]->name, module_conf, strlen(stack->mod[i]->name))) { Index: services/rpz.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/rpz.c,v diff -u -p -r1.1.1.13 rpz.c --- services/rpz.c 4 Sep 2024 09:35:35 -0000 1.1.1.13 +++ services/rpz.c 7 Feb 2025 21:25:44 -0000 @@ -1969,6 +1969,7 @@ rpz_synthesize_nodata(struct rpz* ATTR_U 0, /* ttl */ 0, /* prettl */ 0, /* expttl */ + 0, /* norecttl */ 0, /* an */ 0, /* ns */ 0, /* ar */ @@ -1999,6 +2000,7 @@ rpz_synthesize_nxdomain(struct rpz* r, s 0, /* ttl */ 0, /* prettl */ 0, /* expttl */ + 0, /* norecttl */ 0, /* an */ 0, /* ns */ 0, /* ar */ @@ -2031,6 +2033,7 @@ rpz_synthesize_localdata_from_rrset(stru 0, /* ttl */ 0, /* prettl */ 0, /* expttl */ + 0, /* norecttl */ 1, /* an */ 0, /* ns */ 0, /* ar */ @@ -2176,6 +2179,7 @@ rpz_synthesize_cname_override_msg(struct 0, /* ttl */ 0, /* prettl */ 0, /* expttl */ + 0, /* norecttl */ 1, /* an */ 0, /* ns */ 0, /* ar */ @@ -2288,15 +2292,18 @@ rpz_apply_nsip_trigger(struct module_qst if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data"); ret = rpz_synthesize_nodata(r, ms, qchase, az); + ms->rpz_applied = 1; goto done; } switch(action) { case RPZ_NXDOMAIN_ACTION: ret = rpz_synthesize_nxdomain(r, ms, qchase, az); + ms->rpz_applied = 1; break; case RPZ_NODATA_ACTION: ret = rpz_synthesize_nodata(r, ms, qchase, az); + ms->rpz_applied = 1; break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2306,11 +2313,13 @@ rpz_apply_nsip_trigger(struct module_qst break; case RPZ_DROP_ACTION: ret = rpz_synthesize_nodata(r, ms, qchase, az); + ms->rpz_applied = 1; ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: ret = rpz_synthesize_nsip_localdata(r, ms, qchase, raddr, az); if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } + ms->rpz_applied = 1; break; case RPZ_PASSTHRU_ACTION: ret = NULL; @@ -2318,6 +2327,7 @@ rpz_apply_nsip_trigger(struct module_qst break; case RPZ_CNAME_OVERRIDE_ACTION: ret = rpz_synthesize_cname_override_msg(r, ms, qchase); + ms->rpz_applied = 1; break; default: verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", @@ -2352,9 +2362,11 @@ rpz_apply_nsdname_trigger(struct module_ switch(action) { case RPZ_NXDOMAIN_ACTION: ret = rpz_synthesize_nxdomain(r, ms, qchase, az); + ms->rpz_applied = 1; break; case RPZ_NODATA_ACTION: ret = rpz_synthesize_nodata(r, ms, qchase, az); + ms->rpz_applied = 1; break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2364,11 +2376,13 @@ rpz_apply_nsdname_trigger(struct module_ break; case RPZ_DROP_ACTION: ret = rpz_synthesize_nodata(r, ms, qchase, az); + ms->rpz_applied = 1; ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: ret = rpz_synthesize_nsdname_localdata(r, ms, qchase, z, match, az); if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } + ms->rpz_applied = 1; break; case RPZ_PASSTHRU_ACTION: ret = NULL; @@ -2376,6 +2390,7 @@ rpz_apply_nsdname_trigger(struct module_ break; case RPZ_CNAME_OVERRIDE_ACTION: ret = rpz_synthesize_cname_override_msg(r, ms, qchase); + ms->rpz_applied = 1; break; default: verbose(VERB_ALGO, "rpz: nsdname: bug: unhandled or invalid action: '%s'", @@ -2579,9 +2594,11 @@ struct dns_msg* rpz_callback_from_iterat switch(localzone_type_to_rpz_action(lzt)) { case RPZ_NXDOMAIN_ACTION: ret = rpz_synthesize_nxdomain(r, ms, &is->qchase, a); + ms->rpz_applied = 1; break; case RPZ_NODATA_ACTION: ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); + ms->rpz_applied = 1; break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2591,11 +2608,13 @@ struct dns_msg* rpz_callback_from_iterat break; case RPZ_DROP_ACTION: ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); + ms->rpz_applied = 1; ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: ret = rpz_synthesize_qname_localdata_msg(r, ms, &is->qchase, z, a); if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); } + ms->rpz_applied = 1; break; case RPZ_PASSTHRU_ACTION: ret = NULL; Index: services/cache/dns.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/cache/dns.c,v diff -u -p -r1.25 dns.c --- services/cache/dns.c 4 Sep 2024 09:36:40 -0000 1.25 +++ services/cache/dns.c 7 Feb 2025 21:25:44 -0000 @@ -88,7 +88,7 @@ store_rrsets(struct module_env* env, str /* update ref if it was in the cache */ switch(rrset_cache_update(env->rrset_cache, &rep->ref[i], env->alloc, ((ntohs(rep->ref[i].key->rk.type)== - LDNS_RR_TYPE_NS && !pside)?qstarttime:now + leeway))) { + LDNS_RR_TYPE_NS && !pside)?qstarttime:now) + leeway)) { case 0: /* ref unchanged, item inserted */ break; case 2: /* ref updated, cache is superior */ @@ -162,7 +162,7 @@ dns_cache_store_msg(struct module_env* e size_t i; /* store RRsets */ - for(i=0; irrset_count; i++) { + for(i=0; irrset_count; i++) { rep->ref[i].key = rep->rrsets[i]; rep->ref[i].id = rep->rrsets[i]->id; } @@ -197,6 +197,7 @@ dns_cache_store_msg(struct module_env* e reply_info_sortref(rep); if(!(e = query_info_entrysetup(qinfo, rep, hash))) { log_err("store_msg: malloc failed"); + reply_info_delete(rep, NULL); return; } slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc); @@ -365,7 +366,7 @@ find_add_addrs(struct module_env* env, u /** find and add A and AAAA records for missing nameservers in delegpt */ int cache_fill_missing(struct module_env* env, uint16_t qclass, - struct regional* region, struct delegpt* dp) + struct regional* region, struct delegpt* dp, uint32_t flags) { struct delegpt_ns* ns; struct msgreply_entry* neg; @@ -376,7 +377,7 @@ cache_fill_missing(struct module_env* en continue; ns->cache_lookup_count++; akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0); + ns->namelen, LDNS_RR_TYPE_A, qclass, flags, now, 0); if(akey) { if(!delegpt_add_rrset_A(dp, region, akey, ns->lame, NULL)) { @@ -397,7 +398,7 @@ cache_fill_missing(struct module_env* en } } akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); + ns->namelen, LDNS_RR_TYPE_AAAA, qclass, flags, now, 0); if(akey) { if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame, NULL)) { @@ -607,22 +608,8 @@ tomsg(struct module_env* env, struct que time_t now_control = now; if(now > r->ttl) { /* Check if we are allowed to serve expired */ - if(allow_expired) { - if(env->cfg->serve_expired_ttl && - r->serve_expired_ttl < now) { - return NULL; - } - /* Ignore expired failure answers */ - if(FLAGS_GET_RCODE(r->flags) != - LDNS_RCODE_NOERROR && - FLAGS_GET_RCODE(r->flags) != - LDNS_RCODE_NXDOMAIN && - FLAGS_GET_RCODE(r->flags) != - LDNS_RCODE_YXDOMAIN) - return 0; - } else { + if(!allow_expired || !reply_info_can_answer_expired(r, now)) return NULL; - } /* Change the current time so we can pass the below TTL checks when * serving expired data. */ now_control = r->ttl - env->cfg->serve_expired_reply_ttl; @@ -641,6 +628,7 @@ tomsg(struct module_env* env, struct que else msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; + msg->rep->serve_expired_norec_ttl = 0; msg->rep->security = r->security; msg->rep->an_numrrsets = r->an_numrrsets; msg->rep->ns_numrrsets = r->ns_numrrsets; @@ -724,6 +712,7 @@ rrset_msg(struct ub_packed_rrset_key* rr msg->rep->ttl = d->ttl - now; msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; + msg->rep->serve_expired_norec_ttl = 0; msg->rep->security = sec_status_unchecked; msg->rep->an_numrrsets = 1; msg->rep->ns_numrrsets = 0; @@ -763,6 +752,7 @@ synth_dname_msg(struct ub_packed_rrset_k msg->rep->ttl = d->ttl - now; msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; + msg->rep->serve_expired_norec_ttl = 0; msg->rep->security = sec_status_unchecked; msg->rep->an_numrrsets = 1; msg->rep->ns_numrrsets = 0; @@ -1070,6 +1060,35 @@ dns_cache_store(struct module_env* env, struct regional* region, uint32_t flags, time_t qstarttime) { struct reply_info* rep = NULL; + if(SERVE_EXPIRED) { + /* We are serving expired records. Before caching, check if a + * useful expired record exists. */ + struct msgreply_entry* e = msg_cache_lookup(env, + msgqinf->qname, msgqinf->qname_len, msgqinf->qtype, + msgqinf->qclass, flags, 0, 0); + if(e) { + struct reply_info* cached = e->entry.data; + if(cached->ttl < *env->now + && reply_info_could_use_expired(cached, *env->now) + /* If we are validating make sure only + * validating modules can update such messages. + * In that case don't cache it and let a + * subsequent module handle the caching. For + * example, the iterator should not replace an + * expired secure answer with a fresh unchecked + * one and let the validator manage caching. */ + && cached->security != sec_status_bogus + && (env->need_to_validate && + msgrep->security == sec_status_unchecked)) { + verbose(VERB_ALGO, "a validated expired entry " + "could be overwritten, skip caching " + "the new message at this stage"); + lock_rw_unlock(&e->entry.lock); + return 1; + } + lock_rw_unlock(&e->entry.lock); + } + } /* alloc, malloc properly (not in region, like msg is) */ rep = reply_info_copy(msgrep, env->alloc, NULL); if(!rep) Index: services/cache/dns.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/cache/dns.h,v diff -u -p -r1.11 dns.h --- services/cache/dns.h 13 Feb 2024 12:57:11 -0000 1.11 +++ services/cache/dns.h 7 Feb 2025 21:25:44 -0000 @@ -202,10 +202,11 @@ struct dns_msg* dns_cache_lookup(struct * @param qclass: which class to look in. * @param region: where to store new dp info. * @param dp: delegation point to fill missing entries. + * @param flags: rrset flags, or 0. * @return false on alloc failure. */ int cache_fill_missing(struct module_env* env, uint16_t qclass, - struct regional* region, struct delegpt* dp); + struct regional* region, struct delegpt* dp, uint32_t flags); /** * Utility, create new, unpacked data structure for cache response. Index: services/cache/rrset.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/cache/rrset.c,v diff -u -p -r1.8 rrset.c --- services/cache/rrset.c 13 Jun 2024 14:30:28 -0000 1.8 +++ services/cache/rrset.c 7 Feb 2025 21:25:44 -0000 @@ -128,8 +128,8 @@ need_to_update_rrset(void* nd, void* cd, { struct packed_rrset_data* newd = (struct packed_rrset_data*)nd; struct packed_rrset_data* cached = (struct packed_rrset_data*)cd; - /* o if new data is expired, current data is better */ - if( newd->ttl < timenow && cached->ttl >= timenow) + /* o if new data is expired, cached data is better */ + if( newd->ttl < timenow && timenow <= cached->ttl) return 0; /* o store if rrset has been validated * everything better than bogus data @@ -140,9 +140,9 @@ need_to_update_rrset(void* nd, void* cd, if( cached->security == sec_status_bogus && newd->security != sec_status_bogus && !equal) return 1; - /* o if current RRset is more trustworthy - insert it */ + /* o if new RRset is more trustworthy - insert it */ if( newd->trust > cached->trust ) { - /* if the cached rrset is bogus, and this one equal, + /* if the cached rrset is bogus, and new is equal, * do not update the TTL - let it expire. */ if(equal && cached->ttl >= timenow && cached->security == sec_status_bogus) @@ -155,7 +155,7 @@ need_to_update_rrset(void* nd, void* cd, /* o same trust, but different in data - insert it */ if( newd->trust == cached->trust && !equal ) { /* if this is type NS, do not 'stick' to owner that changes - * the NS RRset, but use the old TTL for the new data, and + * the NS RRset, but use the cached TTL for the new data, and * update to fetch the latest data. ttl is not expired, because * that check was before this one. */ if(ns) { Index: smallapp/unbound-control-setup.sh.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/smallapp/unbound-control-setup.sh.in,v diff -u -p -r1.9 unbound-control-setup.sh.in --- smallapp/unbound-control-setup.sh.in 4 Sep 2024 09:36:40 -0000 1.9 +++ smallapp/unbound-control-setup.sh.in 7 Feb 2025 21:25:44 -0000 @@ -104,7 +104,7 @@ while getopts 'd:hr' arg; do done shift $((OPTIND - 1)) -if ! openssl >/dev/null 2>&1; then +if ! openssl version /dev/null 2>&1; then echo "$0 requires openssl to be installed for keys/certificates generation." >&2 exit 1 fi Index: smallapp/unbound-control.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/smallapp/unbound-control.c,v diff -u -p -r1.28 unbound-control.c --- smallapp/unbound-control.c 4 Sep 2024 09:36:40 -0000 1.28 +++ smallapp/unbound-control.c 7 Feb 2025 21:25:44 -0000 @@ -293,6 +293,9 @@ static void print_mem(struct ub_shm_stat PR_LL("mem.streamwait", s->svr.mem_stream_wait); PR_LL("mem.http.query_buffer", s->svr.mem_http2_query_buffer); PR_LL("mem.http.response_buffer", s->svr.mem_http2_response_buffer); +#ifdef HAVE_NGTCP2 + PR_LL("mem.quic", s->svr.mem_quic); +#endif } /** print histogram */ @@ -359,6 +362,9 @@ static void print_extended(struct ub_sta PR_UL("num.query.tls_resume", s->svr.qtls_resume); PR_UL("num.query.ipv6", s->svr.qipv6); PR_UL("num.query.https", s->svr.qhttps); +#ifdef HAVE_NGTCP2 + PR_UL("num.query.quic", s->svr.qquic); +#endif /* flags */ PR_UL("num.query.flags.QR", s->svr.qbit_QR); Index: smallapp/unbound-host.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/smallapp/unbound-host.c,v diff -u -p -r1.8 unbound-host.c --- smallapp/unbound-host.c 5 Sep 2023 11:12:10 -0000 1.8 +++ smallapp/unbound-host.c 7 Feb 2025 21:25:44 -0000 @@ -50,6 +50,8 @@ #undef calloc #undef free #undef realloc +#undef reallocarray +#undef strdup #endif #ifdef UNBOUND_ALLOC_LITE #undef malloc @@ -492,7 +494,11 @@ int main(int argc, char* argv[]) if(strcmp(use_syslog, "yes") == 0) /* disable use-syslog */ check_ub_res(ub_ctx_set_option(ctx, "use-syslog:", "no")); +#ifdef UNBOUND_ALLOC_STATS + unbound_stat_free_log(use_syslog, __FILE__, __LINE__, __func__); +#else free(use_syslog); +#endif } argc -= optind; argv += optind; Index: smallapp/worker_cb.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/smallapp/worker_cb.c,v diff -u -p -r1.12 worker_cb.c --- smallapp/worker_cb.c 23 Feb 2022 12:04:06 -0000 1.12 +++ smallapp/worker_cb.c 7 Feb 2025 21:25:44 -0000 @@ -255,3 +255,19 @@ void dtio_mainfdcallback(int ATTR_UNUSED log_assert(0); } #endif + +#ifdef HAVE_NGTCP2 +void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} +#endif + +#ifdef HAVE_NGTCP2 +void doq_client_timer_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), + void* ATTR_UNUSED(arg)) +{ + log_assert(0); +} +#endif Index: testcode/checklocks.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/checklocks.c,v diff -u -p -r1.1.1.3 checklocks.c --- testcode/checklocks.c 13 Jun 2024 14:29:33 -0000 1.1.1.3 +++ testcode/checklocks.c 7 Feb 2025 21:25:44 -0000 @@ -68,6 +68,8 @@ static struct thr_check* thread_infos[TH int check_locking_order = 1; /** the pid of this runset, reasonably unique. */ static pid_t check_lock_pid; +/** the name of the output file */ +static const char* output_name = "ublocktrace"; /** * Should checklocks print a trace of the lock and unlock calls. * It uses fprintf for that because the log function uses a lock and that @@ -142,7 +144,8 @@ acquire_locklock(struct checked_lock* lo /** add protected region */ void -lock_protect(void *p, void* area, size_t size) +lock_protect_place(void* p, void* area, size_t size, const char* def_func, + const char* def_file, int def_line, const char* def_area) { struct checked_lock* lock = *(struct checked_lock**)p; struct protected_area* e = (struct protected_area*)malloc( @@ -151,6 +154,10 @@ lock_protect(void *p, void* area, size_t fatal_exit("lock_protect: out of memory"); e->region = area; e->size = size; + e->def_func = def_func; + e->def_file = def_file; + e->def_line = def_line; + e->def_area = def_area; e->hold = malloc(size); if(!e->hold) fatal_exit("lock_protect: out of memory"); @@ -203,6 +210,9 @@ prot_check(struct checked_lock* lock, if(memcmp(p->hold, p->region, p->size) != 0) { log_hex("memory prev", p->hold, p->size); log_hex("memory here", p->region, p->size); + log_err("lock_protect on %s %s:%d %s failed", + p->def_func, p->def_file, p->def_line, + p->def_area); lock_error(lock, func, file, line, "protected area modified"); } @@ -675,13 +685,19 @@ checklock_unlock(enum check_lock_type ty } } +void +checklock_set_output_name(const char* name) +{ + output_name = name; +} + /** open order info debug file, thr->num must be valid */ static void open_lockorder(struct thr_check* thr) { char buf[24]; time_t t; - snprintf(buf, sizeof(buf), "ublocktrace.%d", thr->num); + snprintf(buf, sizeof(buf), "%s.%d", output_name, thr->num); thr->order_info = fopen(buf, "w"); if(!thr->order_info) fatal_exit("could not open %s: %s", buf, strerror(errno)); Index: testcode/checklocks.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/checklocks.h,v diff -u -p -r1.1.1.1 checklocks.h --- testcode/checklocks.h 17 Sep 2018 09:43:45 -0000 1.1.1.1 +++ testcode/checklocks.h 7 Feb 2025 21:25:44 -0000 @@ -90,6 +90,14 @@ struct protected_area { void* hold; /** next protected area in list */ struct protected_area* next; + /** the place where the lock_protect is made, at init. */ + const char* def_func; + /** the file where the lock_protect is made */ + const char* def_file; + /** the line number where the lock_protect is made */ + int def_line; + /** the text string for the area that is protected, at init call. */ + const char* def_area; }; /** @@ -181,12 +189,19 @@ struct checked_lock { * It demangles the lock itself (struct checked_lock**). * @param area: ptr to mem. * @param size: length of area. + * @param def_func: function where the lock_protect() line is. + * @param def_file: file where the lock_protect() line is. + * @param def_line: line where the lock_protect() line is. + * @param def_area: area string * You can call it multiple times with the same lock to give several areas. * Call it when you are done initializing the area, since it will be copied * at this time and protected right away against unauthorised changes until * the next lock() call is done. */ -void lock_protect(void* lock, void* area, size_t size); +void lock_protect_place(void* lock, void* area, size_t size, + const char* def_func, const char* def_file, int def_line, + const char* def_area); +#define lock_protect(lock, area, size) lock_protect_place(lock, area, size, __func__, __FILE__, __LINE__, #area) /** * Remove protected area from lock. @@ -202,6 +217,13 @@ void lock_unprotect(void* lock, void* ar * @return: in bytes, including protected areas. */ size_t lock_get_mem(void* lock); + +/** + * Set the output name, prefix, of the lock check output file(s). + * Call it before the checklock_start or thread creation. Pass a fixed string. + * @param name: string to use for output data file names. + */ +void checklock_set_output_name(const char* name); /** * Initialise checklock. Sets up internal debug structures. Index: testcode/fake_event.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/fake_event.c,v diff -u -p -r1.1.1.16 fake_event.c --- testcode/fake_event.c 4 Sep 2024 09:35:36 -0000 1.1.1.16 +++ testcode/fake_event.c 7 Feb 2025 21:25:44 -0000 @@ -939,6 +939,11 @@ listen_create(struct comm_base* base, st int ATTR_UNUSED(http_notls), struct tcl_list* ATTR_UNUSED(tcp_conn_limit), void* ATTR_UNUSED(sslctx), struct dt_env* ATTR_UNUSED(dtenv), + struct doq_table* ATTR_UNUSED(table), + struct ub_randstate* ATTR_UNUSED(rnd), + const char* ATTR_UNUSED(ssl_service_key), + const char* ATTR_UNUSED(ssl_service_pem), + struct config_file* ATTR_UNUSED(cfg), comm_point_callback_type* cb, void *cb_arg) { struct replay_runtime* runtime = (struct replay_runtime*)base; Index: testcode/perf.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/perf.c,v diff -u -p -r1.1.1.8 perf.c --- testcode/perf.c 5 Sep 2023 11:07:49 -0000 1.1.1.8 +++ testcode/perf.c 7 Feb 2025 21:25:44 -0000 @@ -220,7 +220,7 @@ perfsetup(struct perfinfo* info) #endif signal(SIGTERM, perf_sigh) == SIG_ERR) fatal_exit("could not bind to signal"); - info->io = (struct perfio*)calloc(sizeof(struct perfio), info->io_num); + info->io = (struct perfio*)calloc(info->io_num, sizeof(struct perfio)); if(!info->io) fatal_exit("out of memory"); #ifndef S_SPLINT_S FD_ZERO(&info->rset); @@ -501,8 +501,8 @@ qlist_grow_capacity(struct perfinfo* inf { size_t newcap = (size_t)((info->qlist_capacity==0)?16: info->qlist_capacity*2); - uint8_t** d = (uint8_t**)calloc(sizeof(uint8_t*), newcap); - size_t* l = (size_t*)calloc(sizeof(size_t), newcap); + uint8_t** d = (uint8_t**)calloc(newcap, sizeof(uint8_t*)); + size_t* l = (size_t*)calloc(newcap, sizeof(size_t)); if(!d || !l) fatal_exit("out of memory"); if(info->qlist_data && info->qlist_capacity) memcpy(d, info->qlist_data, sizeof(uint8_t*)* Index: testcode/testbound.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/testbound.c,v diff -u -p -r1.1.1.11 testbound.c --- testcode/testbound.c 4 Sep 2024 09:35:36 -0000 1.1.1.11 +++ testcode/testbound.c 7 Feb 2025 21:25:44 -0000 @@ -502,7 +502,7 @@ struct listen_port* daemon_remote_open_p struct daemon_remote* daemon_remote_create(struct config_file* ATTR_UNUSED(cfg)) { - return (struct daemon_remote*)calloc(1,1); + return (struct daemon_remote*)calloc(1, sizeof(struct daemon_remote)); } void daemon_remote_delete(struct daemon_remote* rc) @@ -600,3 +600,52 @@ void listen_desetup_locks(void) { /* nothing */ } + +#ifdef HAVE_NGTCP2 +void comm_point_doq_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(event), + void* ATTR_UNUSED(arg)) +{ + /* nothing */ +} + +int doq_conn_cmp(const void* ATTR_UNUSED(key1), const void* ATTR_UNUSED(key2)) +{ + return 0; +} + +int doq_conid_cmp(const void* ATTR_UNUSED(key1), const void* ATTR_UNUSED(key2)) +{ + return 0; +} + +int doq_timer_cmp(const void* ATTR_UNUSED(key1), const void* ATTR_UNUSED(key2)) +{ + return 0; +} + +int doq_stream_cmp(const void* ATTR_UNUSED(key1), const void* ATTR_UNUSED(key2)) +{ + return 0; +} + +struct doq_table* doq_table_create(struct config_file* ATTR_UNUSED(cfg), + struct ub_randstate* ATTR_UNUSED(rnd)) +{ + return calloc(1, sizeof(struct doq_table)); +} + +void doq_table_delete(struct doq_table* table) +{ + free(table); +} + +void doq_timer_cb(void* ATTR_UNUSED(arg)) +{ + /* nothing */ +} + +size_t doq_table_quic_size_get(struct doq_table* ATTR_UNUSED(table)) +{ + return 0; +} +#endif Index: testcode/unitmain.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/unitmain.c,v diff -u -p -r1.1.1.10 unitmain.c --- testcode/unitmain.c 4 Sep 2024 09:35:36 -0000 1.1.1.10 +++ testcode/unitmain.c 7 Feb 2025 21:25:44 -0000 @@ -1232,7 +1232,7 @@ static void edns_ede_answer_encode_test( unit_assert(region); rep = construct_reply_info_base(region, LDNS_RCODE_NOERROR | BIT_QR, 1, - 3600, 3600, 3600, + 3600, 3600, 3600, 0, 0, 0, 0, 0, sec_status_unchecked, LDNS_EDE_NONE); unit_assert(rep); @@ -1432,6 +1432,9 @@ main(int argc, char* argv[]) #ifdef CLIENT_SUBNET ecs_test(); #endif /* CLIENT_SUBNET */ +#ifdef HAVE_NGTCP2 + doq_test(); +#endif /* HAVE_NGTCP2 */ if(log_get_lock()) { lock_basic_destroy((lock_basic_type*)log_get_lock()); } Index: testcode/unitmain.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/unitmain.h,v diff -u -p -r1.1.1.2 unitmain.h --- testcode/unitmain.h 13 Aug 2021 19:55:25 -0000 1.1.1.2 +++ testcode/unitmain.h 7 Feb 2025 21:25:44 -0000 @@ -84,5 +84,7 @@ void authzone_test(void); void zonemd_test(void); /** unit test for tcp_reuse functions */ void tcpreuse_test(void); +/** unit test for doq functions */ +void doq_test(void); #endif /* TESTCODE_UNITMAIN_H */ Index: testcode/unitzonemd.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/unitzonemd.c,v diff -u -p -r1.1.1.3 unitzonemd.c --- testcode/unitzonemd.c 4 Sep 2024 09:35:36 -0000 1.1.1.3 +++ testcode/unitzonemd.c 7 Feb 2025 21:25:44 -0000 @@ -108,7 +108,7 @@ static void zonemd_generate_test(const c digestdup = strdup(digest); unit_assert(digestdup); for(i=0; i= VERB_ALGO) { char zname[255+1]; @@ -165,9 +165,10 @@ static void zonemd_generate_tests(void) 1, 1, "1291b78ddf7669b1a39d014d87626b709b55774c5d7d58fadc556439889a10eaf6f11d615900a4f996bd46279514e473"); /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12 - * from section A.5 */ + * from section A.5. + * Adjusted with renumbered B.root. */ zonemd_generate_test("root-servers.net", SRCDIRSTR "/testdata/zonemd.example_a5.zone", - 1, 1, "f1ca0ccd91bd5573d9f431c00ee0101b2545c97602be0a978a3b11dbfc1c776d5b3e86ae3d973d6b5349ba7f04340f79"); + 1, 1, "5a9521d88984ee123d9626191e2a327a43a16fd4339dd4ecc13d8672d5bae527d066d33645e35778677800005247d199"); } /** test the zonemd check routine */ Index: util/alloc.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/alloc.c,v diff -u -p -r1.5 alloc.c --- util/alloc.c 4 Sep 2024 09:36:41 -0000 1.5 +++ util/alloc.c 7 Feb 2025 21:25:44 -0000 @@ -519,6 +519,15 @@ void *unbound_stat_realloc_log(void *ptr return unbound_stat_realloc(ptr, size); } +/** log to file where alloc was done */ +void *unbound_stat_reallocarray_log(void *ptr, size_t nmemb, size_t size, + const char* file, int line, const char* func) +{ + log_info("%s:%d %s reallocarray(%p, %u, %u)", file, line, func, + ptr, (unsigned)nmemb, (unsigned)size); + return unbound_stat_realloc(ptr, nmemb*size); +} + /** log to file where strdup was done */ char *unbound_stat_strdup_log(const char *s, const char* file, int line, const char* func) Index: util/config_file.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/config_file.c,v diff -u -p -r1.36 config_file.c --- util/config_file.c 4 Sep 2024 09:36:41 -0000 1.36 +++ util/config_file.c 7 Feb 2025 21:25:44 -0000 @@ -135,9 +135,12 @@ config_create(void) cfg->http_query_buffer_size = 4*1024*1024; cfg->http_response_buffer_size = 4*1024*1024; cfg->http_nodelay = 1; + cfg->quic_port = UNBOUND_DNS_OVER_QUIC_PORT; + cfg->quic_size = 8*1024*1024; cfg->use_syslog = 1; cfg->log_identity = NULL; /* changed later with argv[0] */ cfg->log_time_ascii = 0; + cfg->log_time_iso = 0; cfg->log_queries = 0; cfg->log_replies = 0; cfg->log_tag_queryreply = 0; @@ -237,6 +240,7 @@ config_create(void) cfg->harden_short_bufsize = 1; cfg->harden_large_queries = 0; cfg->harden_glue = 1; + cfg->harden_unverified_glue = 0; cfg->harden_dnssec_stripped = 1; cfg->harden_below_nxdomain = 1; cfg->harden_referral_path = 0; @@ -398,6 +402,8 @@ config_create(void) cfg->redis_server_path = NULL; cfg->redis_server_password = NULL; cfg->redis_timeout = 100; + cfg->redis_command_timeout = 0; + cfg->redis_connect_timeout = 0; cfg->redis_server_port = 6379; cfg->redis_expire_records = 0; cfg->redis_logical_db = 0; @@ -408,6 +414,9 @@ config_create(void) cfg->ipset_name_v6 = NULL; #endif cfg->ede = 0; + cfg->iter_scrub_ns = 20; + cfg->iter_scrub_cname = 11; + cfg->max_global_quota = 128; return cfg; error_exit: config_delete(cfg); @@ -541,6 +550,9 @@ int config_set_option(struct config_file else if(strcmp(opt, "log-time-ascii:") == 0) { IS_YES_OR_NO; cfg->log_time_ascii = (strcmp(val, "yes") == 0); log_set_time_asc(cfg->log_time_ascii); } + else if(strcmp(opt, "log-time-iso:") == 0) + { IS_YES_OR_NO; cfg->log_time_iso = (strcmp(val, "yes") == 0); + log_set_time_iso(cfg->log_time_iso); } else S_SIZET_NONZERO("max-udp-size:", max_udp_size) else S_YNO("use-syslog:", use_syslog) else S_STR("log-identity:", log_identity) @@ -594,6 +606,8 @@ int config_set_option(struct config_file else S_MEMSIZE("http-response-buffer-size:", http_response_buffer_size) else S_YNO("http-nodelay:", http_nodelay) else S_YNO("http-notls-downstream:", http_notls_downstream) + else S_NUMBER_NONZERO("quic-port:", quic_port) + else S_MEMSIZE("quic-size:", quic_size) else S_YNO("interface-automatic:", if_automatic) else S_STR("interface-automatic-ports:", if_automatic_ports) else S_YNO("use-systemd:", use_systemd) @@ -672,6 +686,7 @@ int config_set_option(struct config_file else S_STRLIST("root-hints:", root_hints) else S_STR("target-fetch-policy:", target_fetch_policy) else S_YNO("harden-glue:", harden_glue) + else S_YNO("harden-unverified-glue:", harden_unverified_glue) else S_YNO("harden-short-bufsize:", harden_short_bufsize) else S_YNO("harden-large-queries:", harden_large_queries) else S_YNO("harden-dnssec-stripped:", harden_dnssec_stripped) @@ -712,12 +727,17 @@ int config_set_option(struct config_file SERVE_EXPIRED = cfg->serve_expired; } else if(strcmp(opt, "serve-expired-ttl:") == 0) { IS_NUMBER_OR_ZERO; cfg->serve_expired_ttl = atoi(val); SERVE_EXPIRED_TTL=(time_t)cfg->serve_expired_ttl;} - else S_YNO("serve-expired-ttl-reset:", serve_expired_ttl_reset) + else if(strcmp(opt, "serve-expired-ttl-reset:") == 0) + { IS_YES_OR_NO; cfg->serve_expired_ttl_reset = (strcmp(val, "yes") == 0); + SERVE_EXPIRED_TTL_RESET = cfg->serve_expired_ttl_reset; } else if(strcmp(opt, "serve-expired-reply-ttl:") == 0) { IS_NUMBER_OR_ZERO; cfg->serve_expired_reply_ttl = atoi(val); SERVE_EXPIRED_REPLY_TTL=(time_t)cfg->serve_expired_reply_ttl;} else S_NUMBER_OR_ZERO("serve-expired-client-timeout:", serve_expired_client_timeout) else S_YNO("ede:", ede) else S_YNO("ede-serve-expired:", ede_serve_expired) + else S_NUMBER_OR_ZERO("iter-scrub-ns:", iter_scrub_ns) + else S_NUMBER_OR_ZERO("iter-scrub-cname:", iter_scrub_cname) + else S_NUMBER_OR_ZERO("max-global-quota:", max_global_quota) else S_YNO("serve-original-ttl:", serve_original_ttl) else S_STR("val-nsec3-keysize-iterations:", val_nsec3_key_iterations) else S_YNO("zonemd-permissive-mode:", zonemd_permissive_mode) @@ -1054,6 +1074,7 @@ config_get_option(struct config_file* cf else O_YNO(opt, "use-syslog", use_syslog) else O_STR(opt, "log-identity", log_identity) else O_YNO(opt, "log-time-ascii", log_time_ascii) + else O_YNO(opt, "log-time-iso", log_time_iso) else O_DEC(opt, "num-threads", num_threads) else O_IFC(opt, "interface", num_ifs, ifs) else O_IFC(opt, "outgoing-interface", num_out_ifs, out_ifs) @@ -1137,6 +1158,8 @@ config_get_option(struct config_file* cf else O_MEM(opt, "http-response-buffer-size", http_response_buffer_size) else O_YNO(opt, "http-nodelay", http_nodelay) else O_YNO(opt, "http-notls-downstream", http_notls_downstream) + else O_DEC(opt, "quic-port", quic_port) + else O_MEM(opt, "quic-size", quic_size) else O_YNO(opt, "use-systemd", use_systemd) else O_YNO(opt, "do-daemonize", do_daemonize) else O_STR(opt, "chroot", chrootdir) @@ -1162,6 +1185,7 @@ config_get_option(struct config_file* cf else O_YNO(opt, "harden-short-bufsize", harden_short_bufsize) else O_YNO(opt, "harden-large-queries", harden_large_queries) else O_YNO(opt, "harden-glue", harden_glue) + else O_YNO(opt, "harden-unverified-glue", harden_unverified_glue) else O_YNO(opt, "harden-dnssec-stripped", harden_dnssec_stripped) else O_YNO(opt, "harden-below-nxdomain", harden_below_nxdomain) else O_YNO(opt, "harden-referral-path", harden_referral_path) @@ -1186,6 +1210,9 @@ config_get_option(struct config_file* cf else O_DEC(opt, "serve-expired-client-timeout", serve_expired_client_timeout) else O_YNO(opt, "ede", ede) else O_YNO(opt, "ede-serve-expired", ede_serve_expired) + else O_DEC(opt, "iter-scrub-ns", iter_scrub_ns) + else O_DEC(opt, "iter-scrub-cname", iter_scrub_cname) + else O_DEC(opt, "max-global-quota", max_global_quota) else O_YNO(opt, "serve-original-ttl", serve_original_ttl) else O_STR(opt, "val-nsec3-keysize-iterations",val_nsec3_key_iterations) else O_YNO(opt, "zonemd-permissive-mode", zonemd_permissive_mode) @@ -1352,6 +1379,8 @@ config_get_option(struct config_file* cf else O_STR(opt, "redis-server-path", redis_server_path) else O_STR(opt, "redis-server-password", redis_server_password) else O_DEC(opt, "redis-timeout", redis_timeout) + else O_DEC(opt, "redis-command-timeout", redis_command_timeout) + else O_DEC(opt, "redis-connect-timeout", redis_connect_timeout) else O_YNO(opt, "redis-expire-records", redis_expire_records) else O_DEC(opt, "redis-logical-db", redis_logical_db) #endif /* USE_REDIS */ @@ -2323,7 +2352,7 @@ uint8_t* cfg_parse_nsid(const char* str, uint8_t *dp; for ( ch = str, dp = nsid - ; isxdigit(ch[0]) && isxdigit(ch[1]) + ; isxdigit((unsigned char)ch[0]) && isxdigit((unsigned char)ch[1]) ; ch += 2, dp++) { *dp = (uint8_t)sldns_hexdigit_to_int(ch[0]) * 16; *dp += (uint8_t)sldns_hexdigit_to_int(ch[1]); @@ -2379,6 +2408,7 @@ config_apply(struct config_file* config) MIN_TTL = (time_t)config->min_ttl; SERVE_EXPIRED = config->serve_expired; SERVE_EXPIRED_TTL = (time_t)config->serve_expired_ttl; + SERVE_EXPIRED_TTL_RESET = config->serve_expired_ttl_reset; SERVE_EXPIRED_REPLY_TTL = (time_t)config->serve_expired_reply_ttl; SERVE_ORIGINAL_TTL = config->serve_original_ttl; MAX_NEG_TTL = (time_t)config->max_negative_ttl; @@ -2389,10 +2419,12 @@ config_apply(struct config_file* config) MINIMAL_RESPONSES = config->minimal_responses; RRSET_ROUNDROBIN = config->rrset_roundrobin; LOG_TAG_QUERYREPLY = config->log_tag_queryreply; + MAX_GLOBAL_QUOTA = config->max_global_quota; UNKNOWN_SERVER_NICENESS = config->unknown_server_time_limit; USEFUL_SERVER_TOP_TIMEOUT = RTT_MAX_TIMEOUT; BLACKLIST_PENALTY = USEFUL_SERVER_TOP_TIMEOUT*4; log_set_time_asc(config->log_time_ascii); + log_set_time_iso(config->log_time_iso); autr_permit_small_holddown = config->permit_small_holddown; stream_wait_max = config->stream_wait_size; http2_query_buffer_max = config->http_query_buffer_size; @@ -2792,6 +2824,25 @@ if_is_dnscrypt(const char* ifname, const (void)ifname; (void)port; (void)dnscrypt_port; + return 0; +#endif +} + +/** see if interface is quic, its port number == the quic port number */ +int +if_is_quic(const char* ifname, const char* port, int quic_port) +{ +#ifndef HAVE_NGTCP2 + (void)ifname; + (void)port; + (void)quic_port; + return 0; +#else + char* p = strchr(ifname, '@'); + if(!p && atoi(port) == quic_port) + return 1; + if(p && atoi(p+1) == quic_port) + return 1; return 0; #endif } Index: util/config_file.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/config_file.h,v diff -u -p -r1.34 config_file.h --- util/config_file.h 4 Sep 2024 09:36:41 -0000 1.34 +++ util/config_file.h 7 Feb 2025 21:25:44 -0000 @@ -161,6 +161,11 @@ struct config_file { /** Disable TLS for http sockets downstream */ int http_notls_downstream; + /** port on which to provide DNS over QUIC service */ + int quic_port; + /** size of the quic data, max bytes */ + size_t quic_size; + /** outgoing port range number of ports (per thread) */ int outgoing_num_ports; /** number of outgoing tcp buffers per (per thread) */ @@ -288,6 +293,8 @@ struct config_file { int harden_large_queries; /** harden against spoofed glue (out of zone data) */ int harden_glue; + /** harden against unverified glue */ + int harden_unverified_glue; /** harden against receiving no DNSSEC data for trust anchor */ int harden_dnssec_stripped; /** harden against queries that fall under known nxdomain names */ @@ -339,6 +346,8 @@ struct config_file { int use_syslog; /** log timestamp in ascii UTC */ int log_time_ascii; + /** log timestamp in ISO8601 format */ + int log_time_iso; /** log queries with one line per query */ int log_queries; /** log replies with one line per reply */ @@ -737,6 +746,10 @@ struct config_file { char* redis_server_password; /** timeout (in ms) for communication with the redis server */ int redis_timeout; + /** timeout (in ms) for redis commands */ + int redis_command_timeout; + /** timeout (in ms) for redis connection set up */ + int redis_connect_timeout; /** set timeout on redis records based on DNS response ttl */ int redis_expire_records; /** set the redis logical database upon connection */ @@ -760,6 +773,12 @@ struct config_file { #endif /** respond with Extended DNS Errors (RFC8914) */ int ede; + /** limit on NS RRs in RRset for the iterator scrubber. */ + size_t iter_scrub_ns; + /** limit on CNAME, DNAME RRs in answer for the iterator scrubber. */ + int iter_scrub_cname; + /** limit on upstream queries for an incoming query and subqueries. */ + int max_global_quota; }; /** from cfg username, after daemonize setup performed */ @@ -1392,6 +1411,10 @@ int if_is_pp2(const char* ifname, const /** see if interface is DNSCRYPT, its port number == the dnscrypt port number */ int if_is_dnscrypt(const char* ifname, const char* port, int dnscrypt_port); + +/** see if interface is quic, its port number == the quic port number */ +int if_is_quic(const char* ifname, const char* port, int quic_port); + #ifdef USE_LINUX_IP_LOCAL_PORT_RANGE #define LINUX_IP_LOCAL_PORT_RANGE_PATH "/proc/sys/net/ipv4/ip_local_port_range" #endif Index: util/configlexer.lex =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/configlexer.lex,v diff -u -p -r1.31 configlexer.lex --- util/configlexer.lex 4 Sep 2024 09:36:41 -0000 1.31 +++ util/configlexer.lex 7 Feb 2025 21:25:44 -0000 @@ -269,6 +269,8 @@ http-query-buffer-size{COLON} { YDVAR(1, http-response-buffer-size{COLON} { YDVAR(1, VAR_HTTP_RESPONSE_BUFFER_SIZE) } http-nodelay{COLON} { YDVAR(1, VAR_HTTP_NODELAY) } http-notls-downstream{COLON} { YDVAR(1, VAR_HTTP_NOTLS_DOWNSTREAM) } +quic-port{COLON} { YDVAR(1, VAR_QUIC_PORT) } +quic-size{COLON} { YDVAR(1, VAR_QUIC_SIZE) } use-systemd{COLON} { YDVAR(1, VAR_USE_SYSTEMD) } do-daemonize{COLON} { YDVAR(1, VAR_DO_DAEMONIZE) } interface{COLON} { YDVAR(1, VAR_INTERFACE) } @@ -315,6 +317,7 @@ target-fetch-policy{COLON} { YDVAR(1, VA harden-short-bufsize{COLON} { YDVAR(1, VAR_HARDEN_SHORT_BUFSIZE) } harden-large-queries{COLON} { YDVAR(1, VAR_HARDEN_LARGE_QUERIES) } harden-glue{COLON} { YDVAR(1, VAR_HARDEN_GLUE) } +harden-unverified-glue{COLON} { YDVAR(1, VAR_HARDEN_UNVERIFIED_GLUE) } harden-dnssec-stripped{COLON} { YDVAR(1, VAR_HARDEN_DNSSEC_STRIPPED) } harden-below-nxdomain{COLON} { YDVAR(1, VAR_HARDEN_BELOW_NXDOMAIN) } harden-referral-path{COLON} { YDVAR(1, VAR_HARDEN_REFERRAL_PATH) } @@ -430,6 +433,7 @@ permit-small-holddown{COLON} { YDVAR(1, use-syslog{COLON} { YDVAR(1, VAR_USE_SYSLOG) } log-identity{COLON} { YDVAR(1, VAR_LOG_IDENTITY) } log-time-ascii{COLON} { YDVAR(1, VAR_LOG_TIME_ASCII) } +log-time-iso{COLON} { YDVAR(1, VAR_LOG_TIME_ISO) } log-queries{COLON} { YDVAR(1, VAR_LOG_QUERIES) } log-replies{COLON} { YDVAR(1, VAR_LOG_REPLIES) } log-tag-queryreply{COLON} { YDVAR(1, VAR_LOG_TAG_QUERYREPLY) } @@ -513,7 +517,7 @@ dnstap-log-forwarder-query-messages{COLO YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES) } dnstap-log-forwarder-response-messages{COLON} { YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES) } -dnstap-sample-rate { YDVAR(1, VAR_DNSTAP_SAMPLE_RATE) } +dnstap-sample-rate{COLON} { YDVAR(1, VAR_DNSTAP_SAMPLE_RATE) } disable-dnssec-lame-check{COLON} { YDVAR(1, VAR_DISABLE_DNSSEC_LAME_CHECK) } ip-ratelimit{COLON} { YDVAR(1, VAR_IP_RATELIMIT) } ip-ratelimit-cookie{COLON} { YDVAR(1, VAR_IP_RATELIMIT_COOKIE) } @@ -573,6 +577,8 @@ redis-server-port{COLON} { YDVAR(1, VAR_ redis-server-path{COLON} { YDVAR(1, VAR_CACHEDB_REDISPATH) } redis-server-password{COLON} { YDVAR(1, VAR_CACHEDB_REDISPASSWORD) } redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) } +redis-command-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISCOMMANDTIMEOUT) } +redis-connect-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISCONNECTTIMEOUT) } redis-expire-records{COLON} { YDVAR(1, VAR_CACHEDB_REDISEXPIRERECORDS) } redis-logical-db{COLON} { YDVAR(1, VAR_CACHEDB_REDISLOGICALDB) } ipset{COLON} { YDVAR(0, VAR_IPSET) } @@ -588,6 +594,9 @@ edns-client-string-opcode{COLON} { YDVAR nsid{COLON} { YDVAR(1, VAR_NSID ) } ede{COLON} { YDVAR(1, VAR_EDE ) } proxy-protocol-port{COLON} { YDVAR(1, VAR_PROXY_PROTOCOL_PORT) } +iter-scrub-ns{COLON} { YDVAR(1, VAR_ITER_SCRUB_NS) } +iter-scrub-cname{COLON} { YDVAR(1, VAR_ITER_SCRUB_CNAME) } +max-global-quota{COLON} { YDVAR(1, VAR_MAX_GLOBAL_QUOTA) } {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; } /* Quoted strings. Strip leading and ending quotes */ Index: util/configparser.y =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/configparser.y,v diff -u -p -r1.32 configparser.y --- util/configparser.y 7 Feb 2025 21:22:23 -0000 1.32 +++ util/configparser.y 7 Feb 2025 21:25:44 -0000 @@ -182,6 +182,7 @@ extern struct config_parser_state* cfg_p %token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT %token VAR_CACHEDB_REDISEXPIRERECORDS VAR_CACHEDB_REDISPATH VAR_CACHEDB_REDISPASSWORD %token VAR_CACHEDB_REDISLOGICALDB +%token VAR_CACHEDB_REDISCOMMANDTIMEOUT VAR_CACHEDB_REDISCONNECTTIMEOUT %token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM %token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM %token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL @@ -202,16 +203,18 @@ extern struct config_parser_state* cfg_p %token VAR_RPZ_SIGNAL_NXDOMAIN_RA VAR_INTERFACE_AUTOMATIC_PORTS VAR_EDE %token VAR_INTERFACE_ACTION VAR_INTERFACE_VIEW VAR_INTERFACE_TAG %token VAR_INTERFACE_TAG_ACTION VAR_INTERFACE_TAG_DATA +%token VAR_QUIC_PORT VAR_QUIC_SIZE %token VAR_PROXY_PROTOCOL_PORT VAR_STATISTICS_INHIBIT_ZERO %token VAR_HARDEN_UNKNOWN_ADDITIONAL VAR_DISABLE_EDNS_DO VAR_CACHEDB_NO_STORE %token VAR_LOG_DESTADDR VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED -%token VAR_COOKIE_SECRET_FILE +%token VAR_COOKIE_SECRET_FILE VAR_ITER_SCRUB_NS VAR_ITER_SCRUB_CNAME +%token VAR_MAX_GLOBAL_QUOTA VAR_HARDEN_UNVERIFIED_GLUE VAR_LOG_TIME_ISO %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; -toplevelvar: serverstart contents_server | stubstart contents_stub | - forwardstart contents_forward | pythonstart contents_py | - rcstart contents_rc | dtstart contents_dt | viewstart contents_view | +toplevelvar: serverstart contents_server | stub_clause | + forward_clause | pythonstart contents_py | + rcstart contents_rc | dtstart contents_dt | view_clause | dnscstart contents_dnsc | cachedbstart contents_cachedb | ipsetstart contents_ipset | authstart contents_auth | rpzstart contents_rpz | dynlibstart contents_dl | @@ -340,10 +343,21 @@ content_server: server_num_threads | ser server_edns_client_string_opcode | server_nsid | server_zonemd_permissive_mode | server_max_reuse_tcp_queries | server_tcp_reuse_timeout | server_tcp_auth_query_timeout | + server_quic_port | server_quic_size | server_interface_automatic_ports | server_ede | server_proxy_protocol_port | server_statistics_inhibit_zero | server_harden_unknown_additional | server_disable_edns_do | - server_log_destaddr | server_cookie_secret_file + server_log_destaddr | server_cookie_secret_file | + server_iter_scrub_ns | server_iter_scrub_cname | server_max_global_quota | + server_harden_unverified_glue | server_log_time_iso + ; +stub_clause: stubstart contents_stub + { + /* stub end */ + if(cfg_parser->cfg->stubs && + !cfg_parser->cfg->stubs->name) + yyerror("stub-zone without name"); + } ; stubstart: VAR_STUB_ZONE { @@ -359,17 +373,19 @@ stubstart: VAR_STUB_ZONE } } ; -contents_stub: content_stub contents_stub - | - { - /* stub end */ - if(cfg_parser->cfg->stubs && - !cfg_parser->cfg->stubs->name) - yyerror("stub-zone without name"); - }; +contents_stub: contents_stub content_stub + | ; content_stub: stub_name | stub_host | stub_addr | stub_prime | stub_first | stub_no_cache | stub_ssl_upstream | stub_tcp_upstream ; +forward_clause: forwardstart contents_forward + { + /* forward end */ + if(cfg_parser->cfg->forwards && + !cfg_parser->cfg->forwards->name) + yyerror("forward-zone without name"); + } + ; forwardstart: VAR_FORWARD_ZONE { struct config_stub* s; @@ -384,17 +400,19 @@ forwardstart: VAR_FORWARD_ZONE } } ; -contents_forward: content_forward contents_forward - | - { - /* forward end */ - if(cfg_parser->cfg->forwards && - !cfg_parser->cfg->forwards->name) - yyerror("forward-zone without name"); - }; +contents_forward: contents_forward content_forward + | ; content_forward: forward_name | forward_host | forward_addr | forward_first | forward_no_cache | forward_ssl_upstream | forward_tcp_upstream ; +view_clause: viewstart contents_view + { + /* view end */ + if(cfg_parser->cfg->views && + !cfg_parser->cfg->views->name) + yyerror("view without name"); + } + ; viewstart: VAR_VIEW { struct config_view* s; @@ -409,14 +427,8 @@ viewstart: VAR_VIEW } } ; -contents_view: content_view contents_view - | - { - /* view end */ - if(cfg_parser->cfg->views && - !cfg_parser->cfg->views->name) - yyerror("view without name"); - }; +contents_view: contents_view content_view + | ; content_view: view_name | view_local_zone | view_local_data | view_first | view_response_ip | view_response_ip_data | view_local_data_ptr ; @@ -1199,6 +1211,26 @@ server_http_notls_downstream: VAR_HTTP_N else cfg_parser->cfg->http_notls_downstream = (strcmp($2, "yes")==0); free($2); }; +server_quic_port: VAR_QUIC_PORT STRING_ARG + { + OUTYY(("P(server_quic_port:%s)\n", $2)); +#ifndef HAVE_NGTCP2 + log_warn("%s:%d: Unbound is not compiled with " + "ngtcp2. This is required to use DNS " + "over QUIC.", cfg_parser->filename, cfg_parser->line); +#endif + if(atoi($2) == 0) + yyerror("port number expected"); + else cfg_parser->cfg->quic_port = atoi($2); + free($2); + }; +server_quic_size: VAR_QUIC_SIZE STRING_ARG + { + OUTYY(("P(server_quic_size:%s)\n", $2)); + if(!cfg_parse_memsize($2, &cfg_parser->cfg->quic_size)) + yyerror("memory size expected"); + free($2); + }; server_use_systemd: VAR_USE_SYSTEMD STRING_ARG { OUTYY(("P(server_use_systemd:%s)\n", $2)); @@ -1240,6 +1272,15 @@ server_log_time_ascii: VAR_LOG_TIME_ASCI free($2); } ; +server_log_time_iso: VAR_LOG_TIME_ISO STRING_ARG + { + OUTYY(("P(server_log_time_iso:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->log_time_iso = (strcmp($2, "yes")==0); + free($2); + } + ; server_log_queries: VAR_LOG_QUERIES STRING_ARG { OUTYY(("P(server_log_queries:%s)\n", $2)); @@ -1805,6 +1846,16 @@ server_harden_glue: VAR_HARDEN_GLUE STRI free($2); } ; +server_harden_unverified_glue: VAR_HARDEN_UNVERIFIED_GLUE STRING_ARG + { + OUTYY(("P(server_harden_unverified_glue:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->harden_unverified_glue = + (strcmp($2, "yes")==0); + free($2); + } + ; server_harden_dnssec_stripped: VAR_HARDEN_DNSSEC_STRIPPED STRING_ARG { OUTYY(("P(server_harden_dnssec_stripped:%s)\n", $2)); @@ -3819,7 +3870,8 @@ contents_cachedb: contents_cachedb conte content_cachedb: cachedb_backend_name | cachedb_secret_seed | redis_server_host | redis_server_port | redis_timeout | redis_expire_records | redis_server_path | redis_server_password | - cachedb_no_store | redis_logical_db | cachedb_check_when_serve_expired + cachedb_no_store | redis_logical_db | cachedb_check_when_serve_expired | + redis_command_timeout | redis_connect_timeout ; cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG { @@ -3935,6 +3987,32 @@ redis_timeout: VAR_CACHEDB_REDISTIMEOUT free($2); } ; +redis_command_timeout: VAR_CACHEDB_REDISCOMMANDTIMEOUT STRING_ARG + { + #if defined(USE_CACHEDB) && defined(USE_REDIS) + OUTYY(("P(redis_command_timeout:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("redis command timeout value expected"); + else cfg_parser->cfg->redis_command_timeout = atoi($2); + #else + OUTYY(("P(Compiled without cachedb or redis, ignoring)\n")); + #endif + free($2); + } + ; +redis_connect_timeout: VAR_CACHEDB_REDISCONNECTTIMEOUT STRING_ARG + { + #if defined(USE_CACHEDB) && defined(USE_REDIS) + OUTYY(("P(redis_connect_timeout:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("redis connect timeout value expected"); + else cfg_parser->cfg->redis_connect_timeout = atoi($2); + #else + OUTYY(("P(Compiled without cachedb or redis, ignoring)\n")); + #endif + free($2); + } + ; redis_expire_records: VAR_CACHEDB_REDISEXPIRERECORDS STRING_ARG { #if defined(USE_CACHEDB) && defined(USE_REDIS) @@ -4006,45 +4084,45 @@ server_cookie_secret_file: VAR_COOKIE_SE cfg_parser->cfg->cookie_secret_file = $2; } ; -ipsetstart: VAR_IPSET - { - OUTYY(("\nP(ipset:)\n")); - cfg_parser->started_toplevel = 1; - } - ; -contents_ipset: contents_ipset content_ipset - | ; -content_ipset: ipset_name_v4 | ipset_name_v6 - ; -ipset_name_v4: VAR_IPSET_NAME_V4 STRING_ARG - { - #ifdef USE_IPSET - OUTYY(("P(name-v4:%s)\n", $2)); - if(cfg_parser->cfg->ipset_name_v4) - yyerror("ipset name v4 override, there must be one " - "name for ip v4"); - free(cfg_parser->cfg->ipset_name_v4); - cfg_parser->cfg->ipset_name_v4 = $2; - #else - OUTYY(("P(Compiled without ipset, ignoring)\n")); - free($2); - #endif - } + ipsetstart: VAR_IPSET + { + OUTYY(("\nP(ipset:)\n")); + cfg_parser->started_toplevel = 1; + } + ; + contents_ipset: contents_ipset content_ipset + | ; + content_ipset: ipset_name_v4 | ipset_name_v6 + ; + ipset_name_v4: VAR_IPSET_NAME_V4 STRING_ARG + { + #ifdef USE_IPSET + OUTYY(("P(name-v4:%s)\n", $2)); + if(cfg_parser->cfg->ipset_name_v4) + yyerror("ipset name v4 override, there must be one " + "name for ip v4"); + free(cfg_parser->cfg->ipset_name_v4); + cfg_parser->cfg->ipset_name_v4 = $2; + #else + OUTYY(("P(Compiled without ipset, ignoring)\n")); + free($2); + #endif + } ; -ipset_name_v6: VAR_IPSET_NAME_V6 STRING_ARG + ipset_name_v6: VAR_IPSET_NAME_V6 STRING_ARG { - #ifdef USE_IPSET - OUTYY(("P(name-v6:%s)\n", $2)); - if(cfg_parser->cfg->ipset_name_v6) - yyerror("ipset name v6 override, there must be one " - "name for ip v6"); - free(cfg_parser->cfg->ipset_name_v6); - cfg_parser->cfg->ipset_name_v6 = $2; - #else - OUTYY(("P(Compiled without ipset, ignoring)\n")); - free($2); - #endif - } + #ifdef USE_IPSET + OUTYY(("P(name-v6:%s)\n", $2)); + if(cfg_parser->cfg->ipset_name_v6) + yyerror("ipset name v6 override, there must be one " + "name for ip v6"); + free(cfg_parser->cfg->ipset_name_v6); + cfg_parser->cfg->ipset_name_v6 = $2; + #else + OUTYY(("P(Compiled without ipset, ignoring)\n")); + free($2); + #endif + } ; %% Index: util/fptr_wlist.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/fptr_wlist.c,v diff -u -p -r1.27 fptr_wlist.c --- util/fptr_wlist.c 4 Sep 2024 09:36:41 -0000 1.27 +++ util/fptr_wlist.c 7 Feb 2025 21:25:44 -0000 @@ -47,6 +47,7 @@ #include "util/fptr_wlist.h" #include "util/mini_event.h" #include "services/outside_network.h" +#include "services/listen_dnsport.h" #include "services/mesh.h" #include "services/localzone.h" #include "services/authzone.h" @@ -132,6 +133,9 @@ fptr_whitelist_comm_timer(void (*fptr)(v else if(fptr == &worker_stat_timer_cb) return 1; else if(fptr == &worker_probe_timer_cb) return 1; else if(fptr == &validate_suspend_timer_cb) return 1; +#ifdef HAVE_NGTCP2 + else if(fptr == &doq_timer_cb) return 1; +#endif #ifdef UB_ON_WINDOWS else if(fptr == &wsvc_cron_cb) return 1; #endif @@ -181,6 +185,9 @@ fptr_whitelist_event(void (*fptr)(int, s else if(fptr == &tube_handle_signal) return 1; else if(fptr == &comm_base_handle_slow_accept) return 1; else if(fptr == &comm_point_http_handle_callback) return 1; +#ifdef HAVE_NGTCP2 + else if(fptr == &comm_point_doq_callback) return 1; +#endif #ifdef USE_DNSTAP else if(fptr == &dtio_output_cb) return 1; else if(fptr == &dtio_cmd_cb) return 1; @@ -190,6 +197,10 @@ fptr_whitelist_event(void (*fptr)(int, s else if(fptr == &dtio_tap_callback) return 1; else if(fptr == &dtio_mainfdcallback) return 1; #endif +#ifdef HAVE_NGTCP2 + else if(fptr == &doq_client_event_cb) return 1; + else if(fptr == &doq_client_timer_cb) return 1; +#endif #ifdef UB_ON_WINDOWS else if(fptr == &worker_win_stop_cb) return 1; #endif @@ -248,6 +259,12 @@ fptr_whitelist_rbtree_cmp(int (*fptr) (c else if(fptr == &auth_zone_cmp) return 1; else if(fptr == &auth_data_cmp) return 1; else if(fptr == &auth_xfer_cmp) return 1; +#ifdef HAVE_NGTCP2 + else if(fptr == &doq_conn_cmp) return 1; + else if(fptr == &doq_conid_cmp) return 1; + else if(fptr == &doq_timer_cmp) return 1; + else if(fptr == &doq_stream_cmp) return 1; +#endif return 0; } Index: util/locks.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/locks.h,v diff -u -p -r1.2 locks.h --- util/locks.h 17 Feb 2017 18:53:32 -0000 1.2 +++ util/locks.h 7 Feb 2025 21:25:44 -0000 @@ -88,6 +88,7 @@ #define lock_get_mem(lock) (0) /* nothing */ #define checklock_start() /* nop */ #define checklock_stop() /* nop */ +#define checklock_set_output_name(name) /* nop */ #ifdef HAVE_PTHREAD #include Index: util/log.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/log.c,v diff -u -p -r1.9 log.c --- util/log.c 12 Apr 2024 15:45:24 -0000 1.9 +++ util/log.c 7 Feb 2025 21:25:44 -0000 @@ -45,6 +45,7 @@ #ifdef HAVE_TIME_H #include #endif +#include #ifdef HAVE_SYSLOG_H # include #else @@ -81,6 +82,8 @@ static int logging_to_syslog = 0; #endif /* HAVE_SYSLOG_H */ /** print time in UTC or in secondsfrom1970 */ static int log_time_asc = 0; +/** print time in iso format */ +static int log_time_iso = 0; void log_init(const char* filename, int use_syslog, const char* chrootdir) @@ -205,6 +208,11 @@ void log_set_time_asc(int use_asc) log_time_asc = use_asc; } +void log_set_time_iso(int use_iso) +{ + log_time_iso = use_iso; +} + void* log_get_lock(void) { if(!key_created) @@ -269,6 +277,34 @@ log_vmsg(int pri, const char* type, lock_basic_unlock(&log_lock); return; } +#if defined(HAVE_STRFTIME) && defined(HAVE_LOCALTIME_R) + if(log_time_iso && log_time_asc) { + char tzbuf[16]; + struct timeval tv; + struct tm tm, *tm_p; + if(gettimeofday(&tv, NULL) < 0) + memset(&tv, 0, sizeof(tv)); + now = (time_t)tv.tv_sec; + tm_p = localtime_r(&now, &tm); + strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%dT%H:%M:%S", tm_p); + if(strftime(tzbuf, sizeof(tzbuf), "%z", tm_p) == 5) { + /* put ':' in "+hh:mm" */ + tzbuf[5] = tzbuf[4]; + tzbuf[4] = tzbuf[3]; + tzbuf[3] = ':'; + tzbuf[6] = 0; + } + fprintf(logfile, "%s.%3.3d%s %s[%d:%x] %s: %s\n", + tmbuf, (int)tv.tv_usec/1000, tzbuf, + ident, (int)getpid(), tid?*tid:0, type, message); +#ifdef UB_ON_WINDOWS + /* line buffering does not work on windows */ + fflush(logfile); +#endif + lock_basic_unlock(&log_lock); + return; + } +#endif /* HAVE_STRFTIME && HAVE_LOCALTIME_R */ now = (time_t)time(NULL); #if defined(HAVE_STRFTIME) && defined(HAVE_LOCALTIME_R) if(log_time_asc && strftime(tmbuf, sizeof(tmbuf), "%b %d %H:%M:%S", Index: util/log.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/log.h,v diff -u -p -r1.7 log.h --- util/log.h 24 Aug 2020 09:41:53 -0000 1.7 +++ util/log.h 7 Feb 2025 21:25:44 -0000 @@ -138,6 +138,12 @@ void log_ident_set_or_default(const char */ void log_set_time_asc(int use_asc); +/** + * Set if the time value is printed in ISO8601 format. + * @param use_iso: if true, ascii timestamps are formatted in iso format. + */ +void log_set_time_iso(int use_iso); + /** get log lock */ void* log_get_lock(void); Index: util/module.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/module.h,v diff -u -p -r1.19 module.h --- util/module.h 4 Sep 2024 09:36:41 -0000 1.19 +++ util/module.h 7 Feb 2025 21:25:44 -0000 @@ -319,13 +319,15 @@ typedef int inplace_cb_query_response_fu /** * Function called when looking for (expired) cached answers during the serve * expired logic. - * Called as func(qstate, lookup_qinfo) + * Called as func(qstate, lookup_qinfo, &is_expired) * Where: * qstate: the query state. * lookup_qinfo: the qinfo to lookup for. + * is_expired: set if the cached answer is expired. */ typedef struct dns_msg* serve_expired_lookup_func_type( - struct module_qstate* qstate, struct query_info* lookup_qinfo); + struct module_qstate* qstate, struct query_info* lookup_qinfo, + int* is_expired); /** * Module environment. @@ -696,6 +698,8 @@ struct module_qstate { /** Extended result of response-ip action processing, mainly * for logging purposes. */ struct respip_action_info* respip_action_info; + /** if the query has been modified by rpz processing. */ + int rpz_applied; /** if the query is rpz passthru, no further rpz processing for it */ int rpz_passthru; /* Flag tcp required. */ Index: util/netevent.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/netevent.c,v diff -u -p -r1.39 netevent.c --- util/netevent.c 4 Sep 2024 09:36:41 -0000 1.39 +++ util/netevent.c 7 Feb 2025 21:25:44 -0000 @@ -53,6 +53,7 @@ #include "dnstap/dnstap.h" #include "dnscrypt/dnscrypt.h" #include "services/listen_dnsport.h" +#include "util/random.h" #ifdef HAVE_SYS_TYPES_H #include #endif @@ -72,9 +73,16 @@ #ifdef HAVE_OPENSSL_ERR_H #include #endif + +#ifdef HAVE_NGTCP2 +#include +#include +#endif + #ifdef HAVE_LINUX_NET_TSTAMP_H #include #endif + /* -------- Start of local definitions -------- */ /** if CMSG_ALIGN is not defined on this platform, a workaround */ #ifndef CMSG_ALIGN @@ -1053,112 +1061,1880 @@ comm_point_udp_ancil_callback(int fd, sh } } - if(verbosity >= VERB_ALGO && rep.srctype != 0) - p_ancil("receive_udp on interface", &rep); -#endif /* S_SPLINT_S */ + if(verbosity >= VERB_ALGO && rep.srctype != 0) + p_ancil("receive_udp on interface", &rep); +#endif /* S_SPLINT_S */ + + if(rep.c->pp2_enabled && !consume_pp2_header(rep.c->buffer, + &rep, 0)) { + log_err("proxy_protocol: could not consume PROXYv2 header"); + return; + } + if(!rep.is_proxied) { + rep.client_addrlen = rep.remote_addrlen; + memmove(&rep.client_addr, &rep.remote_addr, + rep.remote_addrlen); + } + + fptr_ok(fptr_whitelist_comm_point(rep.c->callback)); + if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) { + /* send back immediate reply */ + struct sldns_buffer *buffer; +#ifdef USE_DNSCRYPT + buffer = rep.c->dnscrypt_buffer; +#else + buffer = rep.c->buffer; +#endif + (void)comm_point_send_udp_msg_if(rep.c, buffer, + (struct sockaddr*)&rep.remote_addr, + rep.remote_addrlen, &rep); + } + if(!rep.c || rep.c->fd == -1) /* commpoint closed */ + break; + } +} +#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */ + +void +comm_point_udp_callback(int fd, short event, void* arg) +{ + struct comm_reply rep; + ssize_t rcv; + int i; + struct sldns_buffer *buffer; + + rep.c = (struct comm_point*)arg; + log_assert(rep.c->type == comm_udp); + + if(!(event&UB_EV_READ)) + return; + log_assert(rep.c && rep.c->buffer && rep.c->fd == fd); + ub_comm_base_now(rep.c->ev->base); + for(i=0; ibuffer); + rep.remote_addrlen = (socklen_t)sizeof(rep.remote_addr); + log_assert(fd != -1); + log_assert(sldns_buffer_remaining(rep.c->buffer) > 0); + rcv = recvfrom(fd, (void*)sldns_buffer_begin(rep.c->buffer), + sldns_buffer_remaining(rep.c->buffer), MSG_DONTWAIT, + (struct sockaddr*)&rep.remote_addr, &rep.remote_addrlen); + if(rcv == -1) { +#ifndef USE_WINSOCK + if(errno != EAGAIN && errno != EINTR + && udp_recv_needs_log(errno)) + log_err("recvfrom %d failed: %s", + fd, strerror(errno)); +#else + if(WSAGetLastError() != WSAEINPROGRESS && + WSAGetLastError() != WSAECONNRESET && + WSAGetLastError()!= WSAEWOULDBLOCK && + udp_recv_needs_log(WSAGetLastError())) + log_err("recvfrom failed: %s", + wsa_strerror(WSAGetLastError())); +#endif + return; + } + sldns_buffer_skip(rep.c->buffer, rcv); + sldns_buffer_flip(rep.c->buffer); + rep.srctype = 0; + rep.is_proxied = 0; + + if(rep.c->pp2_enabled && !consume_pp2_header(rep.c->buffer, + &rep, 0)) { + log_err("proxy_protocol: could not consume PROXYv2 header"); + return; + } + if(!rep.is_proxied) { + rep.client_addrlen = rep.remote_addrlen; + memmove(&rep.client_addr, &rep.remote_addr, + rep.remote_addrlen); + } + + fptr_ok(fptr_whitelist_comm_point(rep.c->callback)); + if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) { + /* send back immediate reply */ +#ifdef USE_DNSCRYPT + buffer = rep.c->dnscrypt_buffer; +#else + buffer = rep.c->buffer; +#endif + (void)comm_point_send_udp_msg(rep.c, buffer, + (struct sockaddr*)&rep.remote_addr, + rep.remote_addrlen, 0); + } + if(!rep.c || rep.c->fd != fd) /* commpoint closed to -1 or reused for + another UDP port. Note rep.c cannot be reused with TCP fd. */ + break; + } +} + +#ifdef HAVE_NGTCP2 +void +doq_pkt_addr_init(struct doq_pkt_addr* paddr) +{ + paddr->addrlen = (socklen_t)sizeof(paddr->addr); + paddr->localaddrlen = (socklen_t)sizeof(paddr->localaddr); + paddr->ifindex = 0; +} + +/** set the ecn on the transmission */ +static void +doq_set_ecn(int fd, int family, uint32_t ecn) +{ + unsigned int val = ecn; + if(family == AF_INET6) { + if(setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, + (socklen_t)sizeof(val)) == -1) { + log_err("setsockopt(.. IPV6_TCLASS ..): %s", + strerror(errno)); + } + return; + } + if(setsockopt(fd, IPPROTO_IP, IP_TOS, &val, + (socklen_t)sizeof(val)) == -1) { + log_err("setsockopt(.. IP_TOS ..): %s", + strerror(errno)); + } +} + +/** set the local address in the control ancillary data */ +static void +doq_set_localaddr_cmsg(struct msghdr* msg, size_t control_size, + struct doq_addr_storage* localaddr, socklen_t localaddrlen, + int ifindex) +{ +#ifndef S_SPLINT_S + struct cmsghdr* cmsg; +#endif /* S_SPLINT_S */ +#ifndef S_SPLINT_S + cmsg = CMSG_FIRSTHDR(msg); + if(localaddr->sockaddr.in.sin_family == AF_INET) { +#ifdef IP_PKTINFO + struct sockaddr_in* sa = (struct sockaddr_in*)localaddr; + struct in_pktinfo v4info; + log_assert(localaddrlen >= sizeof(struct sockaddr_in)); + msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); + memset(msg->msg_control, 0, msg->msg_controllen); + log_assert(msg->msg_controllen <= control_size); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + memset(&v4info, 0, sizeof(v4info)); +# ifdef HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST + memmove(&v4info.ipi_spec_dst, &sa->sin_addr, + sizeof(struct in_addr)); +# else + memmove(&v4info.ipi_addr, &sa->sin_addr, + sizeof(struct in_addr)); +# endif + v4info.ipi_ifindex = ifindex; + memmove(CMSG_DATA(cmsg), &v4info, sizeof(struct in_pktinfo)); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); +#elif defined(IP_SENDSRCADDR) + struct sockaddr_in* sa= (struct sockaddr_in*)localaddr; + log_assert(localaddrlen >= sizeof(struct sockaddr_in)); + msg->msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); + memset(msg->msg_control, 0, msg->msg_controllen); + log_assert(msg->msg_controllen <= control_size); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + memmove(CMSG_DATA(cmsg), &sa->sin_addr, + sizeof(struct in_addr)); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); +#endif + } else { + struct sockaddr_in6* sa6 = (struct sockaddr_in6*)localaddr; + struct in6_pktinfo v6info; + log_assert(localaddrlen >= sizeof(struct sockaddr_in6)); + msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); + memset(msg->msg_control, 0, msg->msg_controllen); + log_assert(msg->msg_controllen <= control_size); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + memset(&v6info, 0, sizeof(v6info)); + memmove(&v6info.ipi6_addr, &sa6->sin6_addr, + sizeof(struct in6_addr)); + v6info.ipi6_ifindex = ifindex; + memmove(CMSG_DATA(cmsg), &v6info, sizeof(struct in6_pktinfo)); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + } +#endif /* S_SPLINT_S */ + /* Ignore unused variables, if no assertions are compiled. */ + (void)localaddrlen; + (void)control_size; +} + +/** write address and port into strings */ +static int +doq_print_addr_port(struct doq_addr_storage* addr, socklen_t addrlen, + char* host, size_t hostlen, char* port, size_t portlen) +{ + if(addr->sockaddr.in.sin_family == AF_INET) { + struct sockaddr_in* sa = (struct sockaddr_in*)addr; + log_assert(addrlen >= sizeof(*sa)); + if(inet_ntop(sa->sin_family, &sa->sin_addr, host, + (socklen_t)hostlen) == 0) { + log_hex("inet_ntop error: address", &sa->sin_addr, + sizeof(sa->sin_addr)); + return 0; + } + snprintf(port, portlen, "%u", (unsigned)ntohs(sa->sin_port)); + } else if(addr->sockaddr.in.sin_family == AF_INET6) { + struct sockaddr_in6* sa6 = (struct sockaddr_in6*)addr; + log_assert(addrlen >= sizeof(*sa6)); + if(inet_ntop(sa6->sin6_family, &sa6->sin6_addr, host, + (socklen_t)hostlen) == 0) { + log_hex("inet_ntop error: address", &sa6->sin6_addr, + sizeof(sa6->sin6_addr)); + return 0; + } + snprintf(port, portlen, "%u", (unsigned)ntohs(sa6->sin6_port)); + } + return 1; +} + +/** doq store the blocked packet when write has blocked */ +static void +doq_store_blocked_pkt(struct comm_point* c, struct doq_pkt_addr* paddr, + uint32_t ecn) +{ + if(c->doq_socket->have_blocked_pkt) + return; /* should not happen that we write when there is + already a blocked write, but if so, drop it. */ + if(sldns_buffer_limit(c->doq_socket->pkt_buf) > + sldns_buffer_capacity(c->doq_socket->blocked_pkt)) + return; /* impossibly large, drop packet. impossible because + pkt_buf and blocked_pkt are the same size. */ + c->doq_socket->have_blocked_pkt = 1; + c->doq_socket->blocked_pkt_pi.ecn = ecn; + memcpy(c->doq_socket->blocked_paddr, paddr, + sizeof(*c->doq_socket->blocked_paddr)); + sldns_buffer_clear(c->doq_socket->blocked_pkt); + sldns_buffer_write(c->doq_socket->blocked_pkt, + sldns_buffer_begin(c->doq_socket->pkt_buf), + sldns_buffer_limit(c->doq_socket->pkt_buf)); + sldns_buffer_flip(c->doq_socket->blocked_pkt); +} + +void +doq_send_pkt(struct comm_point* c, struct doq_pkt_addr* paddr, uint32_t ecn) +{ + struct msghdr msg; + struct iovec iov[1]; + union { + struct cmsghdr hdr; + char buf[256]; + } control; + ssize_t ret; + iov[0].iov_base = sldns_buffer_begin(c->doq_socket->pkt_buf); + iov[0].iov_len = sldns_buffer_limit(c->doq_socket->pkt_buf); + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void*)&paddr->addr; + msg.msg_namelen = paddr->addrlen; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = control.buf; +#ifndef S_SPLINT_S + msg.msg_controllen = sizeof(control.buf); +#endif /* S_SPLINT_S */ + msg.msg_flags = 0; + + doq_set_localaddr_cmsg(&msg, sizeof(control.buf), &paddr->localaddr, + paddr->localaddrlen, paddr->ifindex); + doq_set_ecn(c->fd, paddr->addr.sockaddr.in.sin_family, ecn); + + for(;;) { + ret = sendmsg(c->fd, &msg, MSG_DONTWAIT); + if(ret == -1 && errno == EINTR) + continue; + break; + } + if(ret == -1) { +#ifndef USE_WINSOCK + if(errno == EAGAIN || +# ifdef EWOULDBLOCK + errno == EWOULDBLOCK || +# endif + errno == ENOBUFS) +#else + if(WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAENOBUFS || + WSAGetLastError() == WSAEWOULDBLOCK) +#endif + { + /* udp send has blocked */ + doq_store_blocked_pkt(c, paddr, ecn); + return; + } + if(!udp_send_errno_needs_log((void*)&paddr->addr, + paddr->addrlen)) + return; + if(verbosity >= VERB_OPS) { + char host[256], port[32]; + if(doq_print_addr_port(&paddr->addr, paddr->addrlen, + host, sizeof(host), port, sizeof(port))) { + verbose(VERB_OPS, "doq sendmsg to %s %s " + "failed: %s", host, port, + strerror(errno)); + } else { + verbose(VERB_OPS, "doq sendmsg failed: %s", + strerror(errno)); + } + } + return; + } else if(ret != (ssize_t)sldns_buffer_limit(c->doq_socket->pkt_buf)) { + char host[256], port[32]; + if(doq_print_addr_port(&paddr->addr, paddr->addrlen, host, + sizeof(host), port, sizeof(port))) { + log_err("doq sendmsg to %s %s failed: " + "sent %d in place of %d bytes", + host, port, (int)ret, + (int)sldns_buffer_limit(c->doq_socket->pkt_buf)); + } else { + log_err("doq sendmsg failed: " + "sent %d in place of %d bytes", + (int)ret, (int)sldns_buffer_limit(c->doq_socket->pkt_buf)); + } + return; + } +} + +/** fetch port number */ +static int +doq_sockaddr_get_port(struct doq_addr_storage* addr) +{ + if(addr->sockaddr.in.sin_family == AF_INET) { + struct sockaddr_in* sa = (struct sockaddr_in*)addr; + return ntohs(sa->sin_port); + } else if(addr->sockaddr.in.sin_family == AF_INET6) { + struct sockaddr_in6* sa6 = (struct sockaddr_in6*)addr; + return ntohs(sa6->sin6_port); + } + return 0; +} + +/** get local address from ancillary data headers */ +static int +doq_get_localaddr_cmsg(struct comm_point* c, struct doq_pkt_addr* paddr, + int* pkt_continue, struct msghdr* msg) +{ +#ifndef S_SPLINT_S + struct cmsghdr* cmsg; +#endif /* S_SPLINT_S */ + + memset(&paddr->localaddr, 0, sizeof(paddr->localaddr)); +#ifndef S_SPLINT_S + for(cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + if( cmsg->cmsg_level == IPPROTO_IPV6 && + cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo* v6info = + (struct in6_pktinfo*)CMSG_DATA(cmsg); + struct sockaddr_in6* sa= (struct sockaddr_in6*) + &paddr->localaddr; + struct sockaddr_in6* rema = (struct sockaddr_in6*) + &paddr->addr; + if(rema->sin6_family != AF_INET6) { + log_err("doq cmsg family mismatch cmsg is ip6"); + *pkt_continue = 1; + return 0; + } + sa->sin6_family = AF_INET6; + sa->sin6_port = htons(doq_sockaddr_get_port( + (void*)c->socket->addr)); + paddr->ifindex = v6info->ipi6_ifindex; + memmove(&sa->sin6_addr, &v6info->ipi6_addr, + sizeof(struct in6_addr)); + paddr->localaddrlen = sizeof(struct sockaddr_in6); + break; +#ifdef IP_PKTINFO + } else if( cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo* v4info = + (struct in_pktinfo*)CMSG_DATA(cmsg); + struct sockaddr_in* sa= (struct sockaddr_in*) + &paddr->localaddr; + struct sockaddr_in* rema = (struct sockaddr_in*) + &paddr->addr; + if(rema->sin_family != AF_INET) { + log_err("doq cmsg family mismatch cmsg is ip4"); + *pkt_continue = 1; + return 0; + } + sa->sin_family = AF_INET; + sa->sin_port = htons(doq_sockaddr_get_port( + (void*)c->socket->addr)); + paddr->ifindex = v4info->ipi_ifindex; + memmove(&sa->sin_addr, &v4info->ipi_addr, + sizeof(struct in_addr)); + paddr->localaddrlen = sizeof(struct sockaddr_in); + break; +#elif defined(IP_RECVDSTADDR) + } else if( cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_RECVDSTADDR) { + struct sockaddr_in* sa= (struct sockaddr_in*) + &paddr->localaddr; + struct sockaddr_in* rema = (struct sockaddr_in*) + &paddr->addr; + if(rema->sin_family != AF_INET) { + log_err("doq cmsg family mismatch cmsg is ip4"); + *pkt_continue = 1; + return 0; + } + sa->sin_family = AF_INET; + sa->sin_port = htons(doq_sockaddr_get_port( + (void*)c->socket->addr)); + paddr->ifindex = 0; + memmove(&sa.sin_addr, CMSG_DATA(cmsg), + sizeof(struct in_addr)); + paddr->localaddrlen = sizeof(struct sockaddr_in); + break; +#endif /* IP_PKTINFO or IP_RECVDSTADDR */ + } + } +#endif /* S_SPLINT_S */ + +return 1; +} + +/** get packet ecn information */ +static uint32_t +msghdr_get_ecn(struct msghdr* msg, int family) +{ +#ifndef S_SPLINT_S + struct cmsghdr* cmsg; + if(family == AF_INET6) { + for(cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + if(cmsg->cmsg_level == IPPROTO_IPV6 && + cmsg->cmsg_type == IPV6_TCLASS && + cmsg->cmsg_len != 0) { + uint8_t* ecn = (uint8_t*)CMSG_DATA(cmsg); + return *ecn; + } + } + return 0; + } + for(cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + if(cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_TOS && + cmsg->cmsg_len != 0) { + uint8_t* ecn = (uint8_t*)CMSG_DATA(cmsg); + return *ecn; + } + } +#endif /* S_SPLINT_S */ + return 0; +} + +/** receive packet for DoQ on UDP. get ancillary data for addresses, + * return false if failed and the callback can stop receiving UDP packets + * if pkt_continue is false. */ +static int +doq_recv(struct comm_point* c, struct doq_pkt_addr* paddr, int* pkt_continue, + struct ngtcp2_pkt_info* pi) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t rcv; + union { + struct cmsghdr hdr; + char buf[256]; + } ancil; + + msg.msg_name = &paddr->addr; + msg.msg_namelen = (socklen_t)sizeof(paddr->addr); + iov[0].iov_base = sldns_buffer_begin(c->doq_socket->pkt_buf); + iov[0].iov_len = sldns_buffer_remaining(c->doq_socket->pkt_buf); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = ancil.buf; +#ifndef S_SPLINT_S + msg.msg_controllen = sizeof(ancil.buf); +#endif /* S_SPLINT_S */ + msg.msg_flags = 0; + + rcv = recvmsg(c->fd, &msg, MSG_DONTWAIT); + if(rcv == -1) { + if(errno != EAGAIN && errno != EINTR + && udp_recv_needs_log(errno)) { + log_err("recvmsg failed for doq: %s", strerror(errno)); + } + *pkt_continue = 0; + return 0; + } + + paddr->addrlen = msg.msg_namelen; + sldns_buffer_skip(c->doq_socket->pkt_buf, rcv); + sldns_buffer_flip(c->doq_socket->pkt_buf); + if(!doq_get_localaddr_cmsg(c, paddr, pkt_continue, &msg)) + return 0; + pi->ecn = msghdr_get_ecn(&msg, paddr->addr.sockaddr.in.sin_family); + return 1; +} + +/** send the version negotiation for doq. scid and dcid are flipped around + * to send back to the client. */ +static void +doq_send_version_negotiation(struct comm_point* c, struct doq_pkt_addr* paddr, + const uint8_t* dcid, size_t dcidlen, const uint8_t* scid, + size_t scidlen) +{ + uint32_t versions[2]; + size_t versions_len = 0; + ngtcp2_ssize ret; + uint8_t unused_random; + + /* fill the array with supported versions */ + versions[0] = NGTCP2_PROTO_VER_V1; + versions_len = 1; + unused_random = ub_random_max(c->doq_socket->rnd, 256); + sldns_buffer_clear(c->doq_socket->pkt_buf); + ret = ngtcp2_pkt_write_version_negotiation( + sldns_buffer_begin(c->doq_socket->pkt_buf), + sldns_buffer_capacity(c->doq_socket->pkt_buf), unused_random, + dcid, dcidlen, scid, scidlen, versions, versions_len); + if(ret < 0) { + log_err("ngtcp2_pkt_write_version_negotiation failed: %s", + ngtcp2_strerror(ret)); + return; + } + sldns_buffer_set_position(c->doq_socket->pkt_buf, ret); + sldns_buffer_flip(c->doq_socket->pkt_buf); + doq_send_pkt(c, paddr, 0); +} + +/** Find the doq_conn object by remote address and dcid */ +static struct doq_conn* +doq_conn_find(struct doq_table* table, struct doq_addr_storage* addr, + socklen_t addrlen, struct doq_addr_storage* localaddr, + socklen_t localaddrlen, int ifindex, const uint8_t* dcid, + size_t dcidlen) +{ + struct rbnode_type* node; + struct doq_conn key; + memset(&key.node, 0, sizeof(key.node)); + key.node.key = &key; + memmove(&key.key.paddr.addr, addr, addrlen); + key.key.paddr.addrlen = addrlen; + memmove(&key.key.paddr.localaddr, localaddr, localaddrlen); + key.key.paddr.localaddrlen = localaddrlen; + key.key.paddr.ifindex = ifindex; + key.key.dcid = (void*)dcid; + key.key.dcidlen = dcidlen; + node = rbtree_search(table->conn_tree, &key); + if(node) + return (struct doq_conn*)node->key; + return NULL; +} + +/** find the doq_con by the connection id */ +static struct doq_conn* +doq_conn_find_by_id(struct doq_table* table, const uint8_t* dcid, + size_t dcidlen) +{ + struct doq_conid* conid; + lock_rw_rdlock(&table->conid_lock); + conid = doq_conid_find(table, dcid, dcidlen); + if(conid) { + /* make a copy of the key */ + struct doq_conn* conn; + struct doq_conn_key key = conid->key; + uint8_t cid[NGTCP2_MAX_CIDLEN]; + log_assert(conid->key.dcidlen <= NGTCP2_MAX_CIDLEN); + memcpy(cid, conid->key.dcid, conid->key.dcidlen); + key.dcid = cid; + lock_rw_unlock(&table->conid_lock); + + /* now that the conid lock is released, look up the conn */ + lock_rw_rdlock(&table->lock); + conn = doq_conn_find(table, &key.paddr.addr, + key.paddr.addrlen, &key.paddr.localaddr, + key.paddr.localaddrlen, key.paddr.ifindex, key.dcid, + key.dcidlen); + if(!conn) { + /* The connection got deleted between the conid lookup + * and the connection lock grab, it no longer exists, + * so return null. */ + lock_rw_unlock(&table->lock); + return NULL; + } + lock_basic_lock(&conn->lock); + if(conn->is_deleted) { + lock_rw_unlock(&table->lock); + lock_basic_unlock(&conn->lock); + return NULL; + } + lock_rw_unlock(&table->lock); + return conn; + } + lock_rw_unlock(&table->conid_lock); + return NULL; +} + +/** Find the doq_conn, by addr or by connection id */ +static struct doq_conn* +doq_conn_find_by_addr_or_cid(struct doq_table* table, + struct doq_pkt_addr* paddr, const uint8_t* dcid, size_t dcidlen) +{ + struct doq_conn* conn; + lock_rw_rdlock(&table->lock); + conn = doq_conn_find(table, &paddr->addr, paddr->addrlen, + &paddr->localaddr, paddr->localaddrlen, paddr->ifindex, + dcid, dcidlen); + if(conn && conn->is_deleted) { + conn = NULL; + } + if(conn) { + lock_basic_lock(&conn->lock); + lock_rw_unlock(&table->lock); + verbose(VERB_ALGO, "doq: found connection by address, dcid"); + } else { + lock_rw_unlock(&table->lock); + conn = doq_conn_find_by_id(table, dcid, dcidlen); + if(conn) { + verbose(VERB_ALGO, "doq: found connection by dcid"); + } + } + return conn; +} + +/** decode doq packet header, false on handled or failure, true to continue + * to process the packet */ +static int +doq_decode_pkt_header_negotiate(struct comm_point* c, + struct doq_pkt_addr* paddr, struct doq_conn** conn) +{ +#ifdef HAVE_STRUCT_NGTCP2_VERSION_CID + struct ngtcp2_version_cid vc; +#else + uint32_t version; + const uint8_t *dcid, *scid; + size_t dcidlen, scidlen; +#endif + int rv; + +#ifdef HAVE_STRUCT_NGTCP2_VERSION_CID + rv = ngtcp2_pkt_decode_version_cid(&vc, + sldns_buffer_begin(c->doq_socket->pkt_buf), + sldns_buffer_limit(c->doq_socket->pkt_buf), + c->doq_socket->sv_scidlen); +#else + rv = ngtcp2_pkt_decode_version_cid(&version, &dcid, &dcidlen, + &scid, &scidlen, sldns_buffer_begin(c->doq_socket->pkt_buf), + sldns_buffer_limit(c->doq_socket->pkt_buf), c->doq_socket->sv_scidlen); +#endif + if(rv != 0) { + if(rv == NGTCP2_ERR_VERSION_NEGOTIATION) { + /* send the version negotiation */ + doq_send_version_negotiation(c, paddr, +#ifdef HAVE_STRUCT_NGTCP2_VERSION_CID + vc.scid, vc.scidlen, vc.dcid, vc.dcidlen +#else + scid, scidlen, dcid, dcidlen +#endif + ); + return 0; + } + verbose(VERB_ALGO, "doq: could not decode version " + "and CID from QUIC packet header: %s", + ngtcp2_strerror(rv)); + return 0; + } + + if(verbosity >= VERB_ALGO) { + verbose(VERB_ALGO, "ngtcp2_pkt_decode_version_cid packet has " + "QUIC protocol version %u", (unsigned) +#ifdef HAVE_STRUCT_NGTCP2_VERSION_CID + vc. +#endif + version + ); + log_hex("dcid", +#ifdef HAVE_STRUCT_NGTCP2_VERSION_CID + (void*)vc.dcid, vc.dcidlen +#else + (void*)dcid, dcidlen +#endif + ); + log_hex("scid", +#ifdef HAVE_STRUCT_NGTCP2_VERSION_CID + (void*)vc.scid, vc.scidlen +#else + (void*)scid, scidlen +#endif + ); + } + *conn = doq_conn_find_by_addr_or_cid(c->doq_socket->table, paddr, +#ifdef HAVE_STRUCT_NGTCP2_VERSION_CID + vc.dcid, vc.dcidlen +#else + dcid, dcidlen +#endif + ); + if(*conn) + (*conn)->doq_socket = c->doq_socket; + return 1; +} + +/** fill cid structure with random data */ +static void doq_cid_randfill(struct ngtcp2_cid* cid, size_t datalen, + struct ub_randstate* rnd) +{ + uint8_t buf[32]; + if(datalen > sizeof(buf)) + datalen = sizeof(buf); + doq_fill_rand(rnd, buf, datalen); + ngtcp2_cid_init(cid, buf, datalen); +} + +/** send retry packet for doq connection. */ +static void +doq_send_retry(struct comm_point* c, struct doq_pkt_addr* paddr, + struct ngtcp2_pkt_hd* hd) +{ + char host[256], port[32]; + struct ngtcp2_cid scid; + uint8_t token[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN]; + ngtcp2_tstamp ts; + ngtcp2_ssize tokenlen, ret; + + if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host, + sizeof(host), port, sizeof(port))) { + log_err("doq_send_retry failed"); + return; + } + verbose(VERB_ALGO, "doq: sending retry packet to %s %s", host, port); + + /* the server chosen source connection ID */ + scid.datalen = c->doq_socket->sv_scidlen; + doq_cid_randfill(&scid, scid.datalen, c->doq_socket->rnd); + + ts = doq_get_timestamp_nanosec(); + + tokenlen = ngtcp2_crypto_generate_retry_token(token, + c->doq_socket->static_secret, c->doq_socket->static_secret_len, + hd->version, (void*)&paddr->addr, paddr->addrlen, &scid, + &hd->dcid, ts); + if(tokenlen < 0) { + log_err("ngtcp2_crypto_generate_retry_token failed: %s", + ngtcp2_strerror(tokenlen)); + return; + } + + sldns_buffer_clear(c->doq_socket->pkt_buf); + ret = ngtcp2_crypto_write_retry(sldns_buffer_begin(c->doq_socket->pkt_buf), + sldns_buffer_capacity(c->doq_socket->pkt_buf), hd->version, + &hd->scid, &scid, &hd->dcid, token, tokenlen); + if(ret < 0) { + log_err("ngtcp2_crypto_write_retry failed: %s", + ngtcp2_strerror(ret)); + return; + } + sldns_buffer_set_position(c->doq_socket->pkt_buf, ret); + sldns_buffer_flip(c->doq_socket->pkt_buf); + doq_send_pkt(c, paddr, 0); +} + +/** doq send stateless connection close */ +static void +doq_send_stateless_connection_close(struct comm_point* c, + struct doq_pkt_addr* paddr, struct ngtcp2_pkt_hd* hd, + uint64_t error_code) +{ + ngtcp2_ssize ret; + sldns_buffer_clear(c->doq_socket->pkt_buf); + ret = ngtcp2_crypto_write_connection_close( + sldns_buffer_begin(c->doq_socket->pkt_buf), + sldns_buffer_capacity(c->doq_socket->pkt_buf), hd->version, &hd->scid, + &hd->dcid, error_code, NULL, 0); + if(ret < 0) { + log_err("ngtcp2_crypto_write_connection_close failed: %s", + ngtcp2_strerror(ret)); + return; + } + sldns_buffer_set_position(c->doq_socket->pkt_buf, ret); + sldns_buffer_flip(c->doq_socket->pkt_buf); + doq_send_pkt(c, paddr, 0); +} + +/** doq verify retry token, false on failure */ +static int +doq_verify_retry_token(struct comm_point* c, struct doq_pkt_addr* paddr, + struct ngtcp2_cid* ocid, struct ngtcp2_pkt_hd* hd) +{ + char host[256], port[32]; + ngtcp2_tstamp ts; + if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host, + sizeof(host), port, sizeof(port))) { + log_err("doq_verify_retry_token failed"); + return 0; + } + ts = doq_get_timestamp_nanosec(); + verbose(VERB_ALGO, "doq: verifying retry token from %s %s", host, + port); + if(ngtcp2_crypto_verify_retry_token(ocid, +#ifdef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN + hd->token, hd->tokenlen, +#else + hd->token.base, hd->token.len, +#endif + c->doq_socket->static_secret, + c->doq_socket->static_secret_len, hd->version, + (void*)&paddr->addr, paddr->addrlen, &hd->dcid, + 10*NGTCP2_SECONDS, ts) != 0) { + verbose(VERB_ALGO, "doq: could not verify retry token " + "from %s %s", host, port); + return 0; + } + verbose(VERB_ALGO, "doq: verified retry token from %s %s", host, port); + return 1; +} + +/** doq verify token, false on failure */ +static int +doq_verify_token(struct comm_point* c, struct doq_pkt_addr* paddr, + struct ngtcp2_pkt_hd* hd) +{ + char host[256], port[32]; + ngtcp2_tstamp ts; + if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host, + sizeof(host), port, sizeof(port))) { + log_err("doq_verify_token failed"); + return 0; + } + ts = doq_get_timestamp_nanosec(); + verbose(VERB_ALGO, "doq: verifying token from %s %s", host, port); + if(ngtcp2_crypto_verify_regular_token( +#ifdef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN + hd->token, hd->tokenlen, +#else + hd->token.base, hd->token.len, +#endif + c->doq_socket->static_secret, c->doq_socket->static_secret_len, + (void*)&paddr->addr, paddr->addrlen, 3600*NGTCP2_SECONDS, + ts) != 0) { + verbose(VERB_ALGO, "doq: could not verify token from %s %s", + host, port); + return 0; + } + verbose(VERB_ALGO, "doq: verified token from %s %s", host, port); + return 1; +} + +/** delete and remove from the lookup tree the doq_conn connection */ +static void +doq_delete_connection(struct comm_point* c, struct doq_conn* conn) +{ + struct doq_conn copy; + uint8_t cid[NGTCP2_MAX_CIDLEN]; + rbnode_type* node; + if(!conn) + return; + /* Copy the key and set it deleted. */ + conn->is_deleted = 1; + doq_conn_write_disable(conn); + copy.key = conn->key; + log_assert(conn->key.dcidlen <= NGTCP2_MAX_CIDLEN); + memcpy(cid, conn->key.dcid, conn->key.dcidlen); + copy.key.dcid = cid; + copy.node.key = © + lock_basic_unlock(&conn->lock); + + /* Now get the table lock to delete it from the tree */ + lock_rw_wrlock(&c->doq_socket->table->lock); + node = rbtree_delete(c->doq_socket->table->conn_tree, copy.node.key); + if(node) { + conn = (struct doq_conn*)node->key; + lock_basic_lock(&conn->lock); + doq_conn_write_list_remove(c->doq_socket->table, conn); + if(conn->timer.timer_in_list) { + /* Remove timer from list first, because finding the + * rbnode element of the setlist of same timeouts + * needs tree lookup. Edit the tree structure after + * that lookup. */ + doq_timer_list_remove(c->doq_socket->table, + &conn->timer); + } + if(conn->timer.timer_in_tree) + doq_timer_tree_remove(c->doq_socket->table, + &conn->timer); + } + lock_rw_unlock(&c->doq_socket->table->lock); + if(node) { + lock_basic_unlock(&conn->lock); + doq_table_quic_size_subtract(c->doq_socket->table, + sizeof(*conn)+conn->key.dcidlen); + doq_conn_delete(conn, c->doq_socket->table); + } +} + +/** create and setup a new doq connection, to a new destination, or with + * a new dcid. It has a new set of streams. It is inserted in the lookup tree. + * Returns NULL on failure. */ +static struct doq_conn* +doq_setup_new_conn(struct comm_point* c, struct doq_pkt_addr* paddr, + struct ngtcp2_pkt_hd* hd, struct ngtcp2_cid* ocid) +{ + struct doq_conn* conn; + if(!doq_table_quic_size_available(c->doq_socket->table, + c->doq_socket->cfg, sizeof(*conn)+hd->dcid.datalen + + sizeof(struct doq_stream) + + 100 /* estimated input query */ + + 1200 /* estimated output query */)) { + verbose(VERB_ALGO, "doq: no mem available for new connection"); + doq_send_stateless_connection_close(c, paddr, hd, + NGTCP2_CONNECTION_REFUSED); + return NULL; + } + conn = doq_conn_create(c, paddr, hd->dcid.data, hd->dcid.datalen, + hd->version); + if(!conn) { + log_err("doq: could not allocate doq_conn"); + return NULL; + } + lock_rw_wrlock(&c->doq_socket->table->lock); + lock_basic_lock(&conn->lock); + if(!rbtree_insert(c->doq_socket->table->conn_tree, &conn->node)) { + lock_rw_unlock(&c->doq_socket->table->lock); + log_err("doq: duplicate connection"); + /* conn has no entry in writelist, and no timer yet. */ + lock_basic_unlock(&conn->lock); + doq_conn_delete(conn, c->doq_socket->table); + return NULL; + } + lock_rw_unlock(&c->doq_socket->table->lock); + doq_table_quic_size_add(c->doq_socket->table, + sizeof(*conn)+conn->key.dcidlen); + verbose(VERB_ALGO, "doq: created new connection"); + + /* the scid and dcid switch meaning from the accepted client + * connection to the server connection. The 'source' and 'destination' + * meaning is reversed. */ + if(!doq_conn_setup(conn, hd->scid.data, hd->scid.datalen, + (ocid?ocid->data:NULL), (ocid?ocid->datalen:0), +#ifdef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN + hd->token, hd->tokenlen +#else + hd->token.base, hd->token.len +#endif + )) { + log_err("doq: could not set up connection"); + doq_delete_connection(c, conn); + return NULL; + } + return conn; +} + +/** perform doq address validation */ +static int +doq_address_validation(struct comm_point* c, struct doq_pkt_addr* paddr, + struct ngtcp2_pkt_hd* hd, struct ngtcp2_cid* ocid, + struct ngtcp2_cid** pocid) +{ +#ifdef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN + const uint8_t* token = hd->token; + size_t tokenlen = hd->tokenlen; +#else + const uint8_t* token = hd->token.base; + size_t tokenlen = hd->token.len; +#endif + verbose(VERB_ALGO, "doq stateless address validation"); + + if(tokenlen == 0 || token == NULL) { + doq_send_retry(c, paddr, hd); + return 0; + } + if(token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY && + hd->dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN) { + doq_send_stateless_connection_close(c, paddr, hd, + NGTCP2_INVALID_TOKEN); + return 0; + } + if(token[0] == NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY) { + if(!doq_verify_retry_token(c, paddr, ocid, hd)) { + doq_send_stateless_connection_close(c, paddr, hd, + NGTCP2_INVALID_TOKEN); + return 0; + } + *pocid = ocid; + } else if(token[0] == NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR) { + if(!doq_verify_token(c, paddr, hd)) { + doq_send_retry(c, paddr, hd); + return 0; + } +#ifdef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN + hd->token = NULL; + hd->tokenlen = 0; +#else + hd->token.base = NULL; + hd->token.len = 0; +#endif + } else { + verbose(VERB_ALGO, "doq address validation: unrecognised " + "token in hd.token.base with magic byte 0x%2.2x", + (int)token[0]); + if(c->doq_socket->validate_addr) { + doq_send_retry(c, paddr, hd); + return 0; + } +#ifdef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN + hd->token = NULL; + hd->tokenlen = 0; +#else + hd->token.base = NULL; + hd->token.len = 0; +#endif + } + return 1; +} + +/** the doq accept, returns false if no further processing of content */ +static int +doq_accept(struct comm_point* c, struct doq_pkt_addr* paddr, + struct doq_conn** conn, struct ngtcp2_pkt_info* pi) +{ + int rv; + struct ngtcp2_pkt_hd hd; + struct ngtcp2_cid ocid, *pocid=NULL; + int err_retry; + memset(&hd, 0, sizeof(hd)); + rv = ngtcp2_accept(&hd, sldns_buffer_begin(c->doq_socket->pkt_buf), + sldns_buffer_limit(c->doq_socket->pkt_buf)); + if(rv != 0) { + if(rv == NGTCP2_ERR_RETRY) { + doq_send_retry(c, paddr, &hd); + return 0; + } + log_err("doq: initial packet failed, ngtcp2_accept failed: %s", + ngtcp2_strerror(rv)); + return 0; + } + if(c->doq_socket->validate_addr || +#ifdef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN + hd.tokenlen +#else + hd.token.len +#endif + ) { + if(!doq_address_validation(c, paddr, &hd, &ocid, &pocid)) + return 0; + } + *conn = doq_setup_new_conn(c, paddr, &hd, pocid); + if(!*conn) + return 0; + (*conn)->doq_socket = c->doq_socket; + if(!doq_conn_recv(c, paddr, *conn, pi, &err_retry, NULL)) { + if(err_retry) + doq_send_retry(c, paddr, &hd); + doq_delete_connection(c, *conn); + *conn = NULL; + return 0; + } + return 1; +} + +/** doq pickup a timer to wait for for the worker. If any timer exists. */ +static void +doq_pickup_timer(struct comm_point* c) +{ + struct doq_timer* t; + struct timeval tv; + int have_time = 0; + memset(&tv, 0, sizeof(tv)); + + lock_rw_wrlock(&c->doq_socket->table->lock); + RBTREE_FOR(t, struct doq_timer*, c->doq_socket->table->timer_tree) { + if(t->worker_doq_socket == NULL || + t->worker_doq_socket == c->doq_socket) { + /* pick up this element */ + t->worker_doq_socket = c->doq_socket; + have_time = 1; + memcpy(&tv, &t->time, sizeof(tv)); + break; + } + } + lock_rw_unlock(&c->doq_socket->table->lock); + + if(have_time) { + struct timeval rel; + timeval_subtract(&rel, &tv, c->doq_socket->now_tv); + comm_timer_set(c->doq_socket->timer, &rel); + memcpy(&c->doq_socket->marked_time, &tv, + sizeof(c->doq_socket->marked_time)); + verbose(VERB_ALGO, "doq pickup timer at %d.%6.6d in %d.%6.6d", + (int)tv.tv_sec, (int)tv.tv_usec, (int)rel.tv_sec, + (int)rel.tv_usec); + } else { + if(comm_timer_is_set(c->doq_socket->timer)) + comm_timer_disable(c->doq_socket->timer); + memset(&c->doq_socket->marked_time, 0, + sizeof(c->doq_socket->marked_time)); + verbose(VERB_ALGO, "doq timer disabled"); + } +} + +/** doq done with connection, release locks and setup timer and write */ +static void +doq_done_setup_timer_and_write(struct comm_point* c, struct doq_conn* conn) +{ + struct doq_conn copy; + uint8_t cid[NGTCP2_MAX_CIDLEN]; + rbnode_type* node; + struct timeval new_tv; + int write_change = 0, timer_change = 0; + + /* No longer in callbacks, so the pointer to doq_socket is back + * to NULL. */ + conn->doq_socket = NULL; + + if(doq_conn_check_timer(conn, &new_tv)) + timer_change = 1; + if( (conn->write_interest && !conn->on_write_list) || + (!conn->write_interest && conn->on_write_list)) + write_change = 1; + + if(!timer_change && !write_change) { + /* Nothing to do. */ + lock_basic_unlock(&conn->lock); + return; + } + + /* The table lock is needed to change the write list and timer tree. + * So the connection lock is release and then the connection is + * looked up again. */ + copy.key = conn->key; + log_assert(conn->key.dcidlen <= NGTCP2_MAX_CIDLEN); + memcpy(cid, conn->key.dcid, conn->key.dcidlen); + copy.key.dcid = cid; + copy.node.key = © + lock_basic_unlock(&conn->lock); + + lock_rw_wrlock(&c->doq_socket->table->lock); + node = rbtree_search(c->doq_socket->table->conn_tree, copy.node.key); + if(!node) { + lock_rw_unlock(&c->doq_socket->table->lock); + /* Must have been deleted in the mean time. */ + return; + } + conn = (struct doq_conn*)node->key; + lock_basic_lock(&conn->lock); + if(conn->is_deleted) { + /* It is deleted now. */ + lock_rw_unlock(&c->doq_socket->table->lock); + lock_basic_unlock(&conn->lock); + return; + } + + if(write_change) { + /* Edit the write lists, we are holding the table.lock and can + * edit the list first,last and also prev,next and on_list + * elements in the doq_conn structures. */ + doq_conn_set_write_list(c->doq_socket->table, conn); + } + if(timer_change) { + doq_timer_set(c->doq_socket->table, &conn->timer, + c->doq_socket, &new_tv); + } + lock_rw_unlock(&c->doq_socket->table->lock); + lock_basic_unlock(&conn->lock); +} + +/** doq done with connection callbacks, release locks and setup write */ +static void +doq_done_with_conn_cb(struct comm_point* c, struct doq_conn* conn) +{ + struct doq_conn copy; + uint8_t cid[NGTCP2_MAX_CIDLEN]; + rbnode_type* node; + + /* no longer in callbacks, so the pointer to doq_socket is back + * to NULL. */ + conn->doq_socket = NULL; + + if( (conn->write_interest && conn->on_write_list) || + (!conn->write_interest && !conn->on_write_list)) { + /* The connection already has the required write list + * status. */ + lock_basic_unlock(&conn->lock); + return; + } + + /* To edit the write list of connections we have to hold the table + * lock, so we release the connection and then look it up again. */ + copy.key = conn->key; + log_assert(conn->key.dcidlen <= NGTCP2_MAX_CIDLEN); + memcpy(cid, conn->key.dcid, conn->key.dcidlen); + copy.key.dcid = cid; + copy.node.key = © + lock_basic_unlock(&conn->lock); + + lock_rw_wrlock(&c->doq_socket->table->lock); + node = rbtree_search(c->doq_socket->table->conn_tree, copy.node.key); + if(!node) { + lock_rw_unlock(&c->doq_socket->table->lock); + /* must have been deleted in the mean time */ + return; + } + conn = (struct doq_conn*)node->key; + lock_basic_lock(&conn->lock); + if(conn->is_deleted) { + /* it is deleted now. */ + lock_rw_unlock(&c->doq_socket->table->lock); + lock_basic_unlock(&conn->lock); + return; + } + + /* edit the write lists, we are holding the table.lock and can + * edit the list first,last and also prev,next and on_list elements + * in the doq_conn structures. */ + doq_conn_set_write_list(c->doq_socket->table, conn); + lock_rw_unlock(&c->doq_socket->table->lock); + lock_basic_unlock(&conn->lock); +} + +/** doq count the length of the write list */ +static size_t +doq_write_list_length(struct comm_point* c) +{ + size_t count = 0; + struct doq_conn* conn; + lock_rw_rdlock(&c->doq_socket->table->lock); + conn = c->doq_socket->table->write_list_first; + while(conn) { + count++; + conn = conn->write_next; + } + lock_rw_unlock(&c->doq_socket->table->lock); + return count; +} + +/** doq pop the first element from the write list to have write events */ +static struct doq_conn* +doq_pop_write_conn(struct comm_point* c) +{ + struct doq_conn* conn; + lock_rw_wrlock(&c->doq_socket->table->lock); + conn = doq_table_pop_first(c->doq_socket->table); + while(conn && conn->is_deleted) { + lock_basic_unlock(&conn->lock); + conn = doq_table_pop_first(c->doq_socket->table); + } + lock_rw_unlock(&c->doq_socket->table->lock); + if(conn) + conn->doq_socket = c->doq_socket; + return conn; +} + +/** doq the connection is done with write callbacks, release it. */ +static void +doq_done_with_write_cb(struct comm_point* c, struct doq_conn* conn, + int delete_it) +{ + if(delete_it) { + doq_delete_connection(c, conn); + return; + } + doq_done_setup_timer_and_write(c, conn); +} + +/** see if the doq socket wants to write packets */ +static int +doq_socket_want_write(struct comm_point* c) +{ + int want_write = 0; + if(c->doq_socket->have_blocked_pkt) + return 1; + lock_rw_rdlock(&c->doq_socket->table->lock); + if(c->doq_socket->table->write_list_first) + want_write = 1; + lock_rw_unlock(&c->doq_socket->table->lock); + return want_write; +} + +/** enable write event for the doq server socket fd */ +static void +doq_socket_write_enable(struct comm_point* c) +{ + verbose(VERB_ALGO, "doq socket want write"); + if(c->doq_socket->event_has_write) + return; + comm_point_listen_for_rw(c, 1, 1); + c->doq_socket->event_has_write = 1; +} + +/** disable write event for the doq server socket fd */ +static void +doq_socket_write_disable(struct comm_point* c) +{ + verbose(VERB_ALGO, "doq socket want no write"); + if(!c->doq_socket->event_has_write) + return; + comm_point_listen_for_rw(c, 1, 0); + c->doq_socket->event_has_write = 0; +} + +/** write blocked packet, if possible. returns false if failed, again. */ +static int +doq_write_blocked_pkt(struct comm_point* c) +{ + struct doq_pkt_addr paddr; + if(!c->doq_socket->have_blocked_pkt) + return 1; + c->doq_socket->have_blocked_pkt = 0; + if(sldns_buffer_limit(c->doq_socket->blocked_pkt) > + sldns_buffer_remaining(c->doq_socket->pkt_buf)) + return 1; /* impossibly large, drop it. + impossible since pkt_buf is same size as blocked_pkt buf. */ + sldns_buffer_clear(c->doq_socket->pkt_buf); + sldns_buffer_write(c->doq_socket->pkt_buf, + sldns_buffer_begin(c->doq_socket->blocked_pkt), + sldns_buffer_limit(c->doq_socket->blocked_pkt)); + sldns_buffer_flip(c->doq_socket->pkt_buf); + memcpy(&paddr, c->doq_socket->blocked_paddr, sizeof(paddr)); + doq_send_pkt(c, &paddr, c->doq_socket->blocked_pkt_pi.ecn); + if(c->doq_socket->have_blocked_pkt) + return 0; + return 1; +} + +/** doq find a timer that timeouted and return the conn, locked. */ +static struct doq_conn* +doq_timer_timeout_conn(struct doq_server_socket* doq_socket) +{ + struct doq_conn* conn = NULL; + struct rbnode_type* node; + lock_rw_wrlock(&doq_socket->table->lock); + node = rbtree_first(doq_socket->table->timer_tree); + if(node && node != RBTREE_NULL) { + struct doq_timer* t = (struct doq_timer*)node; + conn = t->conn; + + /* If now < timer then no further timeouts in tree. */ + if(timeval_smaller(doq_socket->now_tv, &t->time)) { + lock_rw_unlock(&doq_socket->table->lock); + return NULL; + } + + lock_basic_lock(&conn->lock); + conn->doq_socket = doq_socket; - if(rep.c->pp2_enabled && !consume_pp2_header(rep.c->buffer, - &rep, 0)) { - log_err("proxy_protocol: could not consume PROXYv2 header"); - return; - } - if(!rep.is_proxied) { - rep.client_addrlen = rep.remote_addrlen; - memmove(&rep.client_addr, &rep.remote_addr, - rep.remote_addrlen); - } + /* Now that the timer is fired, remove it. */ + doq_timer_unset(doq_socket->table, t); + lock_rw_unlock(&doq_socket->table->lock); + return conn; + } + lock_rw_unlock(&doq_socket->table->lock); + return NULL; +} - fptr_ok(fptr_whitelist_comm_point(rep.c->callback)); - if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) { - /* send back immediate reply */ - struct sldns_buffer *buffer; -#ifdef USE_DNSCRYPT - buffer = rep.c->dnscrypt_buffer; +/** doq timer erase the marker that said which timer the worker uses. */ +static void +doq_timer_erase_marker(struct doq_server_socket* doq_socket) +{ + struct doq_timer* t; + lock_rw_wrlock(&doq_socket->table->lock); + t = doq_timer_find_time(doq_socket->table, &doq_socket->marked_time); + if(t && t->worker_doq_socket == doq_socket) + t->worker_doq_socket = NULL; + lock_rw_unlock(&doq_socket->table->lock); + memset(&doq_socket->marked_time, 0, sizeof(doq_socket->marked_time)); +} + +void +doq_timer_cb(void* arg) +{ + struct doq_server_socket* doq_socket = (struct doq_server_socket*)arg; + struct doq_conn* conn; + verbose(VERB_ALGO, "doq timer callback"); + + doq_timer_erase_marker(doq_socket); + + while((conn = doq_timer_timeout_conn(doq_socket)) != NULL) { + if(conn->is_deleted || +#ifdef HAVE_NGTCP2_CONN_IN_CLOSING_PERIOD + ngtcp2_conn_in_closing_period(conn->conn) || #else - buffer = rep.c->buffer; + ngtcp2_conn_is_in_closing_period(conn->conn) || #endif - (void)comm_point_send_udp_msg_if(rep.c, buffer, - (struct sockaddr*)&rep.remote_addr, - rep.remote_addrlen, &rep); +#ifdef HAVE_NGTCP2_CONN_IN_DRAINING_PERIOD + ngtcp2_conn_in_draining_period(conn->conn) +#else + ngtcp2_conn_is_in_draining_period(conn->conn) +#endif + ) { + if(verbosity >= VERB_ALGO) { + char remotestr[256]; + addr_to_str((void*)&conn->key.paddr.addr, + conn->key.paddr.addrlen, remotestr, + sizeof(remotestr)); + verbose(VERB_ALGO, "doq conn %s is deleted " + "after timeout", remotestr); + } + doq_delete_connection(doq_socket->cp, conn); + continue; } - if(!rep.c || rep.c->fd == -1) /* commpoint closed */ - break; + if(!doq_conn_handle_timeout(conn)) + doq_delete_connection(doq_socket->cp, conn); + else doq_done_setup_timer_and_write(doq_socket->cp, conn); } + + if(doq_socket_want_write(doq_socket->cp)) + doq_socket_write_enable(doq_socket->cp); + else doq_socket_write_disable(doq_socket->cp); + doq_pickup_timer(doq_socket->cp); } -#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */ void -comm_point_udp_callback(int fd, short event, void* arg) +comm_point_doq_callback(int fd, short event, void* arg) { - struct comm_reply rep; - ssize_t rcv; - int i; - struct sldns_buffer *buffer; + struct comm_point* c; + struct doq_pkt_addr paddr; + int i, pkt_continue, err_drop; + struct doq_conn* conn; + struct ngtcp2_pkt_info pi; + size_t count, num_len; - rep.c = (struct comm_point*)arg; - log_assert(rep.c->type == comm_udp); + c = (struct comm_point*)arg; + log_assert(c->type == comm_doq); - if(!(event&UB_EV_READ)) - return; - log_assert(rep.c && rep.c->buffer && rep.c->fd == fd); - ub_comm_base_now(rep.c->ev->base); - for(i=0; ibuffer); - rep.remote_addrlen = (socklen_t)sizeof(rep.remote_addr); - log_assert(fd != -1); - log_assert(sldns_buffer_remaining(rep.c->buffer) > 0); - rcv = recvfrom(fd, (void*)sldns_buffer_begin(rep.c->buffer), - sldns_buffer_remaining(rep.c->buffer), MSG_DONTWAIT, - (struct sockaddr*)&rep.remote_addr, &rep.remote_addrlen); - if(rcv == -1) { -#ifndef USE_WINSOCK - if(errno != EAGAIN && errno != EINTR - && udp_recv_needs_log(errno)) - log_err("recvfrom %d failed: %s", - fd, strerror(errno)); + log_assert(c && c->doq_socket->pkt_buf && c->fd == fd); + ub_comm_base_now(c->ev->base); + + /* see if there is a blocked packet, and send that if possible. + * do not attempt to read yet, even if possible, that would just + * push more answers in reply to those read packets onto the list + * of written replies. First attempt to clear the write content out. + * That keeps the memory usage from bloating up. */ + if(c->doq_socket->have_blocked_pkt) { + if(!doq_write_blocked_pkt(c)) { + /* this write has also blocked, attempt to write + * later. Make sure the event listens to write + * events. */ + if(!c->doq_socket->event_has_write) + doq_socket_write_enable(c); + doq_pickup_timer(c); + return; + } + } + + /* see if there is write interest */ + count = 0; + num_len = doq_write_list_length(c); + while((conn = doq_pop_write_conn(c)) != NULL) { + if(conn->is_deleted || +#ifdef HAVE_NGTCP2_CONN_IN_CLOSING_PERIOD + ngtcp2_conn_in_closing_period(conn->conn) || #else - if(WSAGetLastError() != WSAEINPROGRESS && - WSAGetLastError() != WSAECONNRESET && - WSAGetLastError()!= WSAEWOULDBLOCK && - udp_recv_needs_log(WSAGetLastError())) - log_err("recvfrom failed: %s", - wsa_strerror(WSAGetLastError())); + ngtcp2_conn_is_in_closing_period(conn->conn) || #endif +#ifdef HAVE_NGTCP2_CONN_IN_DRAINING_PERIOD + ngtcp2_conn_in_draining_period(conn->conn) +#else + ngtcp2_conn_is_in_draining_period(conn->conn) +#endif + ) { + conn->doq_socket = NULL; + lock_basic_unlock(&conn->lock); + if(c->doq_socket->have_blocked_pkt) { + if(!c->doq_socket->event_has_write) + doq_socket_write_enable(c); + doq_pickup_timer(c); + return; + } + if(++count > num_len*2) + break; + continue; + } + if(verbosity >= VERB_ALGO) { + char remotestr[256]; + addr_to_str((void*)&conn->key.paddr.addr, + conn->key.paddr.addrlen, remotestr, + sizeof(remotestr)); + verbose(VERB_ALGO, "doq write connection %s %d", + remotestr, doq_sockaddr_get_port( + &conn->key.paddr.addr)); + } + if(doq_conn_write_streams(c, conn, &err_drop)) + err_drop = 0; + doq_done_with_write_cb(c, conn, err_drop); + if(c->doq_socket->have_blocked_pkt) { + if(!c->doq_socket->event_has_write) + doq_socket_write_enable(c); + doq_pickup_timer(c); return; } - sldns_buffer_skip(rep.c->buffer, rcv); - sldns_buffer_flip(rep.c->buffer); - rep.srctype = 0; - rep.is_proxied = 0; + /* Stop overly long write lists that are created + * while we are processing. Do those next time there + * is a write callback. Stops long loops, and keeps + * fair for other events. */ + if(++count > num_len*2) + break; + } - if(rep.c->pp2_enabled && !consume_pp2_header(rep.c->buffer, - &rep, 0)) { - log_err("proxy_protocol: could not consume PROXYv2 header"); + /* check for data to read */ + if((event&UB_EV_READ)!=0) + for(i=0; idoq_socket->have_blocked_pkt) { + if(!c->doq_socket->event_has_write) + doq_socket_write_enable(c); + doq_pickup_timer(c); return; } - if(!rep.is_proxied) { - rep.client_addrlen = rep.remote_addrlen; - memmove(&rep.client_addr, &rep.remote_addr, - rep.remote_addrlen); + sldns_buffer_clear(c->doq_socket->pkt_buf); + doq_pkt_addr_init(&paddr); + log_assert(fd != -1); + log_assert(sldns_buffer_remaining(c->doq_socket->pkt_buf) > 0); + if(!doq_recv(c, &paddr, &pkt_continue, &pi)) { + if(pkt_continue) + continue; + break; } - fptr_ok(fptr_whitelist_comm_point(rep.c->callback)); - if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) { - /* send back immediate reply */ -#ifdef USE_DNSCRYPT - buffer = rep.c->dnscrypt_buffer; + /* handle incoming packet from remote addr to localaddr */ + if(verbosity >= VERB_ALGO) { + char remotestr[256], localstr[256]; + addr_to_str((void*)&paddr.addr, paddr.addrlen, + remotestr, sizeof(remotestr)); + addr_to_str((void*)&paddr.localaddr, + paddr.localaddrlen, localstr, + sizeof(localstr)); + log_info("incoming doq packet from %s port %d on " + "%s port %d ifindex %d", + remotestr, doq_sockaddr_get_port(&paddr.addr), + localstr, + doq_sockaddr_get_port(&paddr.localaddr), + paddr.ifindex); + log_info("doq_recv length %d ecn 0x%x", + (int)sldns_buffer_limit(c->doq_socket->pkt_buf), + (int)pi.ecn); + } + + if(sldns_buffer_limit(c->doq_socket->pkt_buf) == 0) + continue; + + conn = NULL; + if(!doq_decode_pkt_header_negotiate(c, &paddr, &conn)) + continue; + if(!conn) { + if(!doq_accept(c, &paddr, &conn, &pi)) + continue; + if(!doq_conn_write_streams(c, conn, NULL)) { + doq_delete_connection(c, conn); + continue; + } + doq_done_setup_timer_and_write(c, conn); + continue; + } + if( +#ifdef HAVE_NGTCP2_CONN_IN_CLOSING_PERIOD + ngtcp2_conn_in_closing_period(conn->conn) #else - buffer = rep.c->buffer; + ngtcp2_conn_is_in_closing_period(conn->conn) #endif - (void)comm_point_send_udp_msg(rep.c, buffer, - (struct sockaddr*)&rep.remote_addr, - rep.remote_addrlen, 0); + ) { + if(!doq_conn_send_close(c, conn)) { + doq_delete_connection(c, conn); + } else { + doq_done_setup_timer_and_write(c, conn); + } + continue; } - if(!rep.c || rep.c->fd != fd) /* commpoint closed to -1 or reused for - another UDP port. Note rep.c cannot be reused with TCP fd. */ - break; + if( +#ifdef HAVE_NGTCP2_CONN_IN_DRAINING_PERIOD + ngtcp2_conn_in_draining_period(conn->conn) +#else + ngtcp2_conn_is_in_draining_period(conn->conn) +#endif + ) { + doq_done_setup_timer_and_write(c, conn); + continue; + } + if(!doq_conn_recv(c, &paddr, conn, &pi, NULL, &err_drop)) { + /* The receive failed, and if it also failed to send + * a close, drop the connection. That means it is not + * in the closing period. */ + if(err_drop) { + doq_delete_connection(c, conn); + } else { + doq_done_setup_timer_and_write(c, conn); + } + continue; + } + if(!doq_conn_write_streams(c, conn, &err_drop)) { + if(err_drop) { + doq_delete_connection(c, conn); + } else { + doq_done_setup_timer_and_write(c, conn); + } + continue; + } + doq_done_setup_timer_and_write(c, conn); + } + + /* see if we want to have more write events */ + verbose(VERB_ALGO, "doq check write enable"); + if(doq_socket_want_write(c)) + doq_socket_write_enable(c); + else doq_socket_write_disable(c); + doq_pickup_timer(c); +} + +/** create new doq server socket structure */ +static struct doq_server_socket* +doq_server_socket_create(struct doq_table* table, struct ub_randstate* rnd, + const char* ssl_service_key, const char* ssl_service_pem, + struct comm_point* c, struct comm_base* base, struct config_file* cfg) +{ + size_t doq_buffer_size = 4096; /* bytes buffer size, for one packet. */ + struct doq_server_socket* doq_socket; + doq_socket = calloc(1, sizeof(*doq_socket)); + if(!doq_socket) { + return NULL; + } + doq_socket->table = table; + doq_socket->rnd = rnd; + doq_socket->validate_addr = 1; + if(ssl_service_key == NULL || ssl_service_key[0]==0) { + log_err("doq server socket create: no tls-service-key"); + free(doq_socket); + return NULL; + } + if(ssl_service_pem == NULL || ssl_service_pem[0]==0) { + log_err("doq server socket create: no tls-service-pem"); + free(doq_socket); + return NULL; + } + doq_socket->ssl_service_key = strdup(ssl_service_key); + if(!doq_socket->ssl_service_key) { + free(doq_socket); + return NULL; + } + doq_socket->ssl_service_pem = strdup(ssl_service_pem); + if(!doq_socket->ssl_service_pem) { + free(doq_socket->ssl_service_key); + free(doq_socket); + return NULL; + } + doq_socket->ssl_verify_pem = NULL; + /* the doq_socket has its own copy of the static secret, as + * well as other config values, so that they do not need table.lock */ + doq_socket->static_secret_len = table->static_secret_len; + doq_socket->static_secret = memdup(table->static_secret, + table->static_secret_len); + if(!doq_socket->static_secret) { + free(doq_socket->ssl_service_key); + free(doq_socket->ssl_service_pem); + free(doq_socket->ssl_verify_pem); + free(doq_socket); + return NULL; + } + if(!doq_socket_setup_ctx(doq_socket)) { + free(doq_socket->ssl_service_key); + free(doq_socket->ssl_service_pem); + free(doq_socket->ssl_verify_pem); + free(doq_socket->static_secret); + free(doq_socket); + return NULL; + } + doq_socket->idle_timeout = table->idle_timeout; + doq_socket->sv_scidlen = table->sv_scidlen; + doq_socket->cp = c; + doq_socket->pkt_buf = sldns_buffer_new(doq_buffer_size); + if(!doq_socket->pkt_buf) { + free(doq_socket->ssl_service_key); + free(doq_socket->ssl_service_pem); + free(doq_socket->ssl_verify_pem); + free(doq_socket->static_secret); + SSL_CTX_free(doq_socket->ctx); + free(doq_socket); + return NULL; + } + doq_socket->blocked_pkt = sldns_buffer_new( + sldns_buffer_capacity(doq_socket->pkt_buf)); + if(!doq_socket->pkt_buf) { + free(doq_socket->ssl_service_key); + free(doq_socket->ssl_service_pem); + free(doq_socket->ssl_verify_pem); + free(doq_socket->static_secret); + SSL_CTX_free(doq_socket->ctx); + sldns_buffer_free(doq_socket->pkt_buf); + free(doq_socket); + return NULL; + } + doq_socket->blocked_paddr = calloc(1, + sizeof(*doq_socket->blocked_paddr)); + if(!doq_socket->blocked_paddr) { + free(doq_socket->ssl_service_key); + free(doq_socket->ssl_service_pem); + free(doq_socket->ssl_verify_pem); + free(doq_socket->static_secret); + SSL_CTX_free(doq_socket->ctx); + sldns_buffer_free(doq_socket->pkt_buf); + sldns_buffer_free(doq_socket->blocked_pkt); + free(doq_socket); + return NULL; + } + doq_socket->timer = comm_timer_create(base, doq_timer_cb, doq_socket); + if(!doq_socket->timer) { + free(doq_socket->ssl_service_key); + free(doq_socket->ssl_service_pem); + free(doq_socket->ssl_verify_pem); + free(doq_socket->static_secret); + SSL_CTX_free(doq_socket->ctx); + sldns_buffer_free(doq_socket->pkt_buf); + sldns_buffer_free(doq_socket->blocked_pkt); + free(doq_socket->blocked_paddr); + free(doq_socket); + return NULL; + } + memset(&doq_socket->marked_time, 0, sizeof(doq_socket->marked_time)); + comm_base_timept(base, &doq_socket->now_tt, &doq_socket->now_tv); + doq_socket->cfg = cfg; + return doq_socket; +} + +/** delete doq server socket structure */ +static void +doq_server_socket_delete(struct doq_server_socket* doq_socket) +{ + if(!doq_socket) + return; + free(doq_socket->static_secret); + SSL_CTX_free(doq_socket->ctx); +#ifndef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT + free(doq_socket->quic_method); +#endif + free(doq_socket->ssl_service_key); + free(doq_socket->ssl_service_pem); + free(doq_socket->ssl_verify_pem); + sldns_buffer_free(doq_socket->pkt_buf); + sldns_buffer_free(doq_socket->blocked_pkt); + free(doq_socket->blocked_paddr); + comm_timer_delete(doq_socket->timer); + free(doq_socket); +} + +/** find repinfo in the doq table */ +static struct doq_conn* +doq_lookup_repinfo(struct doq_table* table, struct comm_reply* repinfo) +{ + struct doq_conn* conn; + struct doq_conn_key key; + doq_conn_key_from_repinfo(&key, repinfo); + lock_rw_rdlock(&table->lock); + conn = doq_conn_find(table, &key.paddr.addr, + key.paddr.addrlen, &key.paddr.localaddr, + key.paddr.localaddrlen, key.paddr.ifindex, key.dcid, + key.dcidlen); + if(conn) { + lock_basic_lock(&conn->lock); + lock_rw_unlock(&table->lock); + return conn; + } + lock_rw_unlock(&table->lock); + return NULL; +} + +/** doq find connection and stream. From inside callbacks from worker. */ +static int +doq_lookup_conn_stream(struct comm_reply* repinfo, struct comm_point* c, + struct doq_conn** conn, struct doq_stream** stream) +{ + if(c->doq_socket->current_conn) { + *conn = c->doq_socket->current_conn; + } else { + *conn = doq_lookup_repinfo(c->doq_socket->table, repinfo); + if((*conn) && (*conn)->is_deleted) { + lock_basic_unlock(&(*conn)->lock); + *conn = NULL; + } + if(*conn) { + (*conn)->doq_socket = c->doq_socket; + } + } + if(!*conn) { + *stream = NULL; + return 0; + } + *stream = doq_stream_find(*conn, repinfo->doq_streamid); + if(!*stream) { + if(!c->doq_socket->current_conn) { + /* Not inside callbacks, we have our own lock on conn. + * Release it. */ + lock_basic_unlock(&(*conn)->lock); + } + return 0; + } + if((*stream)->is_closed) { + /* stream is closed, ignore reply or drop */ + if(!c->doq_socket->current_conn) { + /* Not inside callbacks, we have our own lock on conn. + * Release it. */ + lock_basic_unlock(&(*conn)->lock); + } + return 0; + } + return 1; +} + +/** doq send a reply from a comm reply */ +static void +doq_socket_send_reply(struct comm_reply* repinfo) +{ + struct doq_conn* conn; + struct doq_stream* stream; + log_assert(repinfo->c->type == comm_doq); + if(!doq_lookup_conn_stream(repinfo, repinfo->c, &conn, &stream)) { + verbose(VERB_ALGO, "doq: send_reply but %s is gone", + (conn?"stream":"connection")); + /* No stream, it may have been closed. */ + /* Drop the reply, it cannot be sent. */ + return; + } + if(!doq_stream_send_reply(conn, stream, repinfo->c->buffer)) + doq_stream_close(conn, stream, 1); + if(!repinfo->c->doq_socket->current_conn) { + /* Not inside callbacks, we have our own lock on conn. + * Release it. */ + doq_done_with_conn_cb(repinfo->c, conn); + /* since we sent a reply, or closed it, the assumption is + * that there is something to write, so enable write event. + * It waits until the write event happens to write the + * streams with answers, this allows some answers to be + * answered before the event loop reaches the doq fd, in + * repinfo->c->fd, and that collates answers. That would + * not happen if we write doq packets right now. */ + doq_socket_write_enable(repinfo->c); + } +} + +/** doq drop a reply from a comm reply */ +static void +doq_socket_drop_reply(struct comm_reply* repinfo) +{ + struct doq_conn* conn; + struct doq_stream* stream; + log_assert(repinfo->c->type == comm_doq); + if(!doq_lookup_conn_stream(repinfo, repinfo->c, &conn, &stream)) { + verbose(VERB_ALGO, "doq: drop_reply but %s is gone", + (conn?"stream":"connection")); + /* The connection or stream is already gone. */ + return; + } + doq_stream_close(conn, stream, 1); + if(!repinfo->c->doq_socket->current_conn) { + /* Not inside callbacks, we have our own lock on conn. + * Release it. */ + doq_done_with_conn_cb(repinfo->c, conn); + doq_socket_write_enable(repinfo->c); } } +#endif /* HAVE_NGTCP2 */ int adjusted_tcp_timeout(struct comm_point* c) { @@ -4081,6 +5857,96 @@ comm_point_create_udp_ancil(struct comm_ } #endif +struct comm_point* +comm_point_create_doq(struct comm_base *base, int fd, sldns_buffer* buffer, + comm_point_callback_type* callback, void* callback_arg, + struct unbound_socket* socket, struct doq_table* table, + struct ub_randstate* rnd, const char* ssl_service_key, + const char* ssl_service_pem, struct config_file* cfg) +{ +#ifdef HAVE_NGTCP2 + struct comm_point* c = (struct comm_point*)calloc(1, + sizeof(struct comm_point)); + short evbits; + if(!c) + return NULL; + c->ev = (struct internal_event*)calloc(1, + sizeof(struct internal_event)); + if(!c->ev) { + free(c); + return NULL; + } + c->ev->base = base; + c->fd = fd; + c->buffer = buffer; + c->timeout = NULL; + c->tcp_is_reading = 0; + c->tcp_byte_count = 0; + c->tcp_parent = NULL; + c->max_tcp_count = 0; + c->cur_tcp_count = 0; + c->tcp_handlers = NULL; + c->tcp_free = NULL; + c->type = comm_doq; + c->tcp_do_close = 0; + c->do_not_close = 0; + c->tcp_do_toggle_rw = 0; + c->tcp_check_nb_connect = 0; +#ifdef USE_MSG_FASTOPEN + c->tcp_do_fastopen = 0; +#endif +#ifdef USE_DNSCRYPT + c->dnscrypt = 0; + c->dnscrypt_buffer = NULL; +#endif +#ifdef HAVE_NGTCP2 + c->doq_socket = doq_server_socket_create(table, rnd, ssl_service_key, + ssl_service_pem, c, base, cfg); + if(!c->doq_socket) { + log_err("could not create doq comm_point"); + comm_point_delete(c); + return NULL; + } +#endif + c->inuse = 0; + c->callback = callback; + c->cb_arg = callback_arg; + c->socket = socket; + c->pp2_enabled = 0; + c->pp2_header_state = pp2_header_none; + evbits = UB_EV_READ | UB_EV_PERSIST; + /* ub_event stuff */ + c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits, + comm_point_doq_callback, c); + if(c->ev->ev == NULL) { + log_err("could not baseset udp event"); + comm_point_delete(c); + return NULL; + } + if(fd!=-1 && ub_event_add(c->ev->ev, c->timeout) != 0 ) { + log_err("could not add udp event"); + comm_point_delete(c); + return NULL; + } + c->event_added = 1; + return c; +#else + /* no libngtcp2, so no QUIC support */ + (void)base; + (void)buffer; + (void)callback; + (void)callback_arg; + (void)socket; + (void)rnd; + (void)table; + (void)ssl_service_key; + (void)ssl_service_pem; + (void)cfg; + sock_close(fd); + return NULL; +#endif /* HAVE_NGTCP2 */ +} + static struct comm_point* comm_point_create_tcp_handler(struct comm_base *base, struct comm_point* parent, size_t bufsize, @@ -4749,11 +6615,29 @@ comm_point_delete(struct comm_point* c) http2_session_delete(c->h2_session); } } +#ifdef HAVE_NGTCP2 + if(c->doq_socket) + doq_server_socket_delete(c->doq_socket); +#endif ub_event_free(c->ev->ev); free(c->ev); free(c); } +#ifdef USE_DNSTAP +static void +send_reply_dnstap(struct dt_env* dtenv, + struct sockaddr* addr, socklen_t addrlen, + struct sockaddr_storage* client_addr, socklen_t client_addrlen, + enum comm_point_type type, void* ssl, sldns_buffer* buffer) +{ + log_addr(VERB_ALGO, "from local addr", (void*)addr, addrlen); + log_addr(VERB_ALGO, "response to client", client_addr, client_addrlen); + dt_msg_send_client_response(dtenv, client_addr, + (struct sockaddr_storage*)addr, type, ssl, buffer); +} +#endif + void comm_point_send_reply(struct comm_reply *repinfo) { @@ -4778,24 +6662,44 @@ comm_point_send_reply(struct comm_reply repinfo->remote_addrlen, 0); #ifdef USE_DNSTAP /* - * sending src (client)/dst (local service) addresses over DNSTAP from udp callback + * sending src (client)/dst (local service) addresses over + * DNSTAP from udp callback */ if(repinfo->c->dtenv != NULL && repinfo->c->dtenv->log_client_response_messages) { - log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr, repinfo->c->socket->addrlen); - log_addr(VERB_ALGO, "response to client", &repinfo->client_addr, repinfo->client_addrlen); - dt_msg_send_client_response(repinfo->c->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr, repinfo->c->type, repinfo->c->ssl, repinfo->c->buffer); + send_reply_dnstap(repinfo->c->dtenv, + repinfo->c->socket->addr, + repinfo->c->socket->addrlen, + &repinfo->client_addr, repinfo->client_addrlen, + repinfo->c->type, repinfo->c->ssl, + repinfo->c->buffer); } #endif } else { #ifdef USE_DNSTAP + struct dt_env* dtenv = +#ifdef HAVE_NGTCP2 + repinfo->c->doq_socket + ?repinfo->c->dtenv: +#endif + repinfo->c->tcp_parent->dtenv; + struct sldns_buffer* dtbuffer = repinfo->c->tcp_req_info + ?repinfo->c->tcp_req_info->spool_buffer + :repinfo->c->buffer; +#ifdef USE_DNSCRYPT + if(repinfo->c->dnscrypt && repinfo->is_dnscrypted) + dtbuffer = repinfo->c->buffer; +#endif /* - * sending src (client)/dst (local service) addresses over DNSTAP from TCP callback + * sending src (client)/dst (local service) addresses over + * DNSTAP from other callbacks */ - if(repinfo->c->tcp_parent->dtenv != NULL && repinfo->c->tcp_parent->dtenv->log_client_response_messages) { - log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr, repinfo->c->socket->addrlen); - log_addr(VERB_ALGO, "response to client", &repinfo->client_addr, repinfo->client_addrlen); - dt_msg_send_client_response(repinfo->c->tcp_parent->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr, repinfo->c->type, repinfo->c->ssl, - ( repinfo->c->tcp_req_info? repinfo->c->tcp_req_info->spool_buffer: repinfo->c->buffer )); + if(dtenv != NULL && dtenv->log_client_response_messages) { + send_reply_dnstap(dtenv, + repinfo->c->socket->addr, + repinfo->c->socket->addrlen, + &repinfo->client_addr, repinfo->client_addrlen, + repinfo->c->type, repinfo->c->ssl, + dtbuffer); } #endif if(repinfo->c->tcp_req_info) { @@ -4811,6 +6715,10 @@ comm_point_send_reply(struct comm_reply comm_point_start_listening(repinfo->c, -1, adjusted_tcp_timeout(repinfo->c)); return; +#ifdef HAVE_NGTCP2 + } else if(repinfo->c->doq_socket) { + doq_socket_send_reply(repinfo); +#endif } else { comm_point_start_listening(repinfo->c, -1, adjusted_tcp_timeout(repinfo->c)); @@ -4838,6 +6746,11 @@ comm_point_drop_reply(struct comm_reply* } reclaim_http_handler(repinfo->c); return; +#ifdef HAVE_NGTCP2 + } else if(repinfo->c->type == comm_doq) { + doq_socket_drop_reply(repinfo); + return; +#endif } reclaim_tcp_handler(repinfo->c); } Index: util/netevent.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/netevent.h,v diff -u -p -r1.23 netevent.h --- util/netevent.h 4 Sep 2024 09:36:41 -0000 1.23 +++ util/netevent.h 7 Feb 2025 21:25:44 -0000 @@ -65,6 +65,9 @@ #ifdef HAVE_NGHTTP2_NGHTTP2_H #include #endif +#ifdef HAVE_NGTCP2 +#include +#endif struct sldns_buffer; struct comm_point; @@ -72,6 +75,11 @@ struct comm_reply; struct tcl_list; struct ub_event_base; struct unbound_socket; +struct doq_server_socket; +struct doq_table; +struct doq_conn; +struct config_file; +struct ub_randstate; struct mesh_state; struct mesh_area; @@ -105,6 +113,8 @@ typedef int comm_point_callback_type(str #define NETEVENT_SLOW_ACCEPT_TIME 2000 /** timeout to slow down log print, so it does not spam the logs, in sec */ #define SLOW_LOG_TIME 10 +/** for doq, the maximum dcid length, in ngtcp2 it is 20. */ +#define DOQ_MAX_CIDLEN 24 /** * A communication point dispatcher. Thread specific. @@ -164,6 +174,19 @@ struct comm_reply { struct sockaddr_storage client_addr; /** the original address length */ socklen_t client_addrlen; +#ifdef HAVE_NGTCP2 + /** the doq ifindex, together with addr and localaddr in pktinfo, + * and dcid makes the doq_conn_key to find the connection */ + int doq_ifindex; + /** the doq dcid, the connection id used to find the connection */ + uint8_t doq_dcid[DOQ_MAX_CIDLEN]; + /** the length of the doq dcid */ + size_t doq_dcidlen; + /** the doq stream id where the query came in on */ + int64_t doq_streamid; + /** port number for doq */ + int doq_srcport; +#endif /* HAVE_NGTCP2 */ }; /** @@ -266,6 +289,11 @@ struct comm_point { /** maximum number of HTTP/2 streams per connection. Send in HTTP/2 * SETTINGS frame. */ uint32_t http2_max_streams; + /* -------- DoQ ------- */ +#ifdef HAVE_NGTCP2 + /** the doq server socket, with list of doq connections */ + struct doq_server_socket* doq_socket; +#endif /* -------- dnstap ------- */ /** the dnstap environment */ @@ -281,6 +309,8 @@ struct comm_point { comm_tcp, /** HTTP handler socket */ comm_http, + /** DOQ handler socket */ + comm_doq, /** AF_UNIX socket - for internal commands. */ comm_local, /** raw - not DNS format - for pipe readers and writers */ @@ -553,6 +583,30 @@ struct comm_point* comm_point_create_udp comm_point_callback_type* callback, void* callback_arg, struct unbound_socket* socket); /** + * Create an UDP comm point for DoQ. Calls malloc. + * setups the structure with the parameters you provide. + * @param base: in which base to alloc the commpoint. + * @param fd : file descriptor of open UDP socket. + * @param buffer: shared buffer by UDP sockets from this thread. + * @param callback: callback function pointer. + * @param callback_arg: will be passed to your callback function. + * @param socket: and opened socket properties will be passed to your callback function. + * @param table: the doq connection table for the host. + * @param rnd: random generator to use. + * @param ssl_service_key: the ssl service key file. + * @param ssl_service_pem: the ssl service pem file. + * @param cfg: config file struct. + * @return: returns the allocated communication point. NULL on error. + * Sets timeout to NULL. Turns off TCP options. + */ +struct comm_point* comm_point_create_doq(struct comm_base* base, + int fd, struct sldns_buffer* buffer, + comm_point_callback_type* callback, void* callback_arg, + struct unbound_socket* socket, struct doq_table* table, + struct ub_randstate* rnd, const char* ssl_service_key, + const char* ssl_service_pem, struct config_file* cfg); + +/** * Create a TCP listener comm point. Calls malloc. * Setups the structure with the parameters you provide. * Also Creates TCP Handlers, pre allocated for you. @@ -823,6 +877,16 @@ void comm_point_udp_ancil_callback(int f /** * This routine is published for checks and tests, and is only used internally. + * handle libevent callback for doq comm point. + * @param fd: file descriptor. + * @param event: event bits from libevent: + * EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT. + * @param arg: the comm_point structure. + */ +void comm_point_doq_callback(int fd, short event, void* arg); + +/** + * This routine is published for checks and tests, and is only used internally. * handle libevent callback for tcp accept comm point * @param fd: file descriptor. * @param event: event bits from libevent: @@ -957,6 +1021,106 @@ void http2_stream_add_meshstate(struct h /** Remove mesh state from stream. When the mesh state has been removed. */ void http2_stream_remove_mesh_state(struct http2_stream* h2_stream); + +/** + * DoQ socket address storage for IP4 or IP6 address. Smaller than + * the sockaddr_storage because not with af_unix pathnames. + */ +struct doq_addr_storage { + union { + struct sockaddr_in in; +#ifdef AF_INET6 + struct sockaddr_in6 in6; +#endif + } sockaddr; +}; + +/** + * The DoQ server socket information, for DNS over QUIC. + */ +struct doq_server_socket { + /** the doq connection table */ + struct doq_table* table; + /** random generator */ + struct ub_randstate* rnd; + /** if address validation is enabled */ + uint8_t validate_addr; + /** the ssl service key file */ + char* ssl_service_key; + /** the ssl service pem file */ + char* ssl_service_pem; + /** the ssl verify pem file */ + char* ssl_verify_pem; + /** the server scid length */ + int sv_scidlen; + /** the idle timeout in nanoseconds */ + uint64_t idle_timeout; + /** the static secret for the server */ + uint8_t* static_secret; + /** length of the static secret */ + size_t static_secret_len; + /** ssl context, SSL_CTX* */ + void* ctx; +#ifndef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT + /** quic method functions, SSL_QUIC_METHOD* */ + void* quic_method; +#endif + /** the comm point for this doq server socket */ + struct comm_point* cp; + /** the buffer for packets, doq in and out */ + struct sldns_buffer* pkt_buf; + /** the current doq connection when we are in callbacks to worker, + * so that we have the already locked structure at our disposal. */ + struct doq_conn* current_conn; + /** if the callback event on the fd has write flags */ + uint8_t event_has_write; + /** if there is a blocked packet in the blocked_pkt buffer */ + int have_blocked_pkt; + /** store blocked packet, a packet that could not be send on the + * nonblocking socket. It has to be sent later, when the write on + * the udp socket unblocks. */ + struct sldns_buffer* blocked_pkt; +#ifdef HAVE_NGTCP2 + /** the ecn info for the blocked packet, congestion information. */ + struct ngtcp2_pkt_info blocked_pkt_pi; +#endif + /** the packet destination for the blocked packet. */ + struct doq_pkt_addr* blocked_paddr; + /** timer for this worker on this comm_point to wait on. */ + struct comm_timer* timer; + /** the timer that is marked by the doq_socket as waited on. */ + struct timeval marked_time; + /** the current time for use by time functions, time_t. */ + time_t* now_tt; + /** the current time for use by time functions, timeval. */ + struct timeval* now_tv; + /** config file for the worker. */ + struct config_file* cfg; +}; + +/** + * DoQ packet address information. From pktinfo, stores local and remote + * address and ifindex, so the packet can be sent there. + */ +struct doq_pkt_addr { + /** the remote addr, and local addr */ + struct doq_addr_storage addr, localaddr; + /** length of addr and length of localaddr */ + socklen_t addrlen, localaddrlen; + /** interface index from pktinfo ancillary information */ + int ifindex; +}; + +/** Initialize the pkt addr with lengths set to sizeof. That is ready for + * a call to recv. */ +void doq_pkt_addr_init(struct doq_pkt_addr* paddr); + +/** send doq packet over UDP. */ +void doq_send_pkt(struct comm_point* c, struct doq_pkt_addr* paddr, + uint32_t ecn); + +/** doq timer callback function. */ +void doq_timer_cb(void* arg); /** * This routine is published for checks and tests, and is only used internally. Index: util/data/msgparse.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/data/msgparse.h,v diff -u -p -r1.12 msgparse.h --- util/data/msgparse.h 4 Sep 2024 09:36:41 -0000 1.12 +++ util/data/msgparse.h 7 Feb 2025 21:25:45 -0000 @@ -89,6 +89,8 @@ extern time_t MIN_NEG_TTL; extern int SERVE_EXPIRED; /** Time to serve records after expiration */ extern time_t SERVE_EXPIRED_TTL; +/** Reset serve expired TTL after failed update attempt */ +extern time_t SERVE_EXPIRED_TTL_RESET; /** TTL to use for expired records */ extern time_t SERVE_EXPIRED_REPLY_TTL; /** Negative cache time (for entries without any RRs.) */ Index: util/data/msgreply.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/data/msgreply.c,v diff -u -p -r1.25 msgreply.c --- util/data/msgreply.c 13 Jun 2024 14:30:28 -0000 1.25 +++ util/data/msgreply.c 7 Feb 2025 21:25:45 -0000 @@ -67,6 +67,8 @@ time_t MIN_NEG_TTL = 0; int SERVE_EXPIRED = 0; /** Time to serve records after expiration */ time_t SERVE_EXPIRED_TTL = 0; +/** Reset serve expired TTL after failed update attempt */ +time_t SERVE_EXPIRED_TTL_RESET = 0; /** TTL to use for expired records */ time_t SERVE_EXPIRED_REPLY_TTL = 30; /** If we serve the original TTL or decrementing TTLs */ @@ -95,8 +97,9 @@ parse_create_qinfo(sldns_buffer* pkt, st /** constructor for replyinfo */ struct reply_info* construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd, - time_t ttl, time_t prettl, time_t expttl, size_t an, size_t ns, - size_t ar, size_t total, enum sec_status sec, sldns_ede_code reason_bogus) + time_t ttl, time_t prettl, time_t expttl, time_t norecttl, size_t an, + size_t ns, size_t ar, size_t total, enum sec_status sec, + sldns_ede_code reason_bogus) { struct reply_info* rep; /* rrset_count-1 because the first ref is part of the struct. */ @@ -114,6 +117,7 @@ construct_reply_info_base(struct regiona rep->ttl = ttl; rep->prefetch_ttl = prettl; rep->serve_expired_ttl = expttl; + rep->serve_expired_norec_ttl = norecttl; rep->an_numrrsets = an; rep->ns_numrrsets = ns; rep->ar_numrrsets = ar; @@ -139,8 +143,8 @@ static int parse_create_repinfo(struct msg_parse* msg, struct reply_info** rep, struct regional* region) { - *rep = construct_reply_info_base(region, msg->flags, msg->qdcount, 0, - 0, 0, msg->an_rrsets, msg->ns_rrsets, msg->ar_rrsets, + *rep = construct_reply_info_base(region, msg->flags, msg->qdcount, 0, + 0, 0, 0, msg->an_rrsets, msg->ns_rrsets, msg->ar_rrsets, msg->rrset_count, sec_status_unchecked, LDNS_EDE_NONE); if(!*rep) return 0; @@ -171,6 +175,32 @@ reply_info_alloc_rrset_keys(struct reply return 1; } +int +reply_info_can_answer_expired(struct reply_info* rep, time_t timenow) +{ + log_assert(rep->ttl < timenow); + /* Really expired */ + if(SERVE_EXPIRED_TTL && rep->serve_expired_ttl < timenow) return 0; + /* Ignore expired failure answers */ + if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR && + FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN && + FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_YXDOMAIN) return 0; + return 1; +} + +int reply_info_could_use_expired(struct reply_info* rep, time_t timenow) +{ + log_assert(rep->ttl < timenow); + /* Really expired */ + if(SERVE_EXPIRED_TTL && rep->serve_expired_ttl < timenow && + !SERVE_EXPIRED_TTL_RESET) return 0; + /* Ignore expired failure answers */ + if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR && + FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN && + FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_YXDOMAIN) return 0; + return 1; +} + struct reply_info * make_new_reply_info(const struct reply_info* rep, struct regional* region, size_t an_numrrsets, size_t copy_rrsets) @@ -185,7 +215,8 @@ make_new_reply_info(const struct reply_i * so the total number of RRsets is an_numrrsets. */ new_rep = construct_reply_info_base(region, rep->flags, rep->qdcount, rep->ttl, rep->prefetch_ttl, - rep->serve_expired_ttl, an_numrrsets, 0, 0, an_numrrsets, + rep->serve_expired_ttl, rep->serve_expired_norec_ttl, + an_numrrsets, 0, 0, an_numrrsets, sec_status_insecure, LDNS_EDE_NONE); if(!new_rep) return NULL; @@ -486,6 +517,8 @@ parse_copy_decompress(sldns_buffer* pkt, } rep->prefetch_ttl = PREFETCH_TTL_CALC(rep->ttl); rep->serve_expired_ttl = rep->ttl + SERVE_EXPIRED_TTL; + /* rep->serve_expired_norec_ttl should stay at 0 */ + log_assert(rep->serve_expired_norec_ttl == 0); return 1; } @@ -568,6 +601,9 @@ reply_info_set_ttls(struct reply_info* r rep->ttl += timenow; rep->prefetch_ttl += timenow; rep->serve_expired_ttl += timenow; + /* Don't set rep->serve_expired_norec_ttl; this should only be set + * on cached records when encountering an error */ + log_assert(rep->serve_expired_norec_ttl == 0); for(i=0; irrset_count; i++) { struct packed_rrset_data* data = (struct packed_rrset_data*) rep->ref[i].key->entry.data; @@ -763,6 +799,7 @@ reply_info_copy(struct reply_info* rep, struct reply_info* cp; cp = construct_reply_info_base(region, rep->flags, rep->qdcount, rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl, + rep->serve_expired_norec_ttl, rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets, rep->rrset_count, rep->security, rep->reason_bogus); if(!cp) Index: util/data/msgreply.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/data/msgreply.h,v diff -u -p -r1.15 msgreply.h --- util/data/msgreply.h 13 Apr 2024 12:24:57 -0000 1.15 +++ util/data/msgreply.h 7 Feb 2025 21:25:45 -0000 @@ -145,7 +145,7 @@ struct reply_info { /** 32 bit padding to pad struct member alignment to 64 bits. */ uint32_t padding; - /** + /** * TTL of the entire reply (for negative caching). * only for use when there are 0 RRsets in this message. * if there are RRsets, check those instead. @@ -158,13 +158,25 @@ struct reply_info { */ time_t prefetch_ttl; - /** + /** * Reply TTL extended with serve expired TTL, to limit time to serve * expired message. */ time_t serve_expired_ttl; /** + * TTL for an expired entry to be used without attempting recursion + * since a previous recursion attempt failed to update the message. + * This is just an efficiency timer when serve-expired-client-timeout + * is configured. It will make Unbound immediately reply with the + * expired entry instead of trying resolution first. + * It is set on cached entries by modules that identified problems + * while resolving, e.g., failed upstreams from Iterator, or failed + * validation from Validator. + */ + time_t serve_expired_norec_ttl; + + /** * The security status from DNSSEC validation of this message. */ enum sec_status security; @@ -244,6 +256,7 @@ struct msgreply_entry { * @param ttl: TTL of replyinfo * @param prettl: prefetch ttl * @param expttl: serve expired ttl + * @param norecttl: serve expired no recursion ttl * @param an: an count * @param ns: ns count * @param ar: ar count @@ -255,8 +268,8 @@ struct msgreply_entry { */ struct reply_info* construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd, - time_t ttl, time_t prettl, time_t expttl, size_t an, size_t ns, - size_t ar, size_t total, enum sec_status sec, + time_t ttl, time_t prettl, time_t expttl, time_t norecttl, size_t an, + size_t ns, size_t ar, size_t total, enum sec_status sec, sldns_ede_code reason_bogus); /** @@ -398,6 +411,24 @@ struct reply_info* reply_info_copy(struc */ int reply_info_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, struct regional* region); + +/** + * Check if an *expired* (checked by the caller already) reply info can be used + * as an expired answer. + * @param rep: expired reply info to check. + * @param timenow: the current time. + * @return 1 if it can be used as an answer, 0 otherwise. + */ +int reply_info_can_answer_expired(struct reply_info* rep, time_t timenow); + +/** + * Check if an *expired* (checked by the caller already) reply info could be + * useful data to stay in the cache. + * @param rep: expired reply info to check. + * @param timenow: the current time. + * @return 1 if it is useful, 0 otherwise. + */ +int reply_info_could_use_expired(struct reply_info* rep, time_t timenow); /* * Create a new reply_info based on 'rep'. The new info is based on Index: util/data/packed_rrset.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/data/packed_rrset.h,v diff -u -p -r1.7 packed_rrset.h --- util/data/packed_rrset.h 23 Feb 2022 12:04:06 -0000 1.7 +++ util/data/packed_rrset.h 7 Feb 2025 21:25:45 -0000 @@ -68,6 +68,8 @@ typedef uint64_t rrset_id_type; * actual network. But messages with these records in it can be stored in * the cache and retrieved for a reply. */ #define PACKED_RRSET_RPZ 0x8 +/** this rrset is A/AAAA and is an unverified glue record */ +#define PACKED_RRSET_UNVERIFIED_GLUE 0x10 /** number of rrs and rrsets for integer overflow protection. More than * this is not really possible (64K packet has much less RRs and RRsets) in @@ -96,6 +98,7 @@ struct packed_rrset_key { * o PACKED_RRSET_SOA_NEG * o PACKED_RRSET_FIXEDTTL (not supposed to be cached) * o PACKED_RRSET_RPZ + * o PACKED_RRSET_UNVERIFIED_GLUE */ uint32_t flags; /** the rrset type in network format */ Index: validator/val_neg.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/validator/val_neg.c,v diff -u -p -r1.9 val_neg.c --- validator/val_neg.c 5 Sep 2023 11:12:11 -0000 1.9 +++ validator/val_neg.c 7 Feb 2025 21:25:45 -0000 @@ -823,7 +823,8 @@ void neg_insert_data(struct val_neg_cach it <= neg->nsec3_max_iter && (h != zone->nsec3_hash || it != zone->nsec3_iter || slen != zone->nsec3_saltlen || - memcmp(zone->nsec3_salt, s, slen) != 0)) { + (slen != 0 && zone->nsec3_salt && s + && memcmp(zone->nsec3_salt, s, slen) != 0))) { if(slen > 0) { uint8_t* sa = memdup(s, slen); @@ -1206,7 +1207,8 @@ neg_params_ok(struct val_neg_zone* zone, return 0; return (h == zone->nsec3_hash && it == zone->nsec3_iter && slen == zone->nsec3_saltlen && - memcmp(zone->nsec3_salt, s, slen) == 0); + (slen != 0 && zone->nsec3_salt && s + && memcmp(zone->nsec3_salt, s, slen) == 0)); } /** get next closer for nsec3 proof */ Index: validator/val_nsec3.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/validator/val_nsec3.c,v diff -u -p -r1.9 val_nsec3.c --- validator/val_nsec3.c 4 Sep 2024 09:36:41 -0000 1.9 +++ validator/val_nsec3.c 7 Feb 2025 21:25:45 -0000 @@ -565,7 +565,8 @@ nsec3_get_hashed(sldns_buffer* buf, uint sldns_buffer_clear(buf); sldns_buffer_write(buf, nm, nmlen); query_dname_tolower(sldns_buffer_begin(buf)); - sldns_buffer_write(buf, salt, saltlen); + if(saltlen != 0) + sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); hash_len = nsec3_hash_algo_size_supported(algo); if(hash_len == 0) { @@ -580,7 +581,8 @@ nsec3_get_hashed(sldns_buffer* buf, uint for(i=0; insec3_keysize); free(ve->nsec3_maxiter); - ve->nsec3_keysize = (size_t*)calloc(sizeof(size_t), (size_t)c); - ve->nsec3_maxiter = (size_t*)calloc(sizeof(size_t), (size_t)c); + ve->nsec3_keysize = (size_t*)calloc((size_t)c, sizeof(size_t)); + ve->nsec3_maxiter = (size_t*)calloc((size_t)c, sizeof(size_t)); if(!ve->nsec3_keysize || !ve->nsec3_maxiter) { log_err("out of memory"); return 0; @@ -2435,6 +2435,8 @@ processFinished(struct module_qstate* qs /* if the result is bogus - set message ttl to bogus ttl to avoid * endless bogus revalidation */ if(vq->orig_msg->rep->security == sec_status_bogus) { + struct msgreply_entry* e; + /* see if we can try again to fetch data */ if(vq->restart_count < ve->max_restart) { verbose(VERB_ALGO, "validation failed, " @@ -2449,10 +2451,51 @@ processFinished(struct module_qstate* qs return 0; } + if(qstate->env->cfg->serve_expired && + (e=msg_cache_lookup(qstate->env, qstate->qinfo.qname, + qstate->qinfo.qname_len, qstate->qinfo.qtype, + qstate->qinfo.qclass, qstate->query_flags, + 0 /*now; allow expired*/, + 1 /*wr; we may update the data*/))) { + struct reply_info* rep = (struct reply_info*)e->entry.data; + if(rep && rep->security > sec_status_bogus && + (!qstate->env->cfg->serve_expired_ttl || + qstate->env->cfg->serve_expired_ttl_reset || + *qstate->env->now <= rep->serve_expired_ttl)) { + verbose(VERB_ALGO, "validation failed but " + "previously cached valid response " + "exists; set serve-expired-norec-ttl " + "for response in cache"); + rep->serve_expired_norec_ttl = NORR_TTL + + *qstate->env->now; + if(qstate->env->cfg->serve_expired_ttl_reset && + *qstate->env->now + qstate->env->cfg->serve_expired_ttl + > rep->serve_expired_ttl) { + verbose(VERB_ALGO, "reset serve-expired-ttl for " + "valid response in cache"); + rep->serve_expired_ttl = *qstate->env->now + + qstate->env->cfg->serve_expired_ttl; + } + /* Return an error response. + * If serve-expired-client-timeout is enabled, + * the client-timeout logic will try to find an + * (expired) answer in the cache as last + * resort. If it is not enabled, expired + * answers are already used before the mesh + * activation. */ + qstate->return_rcode = LDNS_RCODE_SERVFAIL; + qstate->return_msg = NULL; + qstate->ext_state[id] = module_finished; + lock_rw_unlock(&e->entry.lock); + return 0; + } + lock_rw_unlock(&e->entry.lock); + } + vq->orig_msg->rep->ttl = ve->bogus_ttl; vq->orig_msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(vq->orig_msg->rep->ttl); - vq->orig_msg->rep->serve_expired_ttl = + vq->orig_msg->rep->serve_expired_ttl = vq->orig_msg->rep->ttl + qstate->env->cfg->serve_expired_ttl; if((qstate->env->cfg->val_log_level >= 1 || qstate->env->cfg->log_servfail) && @@ -2518,8 +2561,9 @@ processFinished(struct module_qstate* qs * to check if from parentNS */ if(!qstate->no_cache_store) { if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, - vq->orig_msg->rep, 0, qstate->prefetch_leeway, 0, NULL, - qstate->query_flags, qstate->qstarttime)) { + vq->orig_msg->rep, 0, qstate->prefetch_leeway, + 0, qstate->region, qstate->query_flags, + qstate->qstarttime)) { log_err("out of memory caching validator results"); } } @@ -2527,7 +2571,7 @@ processFinished(struct module_qstate* qs /* for a referral, store the verified RRsets */ /* and this does not get prefetched, so no leeway */ if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, - vq->orig_msg->rep, 1, 0, 0, NULL, + vq->orig_msg->rep, 1, 0, 0, qstate->region, qstate->query_flags, qstate->qstarttime)) { log_err("out of memory caching validator results"); } @@ -2617,6 +2661,14 @@ val_operate(struct module_qstate* qstate qstate->ext_state[id] = module_finished; return; } + if(qstate->rpz_applied) { + verbose(VERB_ALGO, "rpz applied, mark it as insecure"); + if(qstate->return_msg) + qstate->return_msg->rep->security = + sec_status_insecure; + qstate->ext_state[id] = module_finished; + return; + } /* qclass ANY should have validation result from spawned * queries. If we get here, it is bogus or an internal error */ if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) { @@ -3053,6 +3105,14 @@ process_ds_response(struct module_qstate int ret; *suspend = 0; vq->empty_DS_name = NULL; + if(sub_qstate && sub_qstate->rpz_applied) { + verbose(VERB_ALGO, "rpz was applied to the DS lookup, " + "make it insecure"); + vq->key_entry = NULL; + vq->state = VAL_FINISHED_STATE; + vq->chase_reply->security = sec_status_insecure; + return; + } ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske, sub_qstate); if(ret != 0) { @@ -3145,6 +3205,15 @@ process_dnskey_response(struct module_qs char reasonbuf[256]; char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; + + if(sub_qstate && sub_qstate->rpz_applied) { + verbose(VERB_ALGO, "rpz was applied to the DNSKEY lookup, " + "make it insecure"); + vq->key_entry = NULL; + vq->state = VAL_FINISHED_STATE; + vq->chase_reply->security = sec_status_insecure; + return; + } if(rcode == LDNS_RCODE_NOERROR) dnskey = reply_find_answer_rrset(qinfo, msg->rep); Index: validator/validator.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/validator/validator.h,v diff -u -p -r1.9 validator.h --- validator/validator.h 13 Feb 2024 12:57:11 -0000 1.9 +++ validator/validator.h 7 Feb 2025 21:25:45 -0000 @@ -159,7 +159,7 @@ struct val_qstate { * The query restart count */ int restart_count; - /** The blacklist saved for chainoftrust elements */ + /** The blacklist saved for chain of trust elements */ struct sock_list* chain_blacklist; /**