diff -u -r -N squid-4.11/acinclude/os-deps.m4 squid-4.12/acinclude/os-deps.m4
--- squid-4.11/acinclude/os-deps.m4	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/acinclude/os-deps.m4	2020-06-08 03:42:16.000000000 +1200
@@ -925,11 +925,13 @@
 ## Solaris 10+ backported IPv6 NAT to their IPFilter v4.1 instead of using v5
   AC_CHECK_MEMBERS([
     struct natlookup.nl_inipaddr.in6,
-    struct natlookup.nl_realipaddr.in6
-  ],,,[
+    struct natlookup.nl_realipaddr.in6],,,[
 #if USE_SOLARIS_IPFILTER_MINOR_T_HACK
 #define minor_t fubar
 #endif
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
 #if HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
@@ -955,7 +957,11 @@
 #elif HAVE_NETINET_IP_FIL_H
 #include <netinet/ip_fil.h>
 #endif
+#if HAVE_IP_NAT_H
 #include <ip_nat.h>
+#elif HAVE_NETINET_IP_NAT_H
+#include <netinet/ip_nat.h>
+#endif
   ])
 
 ])
diff -u -r -N squid-4.11/ChangeLog squid-4.12/ChangeLog
--- squid-4.11/ChangeLog	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/ChangeLog	2020-06-08 03:42:16.000000000 +1200
@@ -1,3 +1,17 @@
+Changes to squid-4.12 (05 Jun 2020):
+
+	- Regression Fix: Revert to slow search for new SMP shm pages
+	- Bug 5045: ext_edirectory_userip_acl is missing include files
+	- Bug 5041: Missing Debug::Extra breaks build on hosts with systemd
+	- Bug 5030: Negative responses are never cached
+	- HTTP: validate Content-Length value prefix
+	- HTTP: add flexible RFC 3986 URI encoder
+	- SslBump: disable OpenSSL TLSv1.3 support for older TLS traffic
+	- Tests: Support passing a custom config.cache to test builds
+	- Fix IPFilter IPv6 detection, especially on NetBSD
+	- Fix stall if transaction overwrites a recently active cache entry
+	- ... and some compile fixes
+
 Changes to squid-4.11 (18 Apr 2020):
 
 	- Bug 5036: capital 'L's in logs when daemon queue overflows
diff -u -r -N squid-4.11/configure squid-4.12/configure
--- squid-4.11/configure	2020-04-20 00:39:06.000000000 +1200
+++ squid-4.12/configure	2020-06-09 19:15:48.000000000 +1200
@@ -1,7 +1,7 @@
 #! /bin/sh
 # From configure.ac Revision.
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for Squid Web Proxy 4.11.
+# Generated by GNU Autoconf 2.69 for Squid Web Proxy 4.12.
 #
 # Report bugs to <http://bugs.squid-cache.org/>.
 #
@@ -595,8 +595,8 @@
 # Identity of this package.
 PACKAGE_NAME='Squid Web Proxy'
 PACKAGE_TARNAME='squid'
-PACKAGE_VERSION='4.11'
-PACKAGE_STRING='Squid Web Proxy 4.11'
+PACKAGE_VERSION='4.12'
+PACKAGE_STRING='Squid Web Proxy 4.12'
 PACKAGE_BUGREPORT='http://bugs.squid-cache.org/'
 PACKAGE_URL=''
 
@@ -1656,7 +1656,7 @@
   # 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 Squid Web Proxy 4.11 to adapt to many kinds of systems.
+\`configure' configures Squid Web Proxy 4.12 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1727,7 +1727,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Squid Web Proxy 4.11:";;
+     short | recursive ) echo "Configuration of Squid Web Proxy 4.12:";;
    esac
   cat <<\_ACEOF
 
@@ -2166,7 +2166,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Squid Web Proxy configure 4.11
+Squid Web Proxy configure 4.12
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -3270,7 +3270,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by Squid Web Proxy $as_me 4.11, which was
+It was created by Squid Web Proxy $as_me 4.12, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -4137,7 +4137,7 @@
 
 # Define the identity of the package.
  PACKAGE='squid'
- VERSION='4.11'
+ VERSION='4.12'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -21120,8 +21120,9 @@
 $as_echo "$as_me: With dl" >&6;}
 fi
 
+
+
 ## check for atomics library before anything that might need it
-# AC_SEARCH_LIBS pollutes LIBS
 
 # save state, key is LIBATOMIC
 LIBATOMIC_CFLAGS="${CFLAGS}"
@@ -21138,63 +21139,76 @@
     eval "${squid_util_var_tosave2}=\"${squid_util_var_tosave}\""
 done
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing __atomic_load_8" >&5
-$as_echo_n "checking for library containing __atomic_load_8... " >&6; }
-if ${ac_cv_search___atomic_load_8+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_func_search_save_LIBS=$LIBS
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether linking without -latomic works" >&5
+$as_echo_n "checking whether linking without -latomic works... " >&6; }
+  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.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char __atomic_load_8 ();
-int
-main ()
-{
-return __atomic_load_8 ();
-  ;
-  return 0;
-}
+
+#include <atomic>
+#include <cstdint>
+      int
+      main(int, char **) {
+          return std::atomic<uint64_t>{}.is_lock_free() ? 0 : 1;
+      }
+
 _ACEOF
-for ac_lib in '' atomic; do
-  if test -z "$ac_lib"; then
-    ac_res="none required"
-  else
-    ac_res=-l$ac_lib
-    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
-  fi
-  if ac_fn_cxx_try_link "$LINENO"; then :
-  ac_cv_search___atomic_load_8=$ac_res
+if ac_fn_cxx_try_link "$LINENO"; then :
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+    libatomic_checker_result="yes"
+else
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    libatomic_checker_result="no"
+
 fi
 rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext
-  if ${ac_cv_search___atomic_load_8+:} false; then :
-  break
-fi
-done
-if ${ac_cv_search___atomic_load_8+:} false; then :
+    conftest$ac_exeext conftest.$ac_ext
+if test "x$libatomic_checker_result" = "xno"; then :
+
+  LIBS="$LIBS -latomic"
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether linking with -latomic works" >&5
+$as_echo_n "checking whether linking with -latomic works... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+#include <atomic>
+#include <cstdint>
+      int
+      main(int, char **) {
+          return std::atomic<uint64_t>{}.is_lock_free() ? 0 : 1;
+      }
 
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+    libatomic_checker_result="yes"
 else
-  ac_cv_search___atomic_load_8=no
-fi
-rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search___atomic_load_8" >&5
-$as_echo "$ac_cv_search___atomic_load_8" >&6; }
-ac_res=$ac_cv_search___atomic_load_8
-if test "$ac_res" != no; then :
-  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
 
-  test "$ac_res" = "none required" || ATOMICLIB=$ac_res
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    libatomic_checker_result="no"
+
 fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  if test "x$libatomic_checker_result" = "xyes"; then :
+
+    ATOMICLIB="-latomic"
+else
+
+    as_fn_error $? "Required library libatomic not found." "$LINENO" 5
 
+fi
+fi
 
 # rollback state, key is LIBATOMIC
 CFLAGS="${LIBATOMIC_CFLAGS}"
@@ -33915,6 +33929,7 @@
   fi
   if test "x$SYSTEMD_LIBS" != "x" ; then
     CXXFLAGS="$SYSTEMD_CFLAGS $CXXFLAGS"
+    LDFLAGS="$SYSTEMD_LIBS $LDFLAGS"
 
 $as_echo "#define USE_SYSTEMD 1" >>confdefs.h
 
@@ -42343,6 +42358,9 @@
 #if USE_SOLARIS_IPFILTER_MINOR_T_HACK
 #define minor_t fubar
 #endif
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
 #if HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
@@ -42368,7 +42386,11 @@
 #elif HAVE_NETINET_IP_FIL_H
 #include <netinet/ip_fil.h>
 #endif
+#if HAVE_IP_NAT_H
 #include <ip_nat.h>
+#elif HAVE_NETINET_IP_NAT_H
+#include <netinet/ip_nat.h>
+#endif
 
 "
 if test "x$ac_cv_member_struct_natlookup_nl_inipaddr_in6" = xyes; then :
@@ -42379,11 +42401,13 @@
 
 
 fi
-ac_fn_cxx_check_member "$LINENO" "struct natlookup" "nl_realipaddr.in6"
-   "ac_cv_member_struct_natlookup_nl_realipaddr_in6___" "
+ac_fn_cxx_check_member "$LINENO" "struct natlookup" "nl_realipaddr.in6" "ac_cv_member_struct_natlookup_nl_realipaddr_in6" "
 #if USE_SOLARIS_IPFILTER_MINOR_T_HACK
 #define minor_t fubar
 #endif
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
 #if HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
@@ -42409,13 +42433,17 @@
 #elif HAVE_NETINET_IP_FIL_H
 #include <netinet/ip_fil.h>
 #endif
+#if HAVE_IP_NAT_H
 #include <ip_nat.h>
+#elif HAVE_NETINET_IP_NAT_H
+#include <netinet/ip_nat.h>
+#endif
 
 "
-if test "x$ac_cv_member_struct_natlookup_nl_realipaddr_in6___" = xyes; then :
+if test "x$ac_cv_member_struct_natlookup_nl_realipaddr_in6" = xyes; then :
 
 cat >>confdefs.h <<_ACEOF
-#define HAVE_STRUCT_NATLOOKUP_NL_REALIPADDR_IN6___ 1
+#define HAVE_STRUCT_NATLOOKUP_NL_REALIPADDR_IN6 1
 _ACEOF
 
 
@@ -44582,7 +44610,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by Squid Web Proxy $as_me 4.11, which was
+This file was extended by Squid Web Proxy $as_me 4.12, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -44648,7 +44676,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-Squid Web Proxy config.status 4.11
+Squid Web Proxy config.status 4.12
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff -u -r -N squid-4.11/configure.ac squid-4.12/configure.ac
--- squid-4.11/configure.ac	2020-04-20 00:39:06.000000000 +1200
+++ squid-4.12/configure.ac	2020-06-09 19:15:47.000000000 +1200
@@ -5,7 +5,7 @@
 ## Please see the COPYING and CONTRIBUTORS files for details.
 ##
 
-AC_INIT([Squid Web Proxy],[4.11],[http://bugs.squid-cache.org/],[squid])
+AC_INIT([Squid Web Proxy],[4.12],[http://bugs.squid-cache.org/],[squid])
 AC_PREREQ(2.61)
 AC_CONFIG_HEADERS([include/autoconf.h])
 AC_CONFIG_AUX_DIR(cfgaux)
@@ -462,11 +462,33 @@
   AC_MSG_NOTICE([With dl])
 fi
 
+AC_DEFUN([LIBATOMIC_CHECKER],[
+  AC_MSG_CHECKING(whether linking $1 -latomic works)
+  AC_LINK_IFELSE([
+    AC_LANG_SOURCE([[
+#include <atomic>
+#include <cstdint>
+      int
+      main(int, char **) {
+          return std::atomic<uint64_t>{}.is_lock_free() ? 0 : 1;
+      }
+  ]])],[
+    AC_MSG_RESULT(yes)
+    libatomic_checker_result="yes"],[
+    AC_MSG_RESULT(no)
+    libatomic_checker_result="no"
+])])
+
 ## check for atomics library before anything that might need it
-# AC_SEARCH_LIBS pollutes LIBS
 SQUID_STATE_SAVE(LIBATOMIC)
-AC_SEARCH_LIBS([__atomic_load_8],[atomic],[
-  test "$ac_res" = "none required" || ATOMICLIB=$ac_res],[])
+LIBATOMIC_CHECKER(without)
+AS_IF([test "x$libatomic_checker_result" = "xno"],[
+  LIBS="$LIBS -latomic"
+  LIBATOMIC_CHECKER(with)
+  AS_IF([test "x$libatomic_checker_result" = "xyes"],[
+    ATOMICLIB="-latomic"],[
+    AC_MSG_ERROR([Required library libatomic not found.])
+])])
 SQUID_STATE_ROLLBACK(LIBATOMIC)
 AC_SUBST(ATOMICLIB)
 
@@ -2162,6 +2184,7 @@
   fi
   if test "x$SYSTEMD_LIBS" != "x" ; then
     CXXFLAGS="$SYSTEMD_CFLAGS $CXXFLAGS"
+    LDFLAGS="$SYSTEMD_LIBS $LDFLAGS"
     AC_DEFINE(USE_SYSTEMD,1,[systemd support is available])
   else
     with_systemd=no
diff -u -r -N squid-4.11/CONTRIBUTORS squid-4.12/CONTRIBUTORS
--- squid-4.11/CONTRIBUTORS	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/CONTRIBUTORS	2020-06-08 03:42:16.000000000 +1200
@@ -25,6 +25,7 @@
     Alex Wu <alex_wu2012@hotmail.com>
     Alin Nastac <mrness@gentoo.org>
     Alter <alter@alter.org.ua>
+    Amit Klein <amit.klein@safebreach.com>
     Amos Jeffries
     Amos Jeffries <amosjeffries@squid-cache.org>
     Amos Jeffries <squid3@treenet.co.nz>
diff -u -r -N squid-4.11/doc/release-notes/release-4.html squid-4.12/doc/release-notes/release-4.html
--- squid-4.11/doc/release-notes/release-4.html	2020-04-20 00:50:33.000000000 +1200
+++ squid-4.12/doc/release-notes/release-4.html	2020-06-09 19:27:13.000000000 +1200
@@ -1,12 +1,12 @@
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
 <HTML>
 <HEAD>
- <META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.76">
+ <META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.80">
  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
- <TITLE>Squid 4.11 release notes</TITLE>
+ <TITLE>Squid 4.12 release notes</TITLE>
 </HEAD>
 <BODY>
-<H1>Squid 4.11 release notes</H1>
+<H1>Squid 4.12 release notes</H1>
 
 <H2>Squid Developers</H2>
 <HR>
@@ -64,7 +64,7 @@
 <HR>
 <H2><A NAME="s1">1.</A> <A HREF="#toc1">Notice</A></H2>
 
-<P>The Squid Team are pleased to announce the release of Squid-4.11.</P>
+<P>The Squid Team are pleased to announce the release of Squid-4.12.</P>
 <P>This new release is available for download from 
 <A HREF="http://www.squid-cache.org/Versions/v4/">http://www.squid-cache.org/Versions/v4/</A> or the
 <A HREF="http://www.squid-cache.org/Download/http-mirrors.html">mirrors</A>.</P>
diff -u -r -N squid-4.11/include/autoconf.h.in squid-4.12/include/autoconf.h.in
--- squid-4.11/include/autoconf.h.in	2020-04-20 00:39:00.000000000 +1200
+++ squid-4.12/include/autoconf.h.in	2020-06-09 19:15:40.000000000 +1200
@@ -1044,8 +1044,8 @@
 /* Define to 1 if `nl_inipaddr.in6' is a member of `struct natlookup'. */
 #undef HAVE_STRUCT_NATLOOKUP_NL_INIPADDR_IN6
 
-/* Define to 1 if `nl_realipaddr.in6' is a member of `struct natlookup '. */
-#undef HAVE_STRUCT_NATLOOKUP_NL_REALIPADDR_IN6___
+/* Define to 1 if `nl_realipaddr.in6' is a member of `struct natlookup'. */
+#undef HAVE_STRUCT_NATLOOKUP_NL_REALIPADDR_IN6
 
 /* The system provides struct rusage */
 #undef HAVE_STRUCT_RUSAGE
diff -u -r -N squid-4.11/include/version.h squid-4.12/include/version.h
--- squid-4.11/include/version.h	2020-04-20 00:39:06.000000000 +1200
+++ squid-4.12/include/version.h	2020-06-09 19:15:48.000000000 +1200
@@ -7,7 +7,7 @@
  */
 
 #ifndef SQUID_RELEASE_TIME
-#define SQUID_RELEASE_TIME 1587299937
+#define SQUID_RELEASE_TIME 1591686937
 #endif
 
 /*
diff -u -r -N squid-4.11/RELEASENOTES.html squid-4.12/RELEASENOTES.html
--- squid-4.11/RELEASENOTES.html	2020-04-20 00:50:33.000000000 +1200
+++ squid-4.12/RELEASENOTES.html	2020-06-09 19:27:13.000000000 +1200
@@ -1,12 +1,12 @@
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
 <HTML>
 <HEAD>
- <META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.76">
+ <META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.80">
  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
- <TITLE>Squid 4.11 release notes</TITLE>
+ <TITLE>Squid 4.12 release notes</TITLE>
 </HEAD>
 <BODY>
-<H1>Squid 4.11 release notes</H1>
+<H1>Squid 4.12 release notes</H1>
 
 <H2>Squid Developers</H2>
 <HR>
@@ -64,7 +64,7 @@
 <HR>
 <H2><A NAME="s1">1.</A> <A HREF="#toc1">Notice</A></H2>
 
-<P>The Squid Team are pleased to announce the release of Squid-4.11.</P>
+<P>The Squid Team are pleased to announce the release of Squid-4.12.</P>
 <P>This new release is available for download from 
 <A HREF="http://www.squid-cache.org/Versions/v4/">http://www.squid-cache.org/Versions/v4/</A> or the
 <A HREF="http://www.squid-cache.org/Download/http-mirrors.html">mirrors</A>.</P>
diff -u -r -N squid-4.11/src/acl/external/delayer/ext_delayer_acl.8 squid-4.12/src/acl/external/delayer/ext_delayer_acl.8
--- squid-4.11/src/acl/external/delayer/ext_delayer_acl.8	2020-04-20 00:50:35.000000000 +1200
+++ squid-4.12/src/acl/external/delayer/ext_delayer_acl.8	2020-06-09 19:27:16.000000000 +1200
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "EXT_DELAYER_ACL 8"
-.TH EXT_DELAYER_ACL 8 "2020-04-19" "perl v5.28.1" "User Contributed Perl Documentation"
+.TH EXT_DELAYER_ACL 8 "2020-06-09" "perl v5.30.3" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -u -r -N squid-4.11/src/acl/external/eDirectory_userip/ext_edirectory_userip_acl.cc squid-4.12/src/acl/external/eDirectory_userip/ext_edirectory_userip_acl.cc
--- squid-4.11/src/acl/external/eDirectory_userip/ext_edirectory_userip_acl.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/acl/external/eDirectory_userip/ext_edirectory_userip_acl.cc	2020-06-08 03:42:16.000000000 +1200
@@ -69,6 +69,12 @@
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
 
 #ifdef HELPER_INPUT_BUFFER
 #define EDUI_MAXLEN     HELPER_INPUT_BUFFER
diff -u -r -N squid-4.11/src/acl/external/kerberos_ldap_group/support_krb5.cc squid-4.12/src/acl/external/kerberos_ldap_group/support_krb5.cc
--- squid-4.11/src/acl/external/kerberos_ldap_group/support_krb5.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/acl/external/kerberos_ldap_group/support_krb5.cc	2020-06-08 03:42:16.000000000 +1200
@@ -467,10 +467,15 @@
                 }
 
                 // overwrite limitation of enctypes
+#if USE_HEIMDAL_KRB5
+                creds->session.keytype = 0;
+                if (creds->session.keyvalue.length > 0)
+                    krb5_free_keyblock_contents(kparam.context, &creds->session);
+#else
                 creds->keyblock.enctype = 0;
                 if (creds->keyblock.contents)
                     krb5_free_keyblock_contents(kparam.context, &creds->keyblock);
-
+#endif
                 code = krb5_get_credentials(kparam.context, 0, kparam.cc[ccindex], creds, &tgt_creds);
                 if (code) {
                     k5_error("Error while getting tgt", code);
diff -u -r -N squid-4.11/src/acl/external/SQL_session/ext_sql_session_acl.8 squid-4.12/src/acl/external/SQL_session/ext_sql_session_acl.8
--- squid-4.11/src/acl/external/SQL_session/ext_sql_session_acl.8	2020-04-20 00:50:35.000000000 +1200
+++ squid-4.12/src/acl/external/SQL_session/ext_sql_session_acl.8	2020-06-09 19:27:16.000000000 +1200
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "EXT_SQL_SESSION_ACL 8"
-.TH EXT_SQL_SESSION_ACL 8 "2020-04-19" "perl v5.28.1" "User Contributed Perl Documentation"
+.TH EXT_SQL_SESSION_ACL 8 "2020-06-09" "perl v5.30.3" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -u -r -N squid-4.11/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8 squid-4.12/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8
--- squid-4.11/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8	2020-04-20 00:50:36.000000000 +1200
+++ squid-4.12/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8	2020-06-09 19:27:16.000000000 +1200
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "EXT_WBINFO_GROUP_ACL 8"
-.TH EXT_WBINFO_GROUP_ACL 8 "2020-04-19" "perl v5.28.1" "User Contributed Perl Documentation"
+.TH EXT_WBINFO_GROUP_ACL 8 "2020-06-09" "perl v5.30.3" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -u -r -N squid-4.11/src/anyp/ProtocolVersion.h squid-4.12/src/anyp/ProtocolVersion.h
--- squid-4.11/src/anyp/ProtocolVersion.h	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/anyp/ProtocolVersion.h	2020-06-08 03:42:16.000000000 +1200
@@ -40,6 +40,9 @@
     unsigned int major;    ///< major version number
     unsigned int minor;    ///< minor version number
 
+    /// whether the version is "known" (e.g., has been parsed or explicitly set)
+    explicit operator bool() const { return protocol != PROTO_NONE; }
+
     bool operator==(const ProtocolVersion& that) const {
         if (this->protocol != that.protocol)
             return false;
diff -u -r -N squid-4.11/src/anyp/Uri.cc squid-4.12/src/anyp/Uri.cc
--- squid-4.11/src/anyp/Uri.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/anyp/Uri.cc	2020-06-08 03:42:16.000000000 +1200
@@ -30,6 +30,56 @@
     "[:]"
     ;
 
+/// Characters which are valid within a URI userinfo section
+static const CharacterSet &
+UserInfoChars()
+{
+    /*
+     * RFC 3986 section 3.2.1
+     *
+     *  userinfo      = *( unreserved / pct-encoded / sub-delims / ":" )
+     *  unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
+     *  pct-encoded   = "%" HEXDIG HEXDIG
+     *  sub-delims    = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
+     */
+    static const auto userInfoValid = CharacterSet("userinfo", ":-._~%!$&'()*+,;=") +
+                                      CharacterSet::ALPHA +
+                                      CharacterSet::DIGIT;
+    return userInfoValid;
+}
+
+/**
+ * Governed by RFC 3986 section 2.1
+ */
+SBuf
+AnyP::Uri::Encode(const SBuf &buf, const CharacterSet &ignore)
+{
+    if (buf.isEmpty())
+        return buf;
+
+    Parser::Tokenizer tk(buf);
+    SBuf goodSection;
+    // optimization for the arguably common "no encoding necessary" case
+    if (tk.prefix(goodSection, ignore) && tk.atEnd())
+        return buf;
+
+    SBuf output;
+    output.reserveSpace(buf.length() * 3); // worst case: encode all chars
+    output.append(goodSection); // may be empty
+
+    while (!tk.atEnd()) {
+        // TODO: Add Tokenizer::parseOne(void).
+        const auto ch = tk.remaining()[0];
+        output.appendf("%%%02X", static_cast<unsigned int>(ch)); // TODO: Optimize using a table
+        (void)tk.skip(ch);
+
+        if (tk.prefix(goodSection, ignore))
+            output.append(goodSection);
+    }
+
+    return output;
+}
+
 const SBuf &
 AnyP::Uri::Asterisk()
 {
@@ -557,7 +607,10 @@
                                        getScheme() == AnyP::PROTO_UNKNOWN;
 
             if (allowUserInfo && !userInfo().isEmpty()) {
-                absolute_.append(userInfo());
+                static const CharacterSet uiChars = CharacterSet(UserInfoChars())
+                                                    .remove('%')
+                                                    .rename("userinfo-reserved");
+                absolute_.append(Encode(userInfo(), uiChars));
                 absolute_.append("@", 1);
             }
             absolute_.append(authority());
@@ -565,7 +618,7 @@
             absolute_.append(host());
             absolute_.append(":", 1);
         }
-        absolute_.append(path());
+        absolute_.append(path()); // TODO: Encode each URI subcomponent in path_ as needed.
     }
 
     return absolute_;
@@ -619,102 +672,76 @@
     return request->canonicalCleanUrl();
 }
 
-/*
- * Test if a URL is relative.
+/**
+ * Test if a URL is a relative reference.
+ *
+ * Governed by RFC 3986 section 4.2
+ *
+ *  relative-ref  = relative-part [ "?" query ] [ "#" fragment ]
  *
- * RFC 2396, Section 5 (Page 17) implies that in a relative URL, a '/' will
- * appear before a ':'.
+ *  relative-part = "//" authority path-abempty
+ *                / path-absolute
+ *                / path-noscheme
+ *                / path-empty
  */
 bool
 urlIsRelative(const char *url)
 {
-    const char *p;
+    if (!url)
+        return false; // no URL
 
-    if (url == NULL) {
-        return (false);
-    }
-    if (*url == '\0') {
-        return (false);
-    }
+    /*
+     * RFC 3986 section 5.2.3
+     *
+     * path          = path-abempty    ; begins with "/" or is empty
+     *               / path-absolute   ; begins with "/" but not "//"
+     *               / path-noscheme   ; begins with a non-colon segment
+     *               / path-rootless   ; begins with a segment
+     *               / path-empty      ; zero characters
+     */
 
-    for (p = url; *p != '\0' && *p != ':' && *p != '/'; ++p);
+    if (*url == '\0')
+        return true; // path-empty
 
-    if (*p == ':') {
-        return (false);
+    if (*url == '/') {
+        // RFC 3986 section 5.2.3
+        // path-absolute   ; begins with "/" but not "//"
+        if (url[1] == '/')
+            return true; // network-path reference, aka. 'scheme-relative URI'
+        else
+            return true; // path-absolute, aka 'absolute-path reference'
     }
-    return (true);
-}
-
-/*
- * Convert a relative URL to an absolute URL using the context of a given
- * request.
- *
- * It is assumed that you have already ensured that the URL is relative.
- *
- * If NULL is returned it is an indication that the method in use in the
- * request does not distinguish between relative and absolute and you should
- * use the url unchanged.
- *
- * If non-NULL is returned, it is up to the caller to free the resulting
- * memory using safe_free().
- */
-char *
-urlMakeAbsolute(const HttpRequest * req, const char *relUrl)
-{
 
-    if (req->method.id() == Http::METHOD_CONNECT) {
-        return (NULL);
+    for (const auto *p = url; *p != '\0' && *p != '/' && *p != '?' && *p != '#'; ++p) {
+        if (*p == ':')
+            return false; // colon is forbidden in first segment
     }
 
-    char *urlbuf = (char *)xmalloc(MAX_URL * sizeof(char));
+    return true; // path-noscheme, path-abempty, path-rootless
+}
 
-    if (req->url.getScheme() == AnyP::PROTO_URN) {
-        // XXX: this is what the original code did, but it seems to break the
-        // intended behaviour of this function. It returns the stored URN path,
-        // not converting the given one into a URN...
-        snprintf(urlbuf, MAX_URL, SQUIDSBUFPH, SQUIDSBUFPRINT(req->url.absolute()));
-        return (urlbuf);
-    }
+void
+AnyP::Uri::addRelativePath(const char *relUrl)
+{
+    // URN cannot be merged
+    if (getScheme() == AnyP::PROTO_URN)
+        return;
 
-    SBuf authorityForm = req->url.authority(); // host[:port]
-    const SBuf &scheme = req->url.getScheme().image();
-    size_t urllen = snprintf(urlbuf, MAX_URL, SQUIDSBUFPH "://" SQUIDSBUFPH "%s" SQUIDSBUFPH,
-                             SQUIDSBUFPRINT(scheme),
-                             SQUIDSBUFPRINT(req->url.userInfo()),
-                             !req->url.userInfo().isEmpty() ? "@" : "",
-                             SQUIDSBUFPRINT(authorityForm));
+    // TODO: Handle . and .. segment normalization
 
-    // if the first char is '/' assume its a relative path
-    // XXX: this breaks on scheme-relative URLs,
-    // but we should not see those outside ESI, and rarely there.
-    // XXX: also breaks on any URL containing a '/' in the query-string portion
-    if (relUrl[0] == '/') {
-        xstrncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
+    const auto lastSlashPos = path_.rfind('/');
+    // TODO: To optimize and simplify, add and use SBuf::replace().
+    const auto relUrlLength = strlen(relUrl);
+    if (lastSlashPos == SBuf::npos) {
+        // start replacing the whole path
+        path_.reserveCapacity(1 + relUrlLength);
+        path_.assign("/", 1);
     } else {
-        SBuf path = req->url.path();
-        SBuf::size_type lastSlashPos = path.rfind('/');
-
-        if (lastSlashPos == SBuf::npos) {
-            // replace the whole path with the given bit(s)
-            urlbuf[urllen] = '/';
-            ++urllen;
-            xstrncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
-        } else {
-            // replace only the last (file?) segment with the given bit(s)
-            ++lastSlashPos;
-            if (lastSlashPos > MAX_URL - urllen - 1) {
-                // XXX: crops bits in the middle of the combined URL.
-                lastSlashPos = MAX_URL - urllen - 1;
-            }
-            SBufToCstring(&urlbuf[urllen], path.substr(0,lastSlashPos));
-            urllen += lastSlashPos;
-            if (urllen + 1 < MAX_URL) {
-                xstrncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
-            }
-        }
+        // start replacing just the last segment
+        path_.reserveCapacity(lastSlashPos + 1 + relUrlLength);
+        path_.chop(0, lastSlashPos+1);
     }
-
-    return (urlbuf);
+    path_.append(relUrl, relUrlLength);
 }
 
 int
@@ -890,105 +917,6 @@
     return rc;
 }
 
-/*
- * Quick-n-dirty host extraction from a URL.  Steps:
- *      Look for a colon
- *      Skip any '/' after the colon
- *      Copy the next SQUID_MAXHOSTNAMELEN bytes to host[]
- *      Look for an ending '/' or ':' and terminate
- *      Look for login info preceeded by '@'
- */
-
-class URLHostName
-{
-
-public:
-    char * extract(char const *url);
-
-private:
-    static char Host [SQUIDHOSTNAMELEN];
-    void init(char const *);
-    void findHostStart();
-    void trimTrailingChars();
-    void trimAuth();
-    char const *hostStart;
-    char const *url;
-};
-
-char *
-urlHostname(const char *url)
-{
-    return URLHostName().extract(url);
-}
-
-char URLHostName::Host[SQUIDHOSTNAMELEN];
-
-void
-URLHostName::init(char const *aUrl)
-{
-    Host[0] = '\0';
-    url = aUrl;
-}
-
-void
-URLHostName::findHostStart()
-{
-    if (NULL == (hostStart = strchr(url, ':')))
-        return;
-
-    ++hostStart;
-
-    while (*hostStart != '\0' && *hostStart == '/')
-        ++hostStart;
-
-    if (*hostStart == ']')
-        ++hostStart;
-}
-
-void
-URLHostName::trimTrailingChars()
-{
-    char *t;
-
-    if ((t = strchr(Host, '/')))
-        *t = '\0';
-
-    if ((t = strrchr(Host, ':')))
-        *t = '\0';
-
-    if ((t = strchr(Host, ']')))
-        *t = '\0';
-}
-
-void
-URLHostName::trimAuth()
-{
-    char *t;
-
-    if ((t = strrchr(Host, '@'))) {
-        ++t;
-        memmove(Host, t, strlen(t) + 1);
-    }
-}
-
-char *
-URLHostName::extract(char const *aUrl)
-{
-    init(aUrl);
-    findHostStart();
-
-    if (hostStart == NULL)
-        return NULL;
-
-    xstrncpy(Host, hostStart, SQUIDHOSTNAMELEN);
-
-    trimTrailingChars();
-
-    trimAuth();
-
-    return Host;
-}
-
 AnyP::Uri::Uri(AnyP::UriScheme const &aScheme) :
     scheme_(aScheme),
     hostIsNumeric_(false),
diff -u -r -N squid-4.11/src/anyp/Uri.h squid-4.12/src/anyp/Uri.h
--- squid-4.11/src/anyp/Uri.h	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/anyp/Uri.h	2020-06-08 03:42:16.000000000 +1200
@@ -78,6 +78,8 @@
     }
 
     void userInfo(const SBuf &s) {userInfo_=s; touch();}
+    /// \returns raw userinfo subcomponent (or an empty string)
+    /// the caller is responsible for caller-specific encoding
     const SBuf &userInfo() const {return userInfo_;}
 
     void host(const char *src);
@@ -97,12 +99,27 @@
     void path(const SBuf &p) {path_=p; touch();}
     const SBuf &path() const;
 
+    /**
+     * Merge a relative-path URL into the existing URI details.
+     * Implements RFC 3986 section 5.2.3
+     *
+     * The caller must ensure relUrl is a valid relative-path.
+     *
+     * NP: absolute-path are also accepted, but path() method
+     * should be used instead when possible.
+     */
+    void addRelativePath(const char *relUrl);
+
     /// the static '/' default URL-path
     static const SBuf &SlashPath();
 
     /// the static '*' pseudo-URI
     static const SBuf &Asterisk();
 
+    /// %-encode characters in a buffer which do not conform to
+    /// the provided set of expected characters.
+    static SBuf Encode(const SBuf &, const CharacterSet &expected);
+
     /**
      * The authority-form URI for currently stored values.
      *
@@ -198,7 +215,6 @@
 char *urlCanonicalCleanWithoutRequest(const SBuf &url, const HttpRequestMethod &, const AnyP::UriScheme &);
 const char *urlCanonicalFakeHttps(const HttpRequest * request);
 bool urlIsRelative(const char *);
-char *urlMakeAbsolute(const HttpRequest *, const char *);
 char *urlRInternal(const char *host, unsigned short port, const char *dir, const char *name);
 char *urlInternal(const char *dir, const char *name);
 bool urlAppendDomain(char *host); ///< apply append_domain config to the given hostname
@@ -244,7 +260,6 @@
  */
 int matchDomainName(const char *host, const char *domain, uint8_t flags = mdnNone);
 int urlCheckRequest(const HttpRequest *);
-char *urlHostname(const char *url);
 void urlExtMethodConfigure(void);
 
 #endif /* SQUID_SRC_ANYP_URI_H */
diff -u -r -N squid-4.11/src/auth/basic/DB/basic_db_auth.8 squid-4.12/src/auth/basic/DB/basic_db_auth.8
--- squid-4.11/src/auth/basic/DB/basic_db_auth.8	2020-04-20 00:50:36.000000000 +1200
+++ squid-4.12/src/auth/basic/DB/basic_db_auth.8	2020-06-09 19:27:17.000000000 +1200
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "BASIC_DB_AUTH 8"
-.TH BASIC_DB_AUTH 8 "2020-04-19" "perl v5.28.1" "User Contributed Perl Documentation"
+.TH BASIC_DB_AUTH 8 "2020-06-09" "perl v5.30.3" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -u -r -N squid-4.11/src/auth/basic/POP3/basic_pop3_auth.8 squid-4.12/src/auth/basic/POP3/basic_pop3_auth.8
--- squid-4.11/src/auth/basic/POP3/basic_pop3_auth.8	2020-04-20 00:50:37.000000000 +1200
+++ squid-4.12/src/auth/basic/POP3/basic_pop3_auth.8	2020-06-09 19:27:17.000000000 +1200
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "BASIC_POP3_AUTH 8"
-.TH BASIC_POP3_AUTH 8 "2020-04-19" "perl v5.28.1" "User Contributed Perl Documentation"
+.TH BASIC_POP3_AUTH 8 "2020-06-09" "perl v5.30.3" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -u -r -N squid-4.11/src/base/CharacterSet.cc squid-4.12/src/base/CharacterSet.cc
--- squid-4.11/src/base/CharacterSet.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/base/CharacterSet.cc	2020-06-08 03:42:16.000000000 +1200
@@ -51,6 +51,13 @@
 }
 
 CharacterSet &
+CharacterSet::remove(const unsigned char c)
+{
+    chars_[static_cast<uint8_t>(c)] = 0;
+    return *this;
+}
+
+CharacterSet &
 CharacterSet::addRange(unsigned char low, unsigned char high)
 {
     //manual loop splitting is needed to cover case where high is 255
diff -u -r -N squid-4.11/src/base/CharacterSet.h squid-4.12/src/base/CharacterSet.h
--- squid-4.11/src/base/CharacterSet.h	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/base/CharacterSet.h	2020-06-08 03:42:16.000000000 +1200
@@ -41,6 +41,9 @@
     /// add a given character to the character set
     CharacterSet & add(const unsigned char c);
 
+    /// remove a given character from the character set
+    CharacterSet & remove(const unsigned char c);
+
     /// add a list of character ranges, expressed as pairs [low,high], including both ends
     CharacterSet & addRange(unsigned char low, unsigned char high);
 
diff -u -r -N squid-4.11/src/clients/Client.cc squid-4.12/src/clients/Client.cc
--- squid-4.11/src/clients/Client.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/clients/Client.cc	2020-06-08 03:42:16.000000000 +1200
@@ -439,7 +439,7 @@
 static bool
 sameUrlHosts(const char *url1, const char *url2)
 {
-    // XXX: Want urlHostname() here, but it uses static storage and copying
+    // XXX: Want AnyP::Uri::parse() here, but it uses static storage and copying
     const char *host1 = strchr(url1, ':');
     const char *host2 = strchr(url2, ':');
 
@@ -468,33 +468,40 @@
 static void
 purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, HttpMsg *rep, Http::HdrType hdr)
 {
-    const char *hdrUrl, *absUrl;
-
-    absUrl = NULL;
-    hdrUrl = rep->header.getStr(hdr);
-    if (hdrUrl == NULL) {
+    const auto hdrUrl = rep->header.getStr(hdr);
+    if (!hdrUrl)
         return;
-    }
 
     /*
      * If the URL is relative, make it absolute so we can find it.
      * If it's absolute, make sure the host parts match to avoid DOS attacks
      * as per RFC 2616 13.10.
      */
+    SBuf absUrlMaker;
+    const char *absUrl = nullptr;
     if (urlIsRelative(hdrUrl)) {
-        absUrl = urlMakeAbsolute(req, hdrUrl);
-        if (absUrl != NULL) {
-            hdrUrl = absUrl;
+        if (req->method.id() == Http::METHOD_CONNECT)
+            absUrl = hdrUrl; // TODO: merge authority-uri and hdrUrl
+        else if (req->url.getScheme() == AnyP::PROTO_URN)
+            absUrl = req->url.absolute().c_str();
+        else {
+            AnyP::Uri tmpUrl = req->url;
+            if (*hdrUrl == '/') {
+                // RFC 3986 section 4.2: absolute-path reference
+                // for this logic replace the entire request-target URI path
+                tmpUrl.path(hdrUrl);
+            } else {
+                tmpUrl.addRelativePath(reqUrl);
+            }
+            absUrlMaker = tmpUrl.absolute();
+            absUrl = absUrlMaker.c_str();
         }
     } else if (!sameUrlHosts(reqUrl, hdrUrl)) {
         return;
-    }
+    } else
+        absUrl = hdrUrl;
 
-    purgeEntriesByUrl(req, hdrUrl);
-
-    if (absUrl != NULL) {
-        safe_free(absUrl);
-    }
+    purgeEntriesByUrl(req, absUrl);
 }
 
 // some HTTP methods should purge matching cache entries
diff -u -r -N squid-4.11/src/client_side.cc squid-4.12/src/client_side.cc
--- squid-4.11/src/client_side.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/client_side.cc	2020-06-08 03:42:16.000000000 +1200
@@ -2365,6 +2365,7 @@
     bodyParser(nullptr),
 #if USE_OPENSSL
     sslBumpMode(Ssl::bumpEnd),
+    tlsParser(Security::HandshakeParser::fromClient),
 #endif
     needProxyProtocolHeader_(false),
 #if USE_OPENSSL
diff -u -r -N squid-4.11/src/client_side_reply.cc squid-4.12/src/client_side_reply.cc
--- squid-4.11/src/client_side_reply.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/client_side_reply.cc	2020-06-08 03:42:16.000000000 +1200
@@ -907,7 +907,7 @@
             const cache_key *key = storeKeyPublic(url, m);
             debugs(88, 5, m << ' ' << url << ' ' << storeKeyText(key));
 #if USE_HTCP
-            neighborsHtcpClear(nullptr, url, req, m, HTCP_CLR_INVALIDATION);
+            neighborsHtcpClear(nullptr, req, m, HTCP_CLR_INVALIDATION);
 #endif
             Store::Root().evictIfFound(key);
         }
@@ -1040,7 +1040,7 @@
         /* Release the cached URI */
         debugs(88, 4, "clientPurgeRequest: GET '" << newEntry->url() << "'" );
 #if USE_HTCP
-        neighborsHtcpClear(newEntry, NULL, http->request, HttpRequestMethod(Http::METHOD_GET), HTCP_CLR_PURGE);
+        neighborsHtcpClear(newEntry, http->request, HttpRequestMethod(Http::METHOD_GET), HTCP_CLR_PURGE);
 #endif
         newEntry->release(true);
         purgeStatus = Http::scOkay;
@@ -1056,7 +1056,7 @@
     if (newEntry && !newEntry->isNull()) {
         debugs(88, 4, "HEAD " << newEntry->url());
 #if USE_HTCP
-        neighborsHtcpClear(newEntry, NULL, http->request, HttpRequestMethod(Http::METHOD_HEAD), HTCP_CLR_PURGE);
+        neighborsHtcpClear(newEntry, http->request, HttpRequestMethod(Http::METHOD_HEAD), HTCP_CLR_PURGE);
 #endif
         newEntry->release(true);
         purgeStatus = Http::scOkay;
@@ -1072,7 +1072,7 @@
         if (entry) {
             debugs(88, 4, "Vary GET " << entry->url());
 #if USE_HTCP
-            neighborsHtcpClear(entry, NULL, http->request, HttpRequestMethod(Http::METHOD_GET), HTCP_CLR_PURGE);
+            neighborsHtcpClear(entry, http->request, HttpRequestMethod(Http::METHOD_GET), HTCP_CLR_PURGE);
 #endif
             entry->release(true);
             purgeStatus = Http::scOkay;
@@ -1083,13 +1083,16 @@
         if (entry) {
             debugs(88, 4, "Vary HEAD " << entry->url());
 #if USE_HTCP
-            neighborsHtcpClear(entry, NULL, http->request, HttpRequestMethod(Http::METHOD_HEAD), HTCP_CLR_PURGE);
+            neighborsHtcpClear(entry, http->request, HttpRequestMethod(Http::METHOD_HEAD), HTCP_CLR_PURGE);
 #endif
             entry->release(true);
             purgeStatus = Http::scOkay;
         }
     }
 
+    if (purgeStatus == Http::scNone)
+        purgeStatus = Http::scNotFound;
+
     /*
      * Make a new entry to hold the reply to be written
      * to the client.
diff -u -r -N squid-4.11/src/Debug.h squid-4.12/src/Debug.h
--- squid-4.11/src/Debug.h	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/Debug.h	2020-06-08 03:42:16.000000000 +1200
@@ -99,6 +99,10 @@
 
     /// configures the active debugging context to write syslog ALERT
     static void ForceAlert();
+
+    /// prefixes each grouped debugs() line after the first one in the group
+    static std::ostream& Extra(std::ostream &os) { return os << "\n    "; }
+
 private:
     static Context *Current; ///< deepest active context; nil outside debugs()
 };
diff -u -r -N squid-4.11/src/format/Format.cc squid-4.12/src/format/Format.cc
--- squid-4.11/src/format/Format.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/format/Format.cc	2020-06-08 03:42:16.000000000 +1200
@@ -322,15 +322,6 @@
     *p = '\0';
 }
 
-#if USE_OPENSSL
-static char *
-sslErrorName(Security::ErrorCode err, char *buf, size_t size)
-{
-    snprintf(buf, size, "SSL_ERR=%d", err);
-    return buf;
-}
-#endif
-
 /// XXX: Misnamed. TODO: Split <h (and this function) to distinguish received
 /// headers from sent headers rather than failing to distinguish requests from responses.
 /// \retval HttpReply sent to the HTTP client (access.log and default context).
@@ -959,9 +950,7 @@
         case LFT_SQUID_ERROR_DETAIL:
 #if USE_OPENSSL
             if (al->request && al->request->errType == ERR_SECURE_CONNECT_FAIL) {
-                out = Ssl::GetErrorName(al->request->errDetail);
-                if (!out)
-                    out = sslErrorName(al->request->errDetail, tmp, sizeof(tmp));
+                out = Ssl::GetErrorName(al->request->errDetail, true);
             } else
 #endif
                 if (al->request && al->request->errDetail != ERR_DETAIL_NONE) {
@@ -1263,10 +1252,7 @@
                     for (const Security::CertErrors *sslError = srvBump->sslErrors(); sslError; sslError = sslError->next) {
                         if (!sb.isEmpty())
                             sb.append(separator);
-                        if (const char *errorName = Ssl::GetErrorName(sslError->element.code))
-                            sb.append(errorName);
-                        else
-                            sb.append(sslErrorName(sslError->element.code, tmp, sizeof(tmp)));
+                        sb.append(Ssl::GetErrorName(sslError->element.code, true));
                         if (sslError->element.depth >= 0)
                             sb.appendf("@depth=%d", sslError->element.depth);
                     }
diff -u -r -N squid-4.11/src/htcp.cc squid-4.12/src/htcp.cc
--- squid-4.11/src/htcp.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/htcp.cc	2020-06-08 03:42:16.000000000 +1200
@@ -138,7 +138,7 @@
 
 public:
     const char *method = nullptr;
-    char *uri = nullptr;
+    const char *uri = nullptr;
     char *version = nullptr;
     char *req_hdrs = nullptr;
     size_t reqHdrsSz = 0; ///< size of the req_hdrs content
@@ -791,6 +791,7 @@
 
     if (spec) {
         stuff.S.method = spec->method;
+        stuff.S.request = spec->request;
         stuff.S.uri = spec->uri;
         stuff.S.version = spec->version;
         stuff.S.req_hdrs = spec->req_hdrs;
@@ -825,15 +826,15 @@
         hdr.clean();
 
 #if USE_ICMP
-        if (char *host = urlHostname(spec->uri)) {
+        if (const char *host = spec->request->url.host()) {
             int rtt = 0;
             int hops = 0;
             int samp = 0;
             netdbHostData(host, &samp, &rtt, &hops);
 
             if (rtt || hops) {
-                char cto_buf[128];
-                snprintf(cto_buf, 128, "%s %d %f %d",
+                char cto_buf[SQUIDHOSTNAMELEN+128];
+                snprintf(cto_buf, sizeof(cto_buf), "%s %d %f %d",
                          host, samp, 0.001 * rtt, hops);
                 hdr.putExt("Cache-to-Origin", cto_buf);
             }
@@ -1470,7 +1471,7 @@
  * Send an HTCP CLR message for a specified item to a given CachePeer.
  */
 void
-htcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &, CachePeer * p, htcp_clr_reason reason)
+htcpClear(StoreEntry * e, HttpRequest * req, const HttpRequestMethod &, CachePeer * p, htcp_clr_reason reason)
 {
     static char pkt[8192];
     ssize_t pktlen;
@@ -1492,14 +1493,9 @@
 
     SBuf sb = req->method.image();
     stuff.S.method = sb.c_str();
-    if (e == NULL || e->mem_obj == NULL) {
-        if (uri == NULL) {
-            return;
-        }
-        stuff.S.uri = xstrdup(uri);
-    } else {
-        stuff.S.uri = (char *) e->url();
-    }
+    stuff.S.request = req;
+    SBuf uri = req->effectiveRequestUri();
+    stuff.S.uri = uri.c_str();
     stuff.S.version = vbuf;
     if (reason != HTCP_CLR_INVALIDATION) {
         HttpStateData::httpBuildRequestHeader(req, e, NULL, &hdr, flags);
@@ -1514,9 +1510,6 @@
     if (reason != HTCP_CLR_INVALIDATION) {
         mb.clean();
     }
-    if (e == NULL) {
-        xfree(stuff.S.uri);
-    }
     if (!pktlen) {
         debugs(31, 3, "htcpClear: htcpBuildPacket() failed");
         return;
diff -u -r -N squid-4.11/src/htcp.h squid-4.12/src/htcp.h
--- squid-4.11/src/htcp.h	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/htcp.h	2020-06-08 03:42:16.000000000 +1200
@@ -57,7 +57,7 @@
 int htcpQuery(StoreEntry * e, HttpRequest * req, CachePeer * p);
 
 /// \ingroup ServerProtocolHTCP
-void htcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &method, CachePeer * p, htcp_clr_reason reason);
+void htcpClear(StoreEntry * e, HttpRequest * req, const HttpRequestMethod &method, CachePeer * p, htcp_clr_reason reason);
 
 /// \ingroup ServerProtocolHTCP
 void htcpSocketShutdown(void);
diff -u -r -N squid-4.11/src/http/ContentLengthInterpreter.cc squid-4.12/src/http/ContentLengthInterpreter.cc
--- squid-4.11/src/http/ContentLengthInterpreter.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/http/ContentLengthInterpreter.cc	2020-06-08 03:42:16.000000000 +1200
@@ -28,6 +28,24 @@
 {
 }
 
+/// checks whether all characters before the Content-Length number are allowed
+/// \returns the start of the digit sequence (or nil on errors)
+const char *
+Http::ContentLengthInterpreter::findDigits(const char *prefix, const char * const valueEnd) const
+{
+    // skip leading OWS in RFC 7230's `OWS field-value OWS`
+    const CharacterSet &whitespace = Http::One::Parser::WhitespaceCharacters();
+    while (prefix < valueEnd) {
+        const auto ch = *prefix;
+        if (CharacterSet::DIGIT[ch])
+            return prefix; // common case: a pre-trimmed field value
+        if (!whitespace[ch])
+            return nullptr; // (trimmed) length does not start with a digit
+        ++prefix;
+    }
+    return nullptr; // empty or whitespace-only value
+}
+
 /// checks whether all characters after the Content-Length are allowed
 bool
 Http::ContentLengthInterpreter::goodSuffix(const char *suffix, const char * const end) const
@@ -52,10 +70,19 @@
 {
     Must(!sawBad);
 
+    const auto valueEnd = rawValue + valueSize;
+
+    const auto digits = findDigits(rawValue, valueEnd);
+    if (!digits) {
+        debugs(55, debugLevel, "WARNING: Leading garbage or empty value in" << Raw("Content-Length", rawValue, valueSize));
+        sawBad = true;
+        return false;
+    }
+
     int64_t latestValue = -1;
     char *suffix = nullptr;
-    // TODO: Handle malformed values with leading signs (e.g., "-0" or "+1").
-    if (!httpHeaderParseOffset(rawValue, &latestValue, &suffix)) {
+
+    if (!httpHeaderParseOffset(digits, &latestValue, &suffix)) {
         debugs(55, DBG_IMPORTANT, "WARNING: Malformed" << Raw("Content-Length", rawValue, valueSize));
         sawBad = true;
         return false;
@@ -68,7 +95,7 @@
     }
 
     // check for garbage after the number
-    if (!goodSuffix(suffix, rawValue + valueSize)) {
+    if (!goodSuffix(suffix, valueEnd)) {
         debugs(55, debugLevel, "WARNING: Trailing garbage in" << Raw("Content-Length", rawValue, valueSize));
         sawBad = true;
         return false;
diff -u -r -N squid-4.11/src/http/ContentLengthInterpreter.h squid-4.12/src/http/ContentLengthInterpreter.h
--- squid-4.11/src/http/ContentLengthInterpreter.h	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/http/ContentLengthInterpreter.h	2020-06-08 03:42:16.000000000 +1200
@@ -46,6 +46,7 @@
     bool sawGood;
 
 protected:
+    const char *findDigits(const char *prefix, const char *valueEnd) const;
     bool goodSuffix(const char *suffix, const char * const end) const;
     bool checkValue(const char *start, const int size);
     bool checkList(const String &list);
diff -u -r -N squid-4.11/src/http/StatusLine.cc squid-4.12/src/http/StatusLine.cc
--- squid-4.11/src/http/StatusLine.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/http/StatusLine.cc	2020-06-08 03:42:16.000000000 +1200
@@ -47,6 +47,17 @@
 {
     assert(p);
 
+    auto packedStatus = status();
+    auto packedReason = reason();
+
+    if (packedStatus == Http::scNone) {
+        static unsigned int reports = 0;
+        if (++reports <= 100)
+            debugs(57, DBG_IMPORTANT, "BUG: Generated response lacks status code");
+        packedStatus = Http::scInternalServerError;
+        packedReason = Http::StatusCodeString(packedStatus); // ignore custom reason_ (if any)
+    }
+
     /* local constants */
     /* AYJ: see bug 2469 - RFC2616 confirms stating 'SP characters' plural! */
     static const char *Http1StatusLineFormat = "HTTP/%d.%d %3d %s\r\n";
@@ -56,15 +67,15 @@
     if (protocol == AnyP::PROTO_ICY) {
         debugs(57, 9, "packing sline " << this << " using " << p << ":");
         debugs(57, 9, "FORMAT=" << IcyStatusLineFormat );
-        debugs(57, 9, "ICY " << status() << " " << reason());
-        p->appendf(IcyStatusLineFormat, status(), reason());
+        debugs(57, 9, "ICY " << packedStatus << " " << packedReason);
+        p->appendf(IcyStatusLineFormat, packedStatus, packedReason);
         return;
     }
 
     debugs(57, 9, "packing sline " << this << " using " << p << ":");
     debugs(57, 9, "FORMAT=" << Http1StatusLineFormat );
-    debugs(57, 9, "HTTP/" << version.major << "." << version.minor << " " << status() << " " << reason());
-    p->appendf(Http1StatusLineFormat, version.major, version.minor, status(), reason());
+    debugs(57, 9, "HTTP/" << version.major << "." << version.minor << " " << packedStatus << " " << packedReason);
+    p->appendf(Http1StatusLineFormat, version.major, version.minor, packedStatus, packedReason);
 }
 
 /*
diff -u -r -N squid-4.11/src/http/url_rewriters/LFS/url_lfs_rewrite.8 squid-4.12/src/http/url_rewriters/LFS/url_lfs_rewrite.8
--- squid-4.11/src/http/url_rewriters/LFS/url_lfs_rewrite.8	2020-04-20 00:50:37.000000000 +1200
+++ squid-4.12/src/http/url_rewriters/LFS/url_lfs_rewrite.8	2020-06-09 19:27:18.000000000 +1200
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "URL_LFS_REWRITE 8"
-.TH URL_LFS_REWRITE 8 "2020-04-19" "perl v5.28.1" "User Contributed Perl Documentation"
+.TH URL_LFS_REWRITE 8 "2020-06-09" "perl v5.30.3" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -u -r -N squid-4.11/src/http.cc squid-4.12/src/http.cc
--- squid-4.11/src/http.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/http.cc	2020-06-08 03:42:16.000000000 +1200
@@ -249,7 +249,7 @@
     if (pe != NULL) {
         assert(e != pe);
 #if USE_HTCP
-        neighborsHtcpClear(e, NULL, e->mem_obj->request, e->mem_obj->method, HTCP_CLR_INVALIDATION);
+        neighborsHtcpClear(e, e->mem_obj->request, e->mem_obj->method, HTCP_CLR_INVALIDATION);
 #endif
         pe->release(true);
     }
@@ -266,7 +266,7 @@
     if (pe != NULL) {
         assert(e != pe);
 #if USE_HTCP
-        neighborsHtcpClear(e, NULL, e->mem_obj->request, HttpRequestMethod(Http::METHOD_HEAD), HTCP_CLR_INVALIDATION);
+        neighborsHtcpClear(e, e->mem_obj->request, HttpRequestMethod(Http::METHOD_HEAD), HTCP_CLR_INVALIDATION);
 #endif
         pe->release(true);
     }
diff -u -r -N squid-4.11/src/ip/Intercept.cc squid-4.12/src/ip/Intercept.cc
--- squid-4.11/src/ip/Intercept.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/ip/Intercept.cc	2020-06-08 03:42:16.000000000 +1200
@@ -204,7 +204,7 @@
     memset(&natLookup, 0, sizeof(natLookup));
     // for NAT lookup set local and remote IP:port's
     if (newConn->remote.isIPv6()) {
-#if HAVE_NATLOOKUP_NL_INIPADDR_IN6
+#if HAVE_STRUCT_NATLOOKUP_NL_INIPADDR_IN6
         natLookup.nl_v = 6;
         newConn->local.getInAddr(natLookup.nl_inipaddr.in6);
         newConn->remote.getInAddr(natLookup.nl_outipaddr.in6);
@@ -292,7 +292,7 @@
         debugs(89, 9, HERE << "address: " << newConn);
         return false;
     } else {
-#if HAVE_NATLOOKUP_NL_REALIPADDR_IN6
+#if HAVE_STRUCT_NATLOOKUP_NL_REALIPADDR_IN6
         if (newConn->remote.isIPv6())
             newConn->local = natLookup.nl_realipaddr.in6;
         else
diff -u -r -N squid-4.11/src/log/DB/log_db_daemon.8 squid-4.12/src/log/DB/log_db_daemon.8
--- squid-4.11/src/log/DB/log_db_daemon.8	2020-04-20 00:50:38.000000000 +1200
+++ squid-4.12/src/log/DB/log_db_daemon.8	2020-06-09 19:27:18.000000000 +1200
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "LOG_DB_DAEMON 8"
-.TH LOG_DB_DAEMON 8 "2020-04-19" "perl v5.28.1" "User Contributed Perl Documentation"
+.TH LOG_DB_DAEMON 8 "2020-06-09" "perl v5.30.3" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -u -r -N squid-4.11/src/neighbors.cc squid-4.12/src/neighbors.cc
--- squid-4.11/src/neighbors.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/neighbors.cc	2020-06-08 03:42:16.000000000 +1200
@@ -1748,7 +1748,7 @@
  * Send HTCP CLR messages to all peers configured to receive them.
  */
 void
-neighborsHtcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &method, htcp_clr_reason reason)
+neighborsHtcpClear(StoreEntry * e, HttpRequest * req, const HttpRequestMethod &method, htcp_clr_reason reason)
 {
     CachePeer *p;
     char buf[128];
@@ -1764,7 +1764,7 @@
             continue;
         }
         debugs(15, 3, "neighborsHtcpClear: sending CLR to " << p->in_addr.toUrl(buf, 128));
-        htcpClear(e, uri, req, method, p, reason);
+        htcpClear(e, req, method, p, reason);
     }
 }
 
diff -u -r -N squid-4.11/src/neighbors.h squid-4.12/src/neighbors.h
--- squid-4.11/src/neighbors.h	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/neighbors.h	2020-06-08 03:42:16.000000000 +1200
@@ -39,7 +39,7 @@
 void neighborAdd(const char *, const char *, int, int, int, int, int);
 void neighbors_init(void);
 #if USE_HTCP
-void neighborsHtcpClear(StoreEntry *, const char *, HttpRequest *, const HttpRequestMethod &, htcp_clr_reason);
+void neighborsHtcpClear(StoreEntry *, HttpRequest *, const HttpRequestMethod &, htcp_clr_reason);
 #endif
 CachePeer *peerFindByName(const char *);
 CachePeer *peerFindByNameAndPort(const char *, unsigned short);
diff -u -r -N squid-4.11/src/security/cert_validators/fake/security_fake_certverify.8 squid-4.12/src/security/cert_validators/fake/security_fake_certverify.8
--- squid-4.11/src/security/cert_validators/fake/security_fake_certverify.8	2020-04-20 00:50:38.000000000 +1200
+++ squid-4.12/src/security/cert_validators/fake/security_fake_certverify.8	2020-06-09 19:27:18.000000000 +1200
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "SECURITY_FAKE_CERTVERIFY 8"
-.TH SECURITY_FAKE_CERTVERIFY 8 "2020-04-19" "perl v5.28.1" "User Contributed Perl Documentation"
+.TH SECURITY_FAKE_CERTVERIFY 8 "2020-06-09" "perl v5.30.3" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -u -r -N squid-4.11/src/security/Handshake.cc squid-4.12/src/security/Handshake.cc
--- squid-4.11/src/security/Handshake.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/security/Handshake.cc	2020-06-08 03:42:16.000000000 +1200
@@ -106,11 +106,11 @@
 
 } // namespace Security
 
-/// Convenience helper: We parse ProtocolVersion but store "int".
+/// parse TLS ProtocolVersion (uint16) and convert it to AnyP::ProtocolVersion
 static AnyP::ProtocolVersion
-ParseProtocolVersion(Parser::BinaryTokenizer &tk)
+ParseProtocolVersion(Parser::BinaryTokenizer &tk, const char *contextLabel = ".version")
 {
-    Parser::BinaryTokenizerContext context(tk, ".version");
+    Parser::BinaryTokenizerContext context(tk, contextLabel);
     uint8_t vMajor = tk.uint8(".major");
     uint8_t vMinor = tk.uint8(".minor");
     if (vMajor == 0 && vMinor == 2)
@@ -187,10 +187,11 @@
 
 /* Security::HandshakeParser */
 
-Security::HandshakeParser::HandshakeParser():
+Security::HandshakeParser::HandshakeParser(const MessageSource source):
     details(new TlsDetails),
     state(atHelloNone),
     resumingSession(false),
+    messageSource(source),
     currentContentType(0),
     done(nullptr),
     expectingModernRecords(false)
@@ -285,12 +286,19 @@
 {
     Must(currentContentType == ContentType::ctChangeCipherSpec);
     // We are currently ignoring Change Cipher Spec Protocol messages.
-    skipMessage("ChangeCipherCpec msg [fragment]");
+    skipMessage("ChangeCipherSpec msg [fragment]");
+
+    // In TLS v1.2 and earlier, ChangeCipherSpec is sent after Hello (when
+    // tlsSupportedVersion is already known) and indicates session resumption.
+    // In later TLS versions, ChangeCipherSpec may be sent before and after
+    // Hello, but it is unused for session resumption and should be ignored.
+    if (!details->tlsSupportedVersion || Tls1p3orLater(details->tlsSupportedVersion))
+        return;
 
-    // Everything after the ChangeCipherCpec message may be encrypted.
-    // Continuing parsing is pointless. Stop here.
     resumingSession = true;
-    done = "ChangeCipherCpec";
+
+    // Everything after the ChangeCipherSpec message may be encrypted. Stop.
+    done = "ChangeCipherSpec in v1.2-";
 }
 
 void
@@ -316,14 +324,19 @@
     switch (message.msg_type) {
     case HandshakeType::hskClientHello:
         Must(state < atHelloReceived);
+        Must(messageSource == fromClient);
         Security::HandshakeParser::parseClientHelloHandshakeMessage(message.msg_body);
         state = atHelloReceived;
         done = "ClientHello";
         return;
     case HandshakeType::hskServerHello:
         Must(state < atHelloReceived);
+        Must(messageSource == fromServer);
         parseServerHelloHandshakeMessage(message.msg_body);
         state = atHelloReceived;
+        // for TLSv1.3 and later, anything after the server Hello is encrypted
+        if (Tls1p3orLater(details->tlsSupportedVersion))
+            done = "ServerHello in v1.3+";
         return;
     case HandshakeType::hskCertificate:
         Must(state < atCertificatesReceived);
@@ -424,6 +437,10 @@
         case 35: // SessionTicket TLS Extension; RFC 5077
             details->tlsTicketsExtension = true;
             details->hasTlsTicket = !extension.data.isEmpty();
+            break;
+        case 43: // supported_versions extension; RFC 8446
+            parseSupportedVersionsExtension(extension.data);
+            break;
         case 13172: // Next Protocol Negotiation Extension (expired draft?)
         default:
             break;
@@ -504,6 +521,78 @@
     return SBuf(); // SNI extension lacks host_name
 }
 
+/// RFC 8446 Section 4.2.1: SupportedVersions extension
+void
+Security::HandshakeParser::parseSupportedVersionsExtension(const SBuf &extensionData) const
+{
+    // Upon detecting a quoted RFC MUST violation, this parser immediately
+    // returns, ignoring the entire extension and resulting in Squid relying on
+    // the legacy_version field value or another (valid) supported_versions
+    // extension. The alternative would be to reject the whole handshake as
+    // invalid. Deployment experience will show which alternative is the best.
+
+    // Please note that several of these MUSTs also imply certain likely
+    // handling of a hypothetical next TLS version (e.g., v1.4).
+
+    // RFC 8446 Section 4.1.2:
+    // In TLS 1.3, the client indicates its version preferences in the
+    // "supported_versions" extension (Section 4.2.1) and the legacy_version
+    // field MUST be set to 0x0303, which is the version number for TLS 1.2.
+    //
+    // RFC 8446 Section 4.2.1:
+    // A server which negotiates TLS 1.3 MUST respond by sending a
+    // "supported_versions" extension containing the selected version value
+    // (0x0304).  It MUST set the ServerHello.legacy_version field to 0x0303
+    // (TLS 1.2).
+    //
+    // Ignore supported_versions senders violating legacy_version MUSTs above:
+    if (details->tlsSupportedVersion != AnyP::ProtocolVersion(AnyP::PROTO_TLS, 1, 2))
+        return;
+
+    AnyP::ProtocolVersion supportedVersionMax;
+    if (messageSource == fromClient) {
+        Parser::BinaryTokenizer tkList(extensionData);
+        Parser::BinaryTokenizer tkVersions(tkList.pstring8("SupportedVersions"));
+        while (!tkVersions.atEnd()) {
+            const auto version = ParseProtocolVersion(tkVersions, "supported_version");
+            if (!supportedVersionMax || TlsVersionEarlierThan(supportedVersionMax, version))
+                supportedVersionMax = version;
+        }
+
+        // ignore empty supported_versions
+        if (!supportedVersionMax)
+            return;
+
+        // supportedVersionMax here may be "earlier" than tlsSupportedVersion: A
+        // TLS v1.3 client may try to negotiate a _legacy_ version X with a TLS
+        // v1.3 server by sending supported_versions containing just X.
+    } else {
+        assert(messageSource == fromServer);
+        Parser::BinaryTokenizer tkVersion(extensionData);
+        const auto version = ParseProtocolVersion(tkVersion, "selected_version");
+        // RFC 8446 Section 4.2.1:
+        // A server which negotiates a version of TLS prior to TLS 1.3 [...]
+        // MUST NOT send the "supported_versions" extension.
+        if (Tls1p2orEarlier(version))
+            return;
+        supportedVersionMax = version;
+    }
+
+    // We overwrite Hello-derived legacy_version because the following MUSTs
+    // indicate that it is ignored in the presence of valid supported_versions
+    // as far as the negotiated version is concerned. For simplicity sake, we
+    // may also overwrite previous valid supported_versions extensions (if any).
+    //
+    // RFC 8446 Section 4.2.1:
+    // If this extension is present in the ClientHello, servers MUST NOT use the
+    // ClientHello.legacy_version value for version negotiation and MUST use
+    // only the "supported_versions" extension to determine client preferences.
+    // Servers MUST only select a version of TLS present in that extension
+    debugs(83, 7, "found " << supportedVersionMax);
+    assert(supportedVersionMax);
+    details->tlsSupportedVersion = supportedVersionMax;
+}
+
 void
 Security::HandshakeParser::skipMessage(const char *description)
 {
@@ -647,6 +736,9 @@
 #if defined(TLSEXT_TYPE_next_proto_neg) // 13172
     extensions.insert(TLSEXT_TYPE_next_proto_neg);
 #endif
+#if defined(TLSEXT_TYPE_supported_versions) // 43
+    extensions.insert(TLSEXT_TYPE_supported_versions);
+#endif
 
     /*
      * OpenSSL does not support these last extensions by default, but those
diff -u -r -N squid-4.11/src/security/Handshake.h squid-4.12/src/security/Handshake.h
--- squid-4.11/src/security/Handshake.h	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/security/Handshake.h	2020-06-08 03:42:16.000000000 +1200
@@ -29,7 +29,11 @@
     std::ostream & print(std::ostream &os) const;
 
     AnyP::ProtocolVersion tlsVersion; ///< The TLS hello message version
-    AnyP::ProtocolVersion tlsSupportedVersion; ///< The requested/used TLS version
+
+    /// For most compliant TLS v1.3+ agents, this is supported_versions maximum.
+    /// For others agents, this is the legacy_version field.
+    AnyP::ProtocolVersion tlsSupportedVersion;
+
     bool compressionSupported; ///< The requested/used compressed  method
     SBuf serverName; ///< The SNI hostname, if any
     bool doHeartBeats;
@@ -59,7 +63,10 @@
     /// The parsing states
     typedef enum {atHelloNone = 0, atHelloStarted, atHelloReceived, atCertificatesReceived, atHelloDoneReceived, atNstReceived, atCcsReceived, atFinishReceived} ParserState;
 
-    HandshakeParser();
+    /// the originator of the TLS handshake being parsed
+    typedef enum { fromClient = 0, fromServer } MessageSource;
+
+    explicit HandshakeParser(MessageSource);
 
     /// Parses the initial sequence of raw bytes sent by the TLS/SSL agent.
     /// Returns true upon successful completion (e.g., got HelloDone).
@@ -67,7 +74,7 @@
     /// Throws on errors.
     bool parseHello(const SBuf &data);
 
-    TlsDetails::Pointer details; ///< TLS handshake meta info or nil.
+    TlsDetails::Pointer details; ///< TLS handshake meta info. Never nil.
 
     Security::CertList serverCertificates; ///< parsed certificates chain
 
@@ -75,6 +82,9 @@
 
     bool resumingSession; ///< True if this is a resuming session
 
+    /// whether we are parsing Server or Client TLS handshake messages
+    MessageSource messageSource;
+
 private:
     bool isSslv2Record(const SBuf &raw) const;
     void parseRecord();
@@ -96,6 +106,7 @@
     bool parseCompressionMethods(const SBuf &raw);
     void parseExtensions(const SBuf &raw);
     SBuf parseSniExtension(const SBuf &extensionData) const;
+    void parseSupportedVersionsExtension(const SBuf &extensionData) const;
 
     void parseCiphers(const SBuf &raw);
     void parseV23Ciphers(const SBuf &raw);
@@ -120,6 +131,40 @@
     YesNoNone expectingModernRecords;
 };
 
+/// whether the given protocol belongs to the TLS/SSL group of protocols
+inline bool
+TlsFamilyProtocol(const AnyP::ProtocolVersion &version)
+{
+    return (version.protocol == AnyP::PROTO_TLS || version.protocol == AnyP::PROTO_SSL);
+}
+
+/// whether TLS/SSL protocol `a` precedes TLS/SSL protocol `b`
+inline bool
+TlsVersionEarlierThan(const AnyP::ProtocolVersion &a, const AnyP::ProtocolVersion &b)
+{
+    Must(TlsFamilyProtocol(a));
+    Must(TlsFamilyProtocol(b));
+
+    if (a.protocol == b.protocol)
+        return a < b;
+
+    return a.protocol == AnyP::PROTO_SSL; // implies that b is TLS
+}
+
+/// whether the given TLS/SSL protocol is TLS v1.2 or earlier, including SSL
+inline bool
+Tls1p2orEarlier(const AnyP::ProtocolVersion &p)
+{
+    return TlsVersionEarlierThan(p, AnyP::ProtocolVersion(AnyP::PROTO_TLS, 1, 3));
+}
+
+/// whether the given TLS/SSL protocol is TLS v1.3 or later
+inline bool
+Tls1p3orLater(const AnyP::ProtocolVersion &p)
+{
+    return !Tls1p2orEarlier(p);
+}
+
 }
 
 #endif // SQUID_SECURITY_HANDSHAKE_H
diff -u -r -N squid-4.11/src/security/NegotiationHistory.cc squid-4.12/src/security/NegotiationHistory.cc
--- squid-4.11/src/security/NegotiationHistory.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/security/NegotiationHistory.cc	2020-06-08 03:42:16.000000000 +1200
@@ -25,7 +25,7 @@
 const char *
 Security::NegotiationHistory::printTlsVersion(AnyP::ProtocolVersion const &v) const
 {
-    if (v.protocol != AnyP::PROTO_SSL && v.protocol != AnyP::PROTO_TLS)
+    if (!TlsFamilyProtocol(v))
         return nullptr;
 
     static char buf[512];
diff -u -r -N squid-4.11/src/ssl/bio.cc squid-4.12/src/ssl/bio.cc
--- squid-4.11/src/ssl/bio.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/ssl/bio.cc	2020-06-08 03:42:16.000000000 +1200
@@ -250,7 +250,8 @@
     parsedHandshake(false),
     parseError(false),
     bumpMode_(bumpNone),
-    rbufConsumePos(0)
+    rbufConsumePos(0),
+    parser_(Security::HandshakeParser::fromServer)
 {
 }
 
@@ -554,6 +555,13 @@
     return parser_.resumingSession;
 }
 
+bool
+Ssl::ServerBio::encryptedCertificates() const
+{
+    return parser_.details->tlsSupportedVersion &&
+        Security::Tls1p3orLater(parser_.details->tlsSupportedVersion);
+}
+
 /// initializes BIO table after allocation
 static int
 squid_bio_create(BIO *bi)
@@ -717,6 +725,12 @@
         SSL_set_options(ssl, SSL_OP_NO_COMPRESSION);
 #endif
 
+#if defined(SSL_OP_NO_TLSv1_3)
+    // avoid "inappropriate fallback" OpenSSL error messages
+    if (details->tlsSupportedVersion && Security::Tls1p2orEarlier(details->tlsSupportedVersion))
+        SSL_set_options(ssl, SSL_OP_NO_TLSv1_3);
+#endif
+
 #if defined(TLSEXT_STATUSTYPE_ocsp)
     if (details->tlsStatusRequest)
         SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp);
diff -u -r -N squid-4.11/src/ssl/bio.h squid-4.12/src/ssl/bio.h
--- squid-4.11/src/ssl/bio.h	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/ssl/bio.h	2020-06-08 03:42:16.000000000 +1200
@@ -143,6 +143,10 @@
 
     bool resumingSession();
 
+    /// whether the server encrypts its certificate (e.g., TLS v1.3)
+    /// \retval false the server uses plain certs or its intent is unknown
+    bool encryptedCertificates() const;
+
     /// The write hold state
     bool holdWrite() const {return holdWrite_;}
     /// Enables or disables the write hold state
diff -u -r -N squid-4.11/src/ssl/ErrorDetail.cc squid-4.12/src/ssl/ErrorDetail.cc
--- squid-4.11/src/ssl/ErrorDetail.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/ssl/ErrorDetail.cc	2020-06-08 03:42:16.000000000 +1200
@@ -233,6 +233,9 @@
         "X509_V_ERR_SUBTREE_MINMAX"
     },
 #endif
+    {   X509_V_ERR_APPLICATION_VERIFICATION, //50
+        "X509_V_ERR_APPLICATION_VERIFICATION"
+    },
 #if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE)
     {
         X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, //51
@@ -257,9 +260,132 @@
         "X509_V_ERR_CRL_PATH_VALIDATION_ERROR"
     },
 #endif
-    {   X509_V_ERR_APPLICATION_VERIFICATION,
-        "X509_V_ERR_APPLICATION_VERIFICATION"
+#if defined(X509_V_ERR_PATH_LOOP)
+    {
+        X509_V_ERR_PATH_LOOP, //55
+        "X509_V_ERR_PATH_LOOP"
+    },
+#endif
+#if defined(X509_V_ERR_SUITE_B_INVALID_VERSION)
+    {
+        X509_V_ERR_SUITE_B_INVALID_VERSION, //56
+        "X509_V_ERR_SUITE_B_INVALID_VERSION"
+    },
+#endif
+#if defined(X509_V_ERR_SUITE_B_INVALID_ALGORITHM)
+    {
+        X509_V_ERR_SUITE_B_INVALID_ALGORITHM, //57
+        "X509_V_ERR_SUITE_B_INVALID_ALGORITHM"
+    },
+#endif
+#if defined(X509_V_ERR_SUITE_B_INVALID_CURVE)
+    {
+        X509_V_ERR_SUITE_B_INVALID_CURVE, //58
+        "X509_V_ERR_SUITE_B_INVALID_CURVE"
+    },
+#endif
+#if defined(X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM)
+    {
+        X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM, //59
+        "X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM"
+    },
+#endif
+#if defined(X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED)
+    {
+        X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED, //60
+        "X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED"
+    },
+#endif
+#if defined(X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256)
+    {
+        X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256, //61
+        "X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256"
+    },
+#endif
+#if defined(X509_V_ERR_HOSTNAME_MISMATCH)
+    {
+        X509_V_ERR_HOSTNAME_MISMATCH, //62
+        "X509_V_ERR_HOSTNAME_MISMATCH"
+    },
+#endif
+#if defined(X509_V_ERR_EMAIL_MISMATCH)
+    {
+        X509_V_ERR_EMAIL_MISMATCH, //63
+        "X509_V_ERR_EMAIL_MISMATCH"
+    },
+#endif
+#if defined(X509_V_ERR_IP_ADDRESS_MISMATCH)
+    {
+        X509_V_ERR_IP_ADDRESS_MISMATCH, //64
+        "X509_V_ERR_IP_ADDRESS_MISMATCH"
+    },
+#endif
+#if defined(X509_V_ERR_DANE_NO_MATCH)
+    {
+        X509_V_ERR_DANE_NO_MATCH, //65
+        "X509_V_ERR_DANE_NO_MATCH"
     },
+#endif
+#if defined(X509_V_ERR_EE_KEY_TOO_SMALL)
+    {
+        X509_V_ERR_EE_KEY_TOO_SMALL, //66
+        "X509_V_ERR_EE_KEY_TOO_SMALL"
+    },
+#endif
+#if defined(X509_V_ERR_CA_KEY_TOO_SMALL)
+    {
+        X509_V_ERR_CA_KEY_TOO_SMALL, //67
+        "X509_V_ERR_CA_KEY_TOO_SMALL"
+    },
+#endif
+#if defined(X509_V_ERR_CA_MD_TOO_WEAK)
+    {
+        X509_V_ERR_CA_MD_TOO_WEAK, //68
+        "X509_V_ERR_CA_MD_TOO_WEAK"
+    },
+#endif
+#if defined(X509_V_ERR_INVALID_CALL)
+    {
+        X509_V_ERR_INVALID_CALL, //69
+        "X509_V_ERR_INVALID_CALL"
+    },
+#endif
+#if defined(X509_V_ERR_STORE_LOOKUP)
+    {
+        X509_V_ERR_STORE_LOOKUP, //70
+        "X509_V_ERR_STORE_LOOKUP"
+    },
+#endif
+#if defined(X509_V_ERR_NO_VALID_SCTS)
+    {
+        X509_V_ERR_NO_VALID_SCTS, //71
+        "X509_V_ERR_NO_VALID_SCTS"
+    },
+#endif
+#if defined(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION)
+    {
+        X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION, //72
+        "X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION"
+    },
+#endif
+#if defined(X509_V_ERR_OCSP_VERIFY_NEEDED)
+    {
+        X509_V_ERR_OCSP_VERIFY_NEEDED, //73
+        "X509_V_ERR_OCSP_VERIFY_NEEDED"
+    },
+#endif
+#if defined(X509_V_ERR_OCSP_VERIFY_FAILED)
+    {
+        X509_V_ERR_OCSP_VERIFY_FAILED, //74
+        "X509_V_ERR_OCSP_VERIFY_FAILED"
+    },
+#endif
+#if defined(X509_V_ERR_OCSP_CERT_UNKNOWN)
+    {
+        X509_V_ERR_OCSP_CERT_UNKNOWN, //75
+        "X509_V_ERR_OCSP_CERT_UNKNOWN"
+    },
+#endif
     { SSL_ERROR_NONE, "SSL_ERROR_NONE"},
     {SSL_ERROR_NONE, NULL}
 };
@@ -286,6 +412,27 @@
     "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX",
     "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX",
     "X509_V_ERR_CRL_PATH_VALIDATION_ERROR",
+    "X509_V_ERR_PATH_LOOP",
+    "X509_V_ERR_SUITE_B_INVALID_VERSION",
+    "X509_V_ERR_SUITE_B_INVALID_ALGORITHM",
+    "X509_V_ERR_SUITE_B_INVALID_CURVE",
+    "X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM",
+    "X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED",
+    "X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256",
+    "X509_V_ERR_HOSTNAME_MISMATCH",
+    "X509_V_ERR_EMAIL_MISMATCH",
+    "X509_V_ERR_IP_ADDRESS_MISMATCH",
+    "X509_V_ERR_DANE_NO_MATCH",
+    "X509_V_ERR_EE_KEY_TOO_SMALL",
+    "X509_V_ERR_CA_KEY_TOO_SMALL",
+    "X509_V_ERR_CA_MD_TOO_WEAK",
+    "X509_V_ERR_INVALID_CALL",
+    "X509_V_ERR_STORE_LOOKUP",
+    "X509_V_ERR_NO_VALID_SCTS",
+    "X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION",
+    "X509_V_ERR_OCSP_VERIFY_NEEDED",
+    "X509_V_ERR_OCSP_VERIFY_FAILED",
+    "X509_V_ERR_OCSP_CERT_UNKNOWN",
     NULL
 };
 
@@ -390,7 +537,7 @@
     return false; // not reached
 }
 
-const char *Ssl::GetErrorName(Security::ErrorCode value)
+const char *Ssl::GetErrorName(Security::ErrorCode value, const bool prefixRawCode)
 {
     if (TheSslErrors.empty())
         loadSslErrorMap();
@@ -399,7 +546,9 @@
     if (it != TheSslErrors.end())
         return it->second->name;
 
-    return NULL;
+    static char tmpBuffer[128];
+    snprintf(tmpBuffer, sizeof(tmpBuffer), "%s%d", prefixRawCode ? "SSL_ERR=" : "", (int)value);
+    return tmpBuffer;
 }
 
 bool
@@ -529,21 +678,14 @@
  */
 const char *Ssl::ErrorDetail::err_code() const
 {
-    static char tmpBuffer[64];
     // We can use the GetErrorName but using the detailEntry is faster,
     // so try it first.
-    const char *err = detailEntry.name.termedBuf();
+    if (const char *err = detailEntry.name.termedBuf())
+        return err;
 
     // error details not loaded yet or not defined in error_details.txt,
     // try the GetErrorName...
-    if (!err)
-        err = GetErrorName(error_no);
-
-    if (!err) {
-        snprintf(tmpBuffer, 64, "%d", (int)error_no);
-        err = tmpBuffer;
-    }
-    return err;
+    return GetErrorName(error_no);
 }
 
 /**
diff -u -r -N squid-4.11/src/ssl/ErrorDetail.h squid-4.12/src/ssl/ErrorDetail.h
--- squid-4.11/src/ssl/ErrorDetail.h	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/ssl/ErrorDetail.h	2020-06-08 03:42:16.000000000 +1200
@@ -26,8 +26,9 @@
 /// The Security::ErrorCode code of the error described by  "name".
 Security::ErrorCode GetErrorCode(const char *name);
 
-/// The string representation of the TLS error "value"
-const char *GetErrorName(Security::ErrorCode value);
+/// \return string representation of a known TLS error (or a raw error code)
+/// \param prefixRawCode whether to prefix raw codes with "SSL_ERR="
+const char *GetErrorName(Security::ErrorCode value, const bool prefixRawCode = false);
 
 /// A short description of the TLS error "value"
 const char *GetErrorDescr(Security::ErrorCode value);
diff -u -r -N squid-4.11/src/ssl/PeekingPeerConnector.cc squid-4.12/src/ssl/PeekingPeerConnector.cc
--- squid-4.11/src/ssl/PeekingPeerConnector.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/ssl/PeekingPeerConnector.cc	2020-06-08 03:42:16.000000000 +1200
@@ -276,18 +276,32 @@
     BIO *b = SSL_get_rbio(session.get());
     Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
 
-    // In Peek mode, the ClientHello message sent to the server. If the
-    // server resuming a previous (spliced) SSL session with the client,
-    // then probably we are here because local SSL object does not know
-    // anything about the session being resumed.
-    //
-    if (srvBio->bumpMode() == Ssl::bumpPeek && (resumingSession = srvBio->resumingSession())) {
-        // we currently splice all resumed sessions unconditionally
-        // if (const bool spliceResumed = true) {
-        bypassCertValidator();
-        checkForPeekAndSpliceMatched(Ssl::bumpSplice);
-        return;
-        // } // else fall through to find a matching ssl_bump action (with limited info)
+    if (srvBio->bumpMode() == Ssl::bumpPeek) {
+        auto bypassValidator = false;
+        if (srvBio->encryptedCertificates()) {
+            // it is pointless to peek at encrypted certificates
+            //
+            // we currently splice all sessions with encrypted certificates
+            // if (const auto spliceEncryptedCertificates = true) {
+            bypassValidator = true;
+            // } // else fall through to find a matching ssl_bump action (with limited info)
+        } else if (srvBio->resumingSession()) {
+            // In peek mode, the ClientHello message is forwarded to the server.
+            // If the server is resuming a previous (spliced) SSL session with
+            // the client, then probably we are here because our local SSL
+            // object does not know anything about the session being resumed.
+            //
+            // we currently splice all resumed sessions
+            // if (const auto spliceResumed = true) {
+            bypassValidator = true;
+            // } // else fall through to find a matching ssl_bump action (with limited info)
+        }
+
+        if (bypassValidator) {
+            bypassCertValidator();
+            checkForPeekAndSpliceMatched(Ssl::bumpSplice);
+            return;
+        }
     }
 
     // If we are in peek-and-splice mode and still we did not write to
diff -u -r -N squid-4.11/src/ssl/PeekingPeerConnector.h squid-4.12/src/ssl/PeekingPeerConnector.h
--- squid-4.11/src/ssl/PeekingPeerConnector.h	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/ssl/PeekingPeerConnector.h	2020-06-08 03:42:16.000000000 +1200
@@ -30,7 +30,6 @@
         Security::PeerConnector(aServerConn, aCallback, alp, timeout),
         clientConn(aClientConn),
         splice(false),
-        resumingSession(false),
         serverCertificateHandled(false)
     {
         request = aRequest;
@@ -75,7 +74,6 @@
     Comm::ConnectionPointer clientConn; ///< TCP connection to the client
     AsyncCall::Pointer closeHandler; ///< we call this when the connection closed
     bool splice; ///< whether we are going to splice or not
-    bool resumingSession; ///< whether it is an SSL resuming session connection
     bool serverCertificateHandled; ///< whether handleServerCertificate() succeeded
 };
 
diff -u -r -N squid-4.11/src/store/id_rewriters/file/storeid_file_rewrite.8 squid-4.12/src/store/id_rewriters/file/storeid_file_rewrite.8
--- squid-4.11/src/store/id_rewriters/file/storeid_file_rewrite.8	2020-04-20 00:50:36.000000000 +1200
+++ squid-4.12/src/store/id_rewriters/file/storeid_file_rewrite.8	2020-06-09 19:27:17.000000000 +1200
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "STOREID_FILE_REWRITE 8"
-.TH STOREID_FILE_REWRITE 8 "2020-04-19" "perl v5.28.1" "User Contributed Perl Documentation"
+.TH STOREID_FILE_REWRITE 8 "2020-06-09" "perl v5.30.3" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -u -r -N squid-4.11/src/store.cc squid-4.12/src/store.cc
--- squid-4.11/src/store.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/store.cc	2020-06-08 03:42:16.000000000 +1200
@@ -917,7 +917,6 @@
         int non_get;
         int not_entry_cachable;
         int wrong_content_length;
-        int negative_cached;
         int too_big;
         int too_small;
         int private_key;
@@ -998,10 +997,6 @@
         if (store_status == STORE_OK && EBIT_TEST(flags, ENTRY_BAD_LENGTH)) {
             debugs(20, 2, "StoreEntry::checkCachable: NO: wrong content-length");
             ++store_check_cachable_hist.no.wrong_content_length;
-        } else if (EBIT_TEST(flags, ENTRY_NEGCACHED)) {
-            debugs(20, 3, "StoreEntry::checkCachable: NO: negative cached");
-            ++store_check_cachable_hist.no.negative_cached;
-            return 0;           /* avoid release call below */
         } else if (!mem_obj || !getReply()) {
             // XXX: In bug 4131, we forgetHit() without mem_obj, so we need
             // this segfault protection, but how can we get such a HIT?
@@ -1053,7 +1048,7 @@
     storeAppendPrintf(sentry, "no.wrong_content_length\t%d\n",
                       store_check_cachable_hist.no.wrong_content_length);
     storeAppendPrintf(sentry, "no.negative_cached\t%d\n",
-                      store_check_cachable_hist.no.negative_cached);
+                      0); // TODO: Remove this backward compatibility hack.
     storeAppendPrintf(sentry, "no.missing_parts\t%d\n",
                       store_check_cachable_hist.no.missing_parts);
     storeAppendPrintf(sentry, "no.too_big\t%d\n",
@@ -1412,7 +1407,10 @@
 #else
         expires = squid_curtime;
 #endif
-    EBIT_SET(flags, ENTRY_NEGCACHED);
+    if (expires > squid_curtime) {
+        EBIT_SET(flags, ENTRY_NEGCACHED);
+        debugs(20, 6, "expires = " << expires << " +" << (expires-squid_curtime) << ' ' << *this);
+    }
 }
 
 void
diff -u -r -N squid-4.11/src/tests/stub_libanyp.cc squid-4.12/src/tests/stub_libanyp.cc
--- squid-4.11/src/tests/stub_libanyp.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/tests/stub_libanyp.cc	2020-06-08 03:42:16.000000000 +1200
@@ -18,6 +18,7 @@
 void AnyP::Uri::host(const char *) STUB
 static SBuf nil;
 const SBuf &AnyP::Uri::path() const STUB_RETVAL(nil)
+void AnyP::Uri::addRelativePath(const char *) STUB
 const SBuf &AnyP::Uri::SlashPath()
 {
     static SBuf slash("/");
@@ -33,11 +34,9 @@
 void urlInitialize() STUB
 const char *urlCanonicalFakeHttps(const HttpRequest *) STUB_RETVAL(nullptr)
 bool urlIsRelative(const char *) STUB_RETVAL(false)
-char *urlMakeAbsolute(const HttpRequest *, const char *)STUB_RETVAL(nullptr)
 char *urlRInternal(const char *, unsigned short, const char *, const char *) STUB_RETVAL(nullptr)
 char *urlInternal(const char *, const char *) STUB_RETVAL(nullptr)
 int matchDomainName(const char *, const char *, uint) STUB_RETVAL(0)
 int urlCheckRequest(const HttpRequest *) STUB_RETVAL(0)
-char *urlHostname(const char *) STUB_RETVAL(nullptr)
 void urlExtMethodConfigure() STUB
 
diff -u -r -N squid-4.11/src/tests/stub_libsecurity.cc squid-4.12/src/tests/stub_libsecurity.cc
--- squid-4.11/src/tests/stub_libsecurity.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/tests/stub_libsecurity.cc	2020-06-08 03:42:16.000000000 +1200
@@ -28,7 +28,7 @@
 std::ostream &Security::operator <<(std::ostream &os, const Security::EncryptorAnswer &) STUB_RETVAL(os)
 
 #include "security/Handshake.h"
-Security::HandshakeParser::HandshakeParser() STUB
+Security::HandshakeParser::HandshakeParser(MessageSource) STUB
 bool Security::HandshakeParser::parseHello(const SBuf &) STUB_RETVAL(false)
 
 #include "security/KeyData.h"
diff -u -r -N squid-4.11/src/tests/stub_StatHist.cc squid-4.12/src/tests/stub_StatHist.cc
--- squid-4.11/src/tests/stub_StatHist.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/tests/stub_StatHist.cc	2020-06-08 03:42:16.000000000 +1200
@@ -16,7 +16,7 @@
 
 void StatHist::dump(StoreEntry * sentry, StatHistBinDumper * bd) const STUB
 void StatHist::enumInit(unsigned int i) STUB_NOP
-void StatHist::count(double d) STUB_NOP
+void StatHist::count(double) {/* STUB_NOP */}
 double statHistDeltaMedian(const StatHist & A, const StatHist & B) STUB_RETVAL(0.0)
 double statHistDeltaPctile(const StatHist & A, const StatHist & B, double pctile) STUB_RETVAL(0.0)
 void StatHist::logInit(unsigned int i, double d1, double d2) STUB
diff -u -r -N squid-4.11/src/urn.cc squid-4.12/src/urn.cc
--- squid-4.11/src/urn.cc	2020-04-20 00:38:51.000000000 +1200
+++ squid-4.12/src/urn.cc	2020-06-08 03:42:16.000000000 +1200
@@ -370,7 +370,6 @@
 {
     char *buf = xstrdup(inbuf);
     char *token;
-    char *host;
     url_entry *list;
     url_entry *old;
     int n = 32;
@@ -389,24 +388,23 @@
             safe_free(old);
         }
 
-        host = urlHostname(token);
-
-        if (NULL == host)
+        AnyP::Uri uri;
+        if (!uri.parse(m, SBuf(token)) || !*uri.host())
             continue;
 
 #if USE_ICMP
-        list[i].rtt = netdbHostRtt(host);
+        list[i].rtt = netdbHostRtt(uri.host());
 
         if (0 == list[i].rtt) {
-            debugs(52, 3, "urnParseReply: Pinging " << host);
-            netdbPingSite(host);
+            debugs(52, 3, "Pinging " << uri.host());
+            netdbPingSite(uri.host());
         }
 #else
         list[i].rtt = 0;
 #endif
 
-        list[i].url = xstrdup(token);
-        list[i].host = xstrdup(host);
+        list[i].url = xstrdup(uri.absolute().c_str());
+        list[i].host = xstrdup(uri.host());
         // TODO: Use storeHas() or lock/unlock entry to avoid creating unlocked
         // ones.
         list[i].flags.cached = storeGetPublic(list[i].url, m) ? 1 : 0;
diff -u -r -N squid-4.11/tools/CharacterSet.cc squid-4.12/tools/CharacterSet.cc
--- squid-4.11/tools/CharacterSet.cc	2020-04-20 00:50:38.000000000 +1200
+++ squid-4.12/tools/CharacterSet.cc	2020-06-09 19:27:19.000000000 +1200
@@ -51,6 +51,13 @@
 }
 
 CharacterSet &
+CharacterSet::remove(const unsigned char c)
+{
+    chars_[static_cast<uint8_t>(c)] = 0;
+    return *this;
+}
+
+CharacterSet &
 CharacterSet::addRange(unsigned char low, unsigned char high)
 {
     //manual loop splitting is needed to cover case where high is 255
diff -u -r -N squid-4.11/tools/helper-mux/helper-mux.8 squid-4.12/tools/helper-mux/helper-mux.8
--- squid-4.11/tools/helper-mux/helper-mux.8	2020-04-20 00:50:38.000000000 +1200
+++ squid-4.12/tools/helper-mux/helper-mux.8	2020-06-09 19:27:19.000000000 +1200
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
+.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "HELPER-MUX 8"
-.TH HELPER-MUX 8 "2020-04-19" "perl v5.28.1" "User Contributed Perl Documentation"
+.TH HELPER-MUX 8 "2020-06-09" "perl v5.30.3" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
