diff -u -r -N squid-5.0.2/acinclude/os-deps.m4 squid-5.0.3/acinclude/os-deps.m4
--- squid-5.0.2/acinclude/os-deps.m4	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/acinclude/os-deps.m4	2020-06-08 03:33:42.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-5.0.2/ChangeLog squid-5.0.3/ChangeLog
--- squid-5.0.2/ChangeLog	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/ChangeLog	2020-06-08 03:33:42.000000000 +1200
@@ -1,3 +1,13 @@
+Changes in squid-5.0.3 (05 Jun 2020):
+
+	- Bug 5046: FreeBSD lacks open(2) O_DSYNC flag
+	- Happy Eyeballs: Do not discard viable reforwarding destinations
+	- Reduced startup time with large rock cache_dirs
+	- Fix the ABA problem with Ipc::Mem::PageStack::pop() in v5.0.1
+	- Fix sending of unknown validation errors to certificate validator
+	- ... and several debug improvements
+	- ... and all fixes from 4.12
+
 Changes in squid-5.0.2 (18 Apr 2020):
 
 	- Bug 5030: Negative responses are never cached
@@ -9,6 +19,7 @@
 	- Fix PURGE error responses
 	- ... and several documentation changes
 	- ... and some compile fixes
+	- ... and all fixes from 4.11
 
 Changes in squid-5.0.1 (14 Jan 2020):
 
@@ -56,6 +67,21 @@
 	- Centralized PagePool/PageStack ID generation
 	- ... and many documentation changes
 	- ... and much code cleanup and polishing
+	- ... and all fixes from 4.10
+
+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):
 
diff -u -r -N squid-5.0.2/configure squid-5.0.3/configure
--- squid-5.0.2/configure	2020-04-20 00:08:55.000000000 +1200
+++ squid-5.0.3/configure	2020-06-09 18:58:26.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 5.0.2.
+# Generated by GNU Autoconf 2.69 for Squid Web Proxy 5.0.3.
 #
 # 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='5.0.2'
-PACKAGE_STRING='Squid Web Proxy 5.0.2'
+PACKAGE_VERSION='5.0.3'
+PACKAGE_STRING='Squid Web Proxy 5.0.3'
 PACKAGE_BUGREPORT='http://bugs.squid-cache.org/'
 PACKAGE_URL=''
 
@@ -1662,7 +1662,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 5.0.2 to adapt to many kinds of systems.
+\`configure' configures Squid Web Proxy 5.0.3 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1733,7 +1733,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Squid Web Proxy 5.0.2:";;
+     short | recursive ) echo "Configuration of Squid Web Proxy 5.0.3:";;
    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 5.0.2
+Squid Web Proxy configure 5.0.3
 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 5.0.2, which was
+It was created by Squid Web Proxy $as_me 5.0.3, 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='5.0.2'
+ VERSION='5.0.3'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -21097,8 +21097,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}"
@@ -21115,63 +21116,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}"
@@ -42692,6 +42706,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
@@ -42717,7 +42734,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 :
@@ -42728,11 +42749,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
@@ -42758,13 +42781,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
 
 
@@ -44931,7 +44958,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 5.0.2, which was
+This file was extended by Squid Web Proxy $as_me 5.0.3, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -44997,7 +45024,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 5.0.2
+Squid Web Proxy config.status 5.0.3
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff -u -r -N squid-5.0.2/configure.ac squid-5.0.3/configure.ac
--- squid-5.0.2/configure.ac	2020-04-20 00:08:55.000000000 +1200
+++ squid-5.0.3/configure.ac	2020-06-09 18:58:26.000000000 +1200
@@ -5,7 +5,7 @@
 ## Please see the COPYING and CONTRIBUTORS files for details.
 ##
 
-AC_INIT([Squid Web Proxy],[5.0.2],[http://bugs.squid-cache.org/],[squid])
+AC_INIT([Squid Web Proxy],[5.0.3],[http://bugs.squid-cache.org/],[squid])
 AC_PREREQ(2.61)
 AC_CONFIG_HEADERS([include/autoconf.h])
 AC_CONFIG_AUX_DIR(cfgaux)
@@ -440,11 +440,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)
 
diff -u -r -N squid-5.0.2/CONTRIBUTORS squid-5.0.3/CONTRIBUTORS
--- squid-5.0.2/CONTRIBUTORS	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/CONTRIBUTORS	2020-06-08 03:33:42.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-5.0.2/doc/release-notes/release-5.html squid-5.0.3/doc/release-notes/release-5.html
--- squid-5.0.2/doc/release-notes/release-5.html	2020-04-20 00:20:13.000000000 +1200
+++ squid-5.0.3/doc/release-notes/release-5.html	2020-06-09 19:09:17.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 5.0.2 release notes</TITLE>
+ <TITLE>Squid 5.0.3 release notes</TITLE>
 </HEAD>
 <BODY>
-<H1>Squid 5.0.2 release notes</H1>
+<H1>Squid 5.0.3 release notes</H1>
 
 <H2>Squid Developers</H2>
 <HR>
@@ -61,7 +61,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-5.0.2 for testing.</P>
+<P>The Squid Team are pleased to announce the release of Squid-5.0.3 for testing.</P>
 <P>This new release is available for download from 
 <A HREF="http://www.squid-cache.org/Versions/v5/">http://www.squid-cache.org/Versions/v5/</A> or the
 <A HREF="http://www.squid-cache.org/Download/http-mirrors.html">mirrors</A>.</P>
@@ -326,6 +326,10 @@
 <P>New code <EM>%A</EM> to display Squid listening IP address the client
 TCP connection was connected to.</P>
 
+<DT><B>http_port</B><DD>
+<P>New <EM>worker-queues</EM> option to have TCP stack maintain dedicated
+listening queue for each worker in SMP.</P>
+
 <DT><B>logformat</B><DD>
 <P>New <EM>ssl::&lt;cert</EM> macro code to display received server X.509
 certificate in PEM format.</P>
@@ -402,6 +406,10 @@
 <DT><B>--disable-optimizations</B><DD>
 <P>No longer implies <EM>--disable-inline</EM> option (which is removed).</P>
 
+<DT><B>--enable-external-acl-helpers</B><DD>
+<P>New helper type <EM>kerberos_sid_group</EM> to match <EM>group=</EM>
+annotations AD Domain group SID.</P>
+
 </DL>
 </P>
 <H2><A NAME="removedoptions"></A> <A NAME="ss4.3">4.3</A> <A HREF="#toc4.3">Removed options</A>
diff -u -r -N squid-5.0.2/include/autoconf.h.in squid-5.0.3/include/autoconf.h.in
--- squid-5.0.2/include/autoconf.h.in	2020-04-20 00:08:47.000000000 +1200
+++ squid-5.0.3/include/autoconf.h.in	2020-06-09 18:58:17.000000000 +1200
@@ -1041,8 +1041,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-5.0.2/include/version.h squid-5.0.3/include/version.h
--- squid-5.0.2/include/version.h	2020-04-20 00:08:55.000000000 +1200
+++ squid-5.0.3/include/version.h	2020-06-09 18:58:26.000000000 +1200
@@ -7,7 +7,7 @@
  */
 
 #ifndef SQUID_RELEASE_TIME
-#define SQUID_RELEASE_TIME 1587298122
+#define SQUID_RELEASE_TIME 1591685891
 #endif
 
 /*
diff -u -r -N squid-5.0.2/RELEASENOTES.html squid-5.0.3/RELEASENOTES.html
--- squid-5.0.2/RELEASENOTES.html	2020-04-20 00:20:13.000000000 +1200
+++ squid-5.0.3/RELEASENOTES.html	2020-06-09 19:09:17.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 5.0.2 release notes</TITLE>
+ <TITLE>Squid 5.0.3 release notes</TITLE>
 </HEAD>
 <BODY>
-<H1>Squid 5.0.2 release notes</H1>
+<H1>Squid 5.0.3 release notes</H1>
 
 <H2>Squid Developers</H2>
 <HR>
@@ -61,7 +61,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-5.0.2 for testing.</P>
+<P>The Squid Team are pleased to announce the release of Squid-5.0.3 for testing.</P>
 <P>This new release is available for download from 
 <A HREF="http://www.squid-cache.org/Versions/v5/">http://www.squid-cache.org/Versions/v5/</A> or the
 <A HREF="http://www.squid-cache.org/Download/http-mirrors.html">mirrors</A>.</P>
@@ -326,6 +326,10 @@
 <P>New code <EM>%A</EM> to display Squid listening IP address the client
 TCP connection was connected to.</P>
 
+<DT><B>http_port</B><DD>
+<P>New <EM>worker-queues</EM> option to have TCP stack maintain dedicated
+listening queue for each worker in SMP.</P>
+
 <DT><B>logformat</B><DD>
 <P>New <EM>ssl::&lt;cert</EM> macro code to display received server X.509
 certificate in PEM format.</P>
@@ -402,6 +406,10 @@
 <DT><B>--disable-optimizations</B><DD>
 <P>No longer implies <EM>--disable-inline</EM> option (which is removed).</P>
 
+<DT><B>--enable-external-acl-helpers</B><DD>
+<P>New helper type <EM>kerberos_sid_group</EM> to match <EM>group=</EM>
+annotations AD Domain group SID.</P>
+
 </DL>
 </P>
 <H2><A NAME="removedoptions"></A> <A NAME="ss4.3">4.3</A> <A HREF="#toc4.3">Removed options</A>
diff -u -r -N squid-5.0.2/src/acl/external/delayer/ext_delayer_acl.8 squid-5.0.3/src/acl/external/delayer/ext_delayer_acl.8
--- squid-5.0.2/src/acl/external/delayer/ext_delayer_acl.8	2020-04-20 00:20:15.000000000 +1200
+++ squid-5.0.3/src/acl/external/delayer/ext_delayer_acl.8	2020-06-09 19:09:20.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-5.0.2/src/acl/external/eDirectory_userip/ext_edirectory_userip_acl.cc squid-5.0.3/src/acl/external/eDirectory_userip/ext_edirectory_userip_acl.cc
--- squid-5.0.2/src/acl/external/eDirectory_userip/ext_edirectory_userip_acl.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/acl/external/eDirectory_userip/ext_edirectory_userip_acl.cc	2020-06-08 03:33:42.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-5.0.2/src/acl/external/kerberos_ldap_group/support_krb5.cc squid-5.0.3/src/acl/external/kerberos_ldap_group/support_krb5.cc
--- squid-5.0.2/src/acl/external/kerberos_ldap_group/support_krb5.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/acl/external/kerberos_ldap_group/support_krb5.cc	2020-06-08 03:33:42.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-5.0.2/src/acl/external/kerberos_sid_group/ext_kerberos_sid_group_acl.8 squid-5.0.3/src/acl/external/kerberos_sid_group/ext_kerberos_sid_group_acl.8
--- squid-5.0.2/src/acl/external/kerberos_sid_group/ext_kerberos_sid_group_acl.8	2020-04-20 00:20:15.000000000 +1200
+++ squid-5.0.3/src/acl/external/kerberos_sid_group/ext_kerberos_sid_group_acl.8	2020-06-09 19:09:20.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_KERBEROS_SID_GROUP_ACL 8"
-.TH EXT_KERBEROS_SID_GROUP_ACL 8 "2020-04-19" "perl v5.28.1" "User Contributed Perl Documentation"
+.TH EXT_KERBEROS_SID_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-5.0.2/src/acl/external/session/ext_session_acl.cc squid-5.0.3/src/acl/external/session/ext_session_acl.cc
--- squid-5.0.2/src/acl/external/session/ext_session_acl.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/acl/external/session/ext_session_acl.cc	2020-06-08 03:33:42.000000000 +1200
@@ -137,6 +137,10 @@
         }
     }
 #elif USE_TRIVIALDB
+#if _SQUID_FREEBSD_ && !defined(O_DSYNC)
+    // FreeBSD lacks O_DSYNC, O_SYNC is closest to correct behaviour
+#define O_DSYNC O_SYNC
+#endif
     db = tdb_open(db_path, 0, TDB_CLEAR_IF_FIRST, O_CREAT|O_DSYNC, 0666);
 #endif
     if (!db) {
diff -u -r -N squid-5.0.2/src/acl/external/SQL_session/ext_sql_session_acl.8 squid-5.0.3/src/acl/external/SQL_session/ext_sql_session_acl.8
--- squid-5.0.2/src/acl/external/SQL_session/ext_sql_session_acl.8	2020-04-20 00:20:16.000000000 +1200
+++ squid-5.0.3/src/acl/external/SQL_session/ext_sql_session_acl.8	2020-06-09 19:09:20.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-5.0.2/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8 squid-5.0.3/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8
--- squid-5.0.2/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8	2020-04-20 00:20:16.000000000 +1200
+++ squid-5.0.3/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8	2020-06-09 19:09:20.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-5.0.2/src/anyp/ProtocolVersion.h squid-5.0.3/src/anyp/ProtocolVersion.h
--- squid-5.0.2/src/anyp/ProtocolVersion.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/anyp/ProtocolVersion.h	2020-06-08 03:33:42.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-5.0.2/src/anyp/Uri.cc squid-5.0.3/src/anyp/Uri.cc
--- squid-5.0.2/src/anyp/Uri.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/anyp/Uri.cc	2020-06-08 03:33:42.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-5.0.2/src/anyp/Uri.h squid-5.0.3/src/anyp/Uri.h
--- squid-5.0.2/src/anyp/Uri.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/anyp/Uri.h	2020-06-08 03:33:42.000000000 +1200
@@ -77,6 +77,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);
@@ -98,12 +100,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.
      *
@@ -199,7 +216,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
@@ -245,7 +261,6 @@
  */
 int matchDomainName(const char *host, const char *domain, MatchDomainNameFlags 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-5.0.2/src/auth/basic/DB/basic_db_auth.8 squid-5.0.3/src/auth/basic/DB/basic_db_auth.8
--- squid-5.0.2/src/auth/basic/DB/basic_db_auth.8	2020-04-20 00:20:16.000000000 +1200
+++ squid-5.0.3/src/auth/basic/DB/basic_db_auth.8	2020-06-09 19:09:21.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-5.0.2/src/auth/basic/POP3/basic_pop3_auth.8 squid-5.0.3/src/auth/basic/POP3/basic_pop3_auth.8
--- squid-5.0.2/src/auth/basic/POP3/basic_pop3_auth.8	2020-04-20 00:20:17.000000000 +1200
+++ squid-5.0.3/src/auth/basic/POP3/basic_pop3_auth.8	2020-06-09 19:09:21.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-5.0.2/src/base/CharacterSet.cc squid-5.0.3/src/base/CharacterSet.cc
--- squid-5.0.2/src/base/CharacterSet.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/base/CharacterSet.cc	2020-06-08 03:33:42.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-5.0.2/src/base/CharacterSet.h squid-5.0.3/src/base/CharacterSet.h
--- squid-5.0.2/src/base/CharacterSet.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/base/CharacterSet.h	2020-06-08 03:33:42.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-5.0.2/src/ClientInfo.h squid-5.0.3/src/ClientInfo.h
--- squid-5.0.2/src/ClientInfo.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ClientInfo.h	2020-06-08 03:33:42.000000000 +1200
@@ -78,6 +78,8 @@
     unsigned int quotaPeekReserv() const; ///< returns the next reserv. to pop
     void quotaDequeue(); ///< pops queue head from queue
     void kickQuotaQueue(); ///< schedule commHandleWriteHelper call
+    /// either selects the head descriptor for writing or calls quotaDequeue()
+    void writeOrDequeue();
 
     /* BandwidthBucket API */
     virtual int quota() override; ///< allocate quota for a just dequeued client
diff -u -r -N squid-5.0.2/src/clients/Client.cc squid-5.0.3/src/clients/Client.cc
--- squid-5.0.2/src/clients/Client.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/clients/Client.cc	2020-06-08 03:33:42.000000000 +1200
@@ -426,7 +426,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, ':');
 
@@ -455,33 +455,40 @@
 static void
 purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, Http::Message *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-5.0.2/src/clients/HttpTunnelerAnswer.cc squid-5.0.3/src/clients/HttpTunnelerAnswer.cc
--- squid-5.0.2/src/clients/HttpTunnelerAnswer.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/clients/HttpTunnelerAnswer.cc	2020-06-08 03:33:42.000000000 +1200
@@ -32,6 +32,9 @@
     if (answer.peerResponseStatus != Http::scNone)
         os << ' ' << answer.peerResponseStatus;
 
+    if (answer.conn)
+        os << ' ' << answer.conn;
+
     os << ']';
     return os;
 }
diff -u -r -N squid-5.0.2/src/clients/HttpTunnelerAnswer.h squid-5.0.3/src/clients/HttpTunnelerAnswer.h
--- squid-5.0.2/src/clients/HttpTunnelerAnswer.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/clients/HttpTunnelerAnswer.h	2020-06-08 03:33:42.000000000 +1200
@@ -43,6 +43,8 @@
 
     /// the status code of the successfully parsed CONNECT response (or scNone)
     StatusCode peerResponseStatus = scNone;
+
+    Comm::ConnectionPointer conn;
 };
 
 std::ostream &operator <<(std::ostream &, const Http::TunnelerAnswer &);
diff -u -r -N squid-5.0.2/src/clients/HttpTunneler.cc squid-5.0.3/src/clients/HttpTunneler.cc
--- squid-5.0.2/src/clients/HttpTunneler.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/clients/HttpTunneler.cc	2020-06-08 03:33:42.000000000 +1200
@@ -18,6 +18,8 @@
 #include "http/one/ResponseParser.h"
 #include "http/StateFlags.h"
 #include "HttpRequest.h"
+#include "neighbors.h"
+#include "pconn.h"
 #include "SquidConfig.h"
 #include "StatCounters.h"
 
@@ -25,6 +27,7 @@
 
 Http::Tunneler::Tunneler(const Comm::ConnectionPointer &conn, const HttpRequest::Pointer &req, AsyncCall::Pointer &aCallback, time_t timeout, const AccessLogEntryPointer &alp):
     AsyncJob("Http::Tunneler"),
+    noteFwdPconnUse(false),
     connection(conn),
     request(req),
     callback(aCallback),
@@ -41,6 +44,7 @@
     assert(callback);
     assert(dynamic_cast<Http::TunnelerAnswer *>(callback->getDialer()));
     url = request->url.authority();
+    watchForClosures();
 }
 
 Http::Tunneler::~Tunneler()
@@ -73,11 +77,22 @@
     Must(url.length());
     Must(lifetimeLimit >= 0);
 
+    // we own this Comm::Connection object and its fd exclusively, but must bail
+    // if others started closing the socket while we were waiting to start()
+    assert(Comm::IsConnOpen(connection));
+    if (fd_table[connection->fd].closing()) {
+        bailWith(new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw(), al));
+        return;
+    }
+
     const auto peer = connection->getPeer();
-    Must(peer); // bail if our peer was reconfigured away
+    // bail if our peer was reconfigured away
+    if (!peer) {
+        bailWith(new ErrorState(ERR_CONNECT_FAIL, Http::scInternalServerError, request.getRaw(), al));
+        return;
+    }
     request->prepForPeering(*peer);
 
-    watchForClosures();
     writeRequest();
     startReadingResponse();
 }
@@ -85,8 +100,8 @@
 void
 Http::Tunneler::handleConnectionClosure(const CommCloseCbParams &params)
 {
-    mustStop("server connection gone");
-    callback = nullptr; // the caller must monitor closures
+    closer = nullptr;
+    bailWith(new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw(), al));
 }
 
 /// make sure we quit if/when the connection is gone
@@ -104,12 +119,11 @@
     comm_add_close_handler(connection->fd, closer);
 }
 
+/// The connection read timeout callback handler.
 void
-Http::Tunneler::handleException(const std::exception& e)
+Http::Tunneler::handleTimeout(const CommTimeoutCbParams &)
 {
-    debugs(83, 2, e.what() << status());
-    connection->close();
-    bailWith(new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al));
+    bailWith(new ErrorState(ERR_CONNECT_FAIL, Http::scGatewayTimeout, request.getRaw(), al));
 }
 
 void
@@ -255,8 +269,11 @@
     Comm::Read(connection, reader);
 
     AsyncCall::Pointer nil;
+    typedef CommCbMemFunT<Http::Tunneler, CommTimeoutCbParams> TimeoutDialer;
+    AsyncCall::Pointer timeoutCall = JobCallback(93, 5,
+                                     TimeoutDialer, this, Http::Tunneler::handleTimeout);
     const auto timeout = Comm::MortalReadTimeout(startTime, lifetimeLimit);
-    commSetConnTimeout(connection, timeout, nil);
+    commSetConnTimeout(connection, timeout, timeoutCall);
 }
 
 /// Parses [possibly incomplete] CONNECT response and reacts to it.
@@ -342,13 +359,51 @@
 {
     Must(error);
     answer().squidError = error;
+
+    if (const auto p = connection->getPeer())
+        peerConnectFailed(p);
+
     callBack();
+    disconnect();
+
+    if (noteFwdPconnUse)
+        fwdPconnPool->noteUses(fd_table[connection->fd].pconn.uses);
+    // TODO: Reuse to-peer connections after a CONNECT error response.
+    connection->close();
+    connection = nullptr;
+}
+
+void
+Http::Tunneler::sendSuccess()
+{
+    assert(answer().positive());
+    callBack();
+    disconnect();
+}
+
+void
+Http::Tunneler::disconnect()
+{
+    if (closer) {
+        comm_remove_close_handler(connection->fd, closer);
+        closer = nullptr;
+    }
+
+    if (reader) {
+        Comm::ReadCancel(connection->fd, reader);
+        reader = nullptr;
+    }
+
+    // remove connection timeout handler
+    commUnsetConnTimeout(connection);
 }
 
 void
 Http::Tunneler::callBack()
 {
     debugs(83, 5, connection << status());
+    if (answer().positive())
+        answer().conn = connection;
     auto cb = callback;
     callback = nullptr;
     ScheduleCallHere(cb);
@@ -361,8 +416,7 @@
 
     if (callback) {
         if (requestWritten && tunnelEstablished) {
-            assert(answer().positive());
-            callBack(); // success
+            sendSuccess();
         } else {
             // we should have bailed when we discovered the job-killing problem
             debugs(83, DBG_IMPORTANT, "BUG: Unexpected state while establishing a CONNECT tunnel " << connection << status());
@@ -370,16 +424,6 @@
         }
         assert(!callback);
     }
-
-    if (closer) {
-        comm_remove_close_handler(connection->fd, closer);
-        closer = nullptr;
-    }
-
-    if (reader) {
-        Comm::ReadCancel(connection->fd, reader);
-        reader = nullptr;
-    }
 }
 
 const char *
diff -u -r -N squid-5.0.2/src/clients/HttpTunneler.h squid-5.0.3/src/clients/HttpTunneler.h
--- squid-5.0.2/src/clients/HttpTunneler.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/clients/HttpTunneler.h	2020-06-08 03:33:42.000000000 +1200
@@ -26,15 +26,9 @@
 namespace Http
 {
 
-/// Establishes an HTTP CONNECT tunnel through a forward proxy.
-///
-/// The caller receives a call back with Http::TunnelerAnswer.
-///
-/// The caller must monitor the connection for closure because this job will not
-/// inform the caller about such events.
-///
-/// This job never closes the connection, even on errors. If a 3rd-party closes
-/// the connection, this job simply quits without informing the caller.
+/// Negotiates an HTTP CONNECT tunnel through a forward proxy using a given
+/// (open and, if needed, encrypted) TCP connection to that proxy. Owns the
+/// connection during these negotiations. The caller receives TunnelerAnswer.
 class Tunneler: virtual public AsyncJob
 {
     CBDATA_CLASS(Tunneler);
@@ -71,6 +65,9 @@
     void setDelayId(DelayId delay_id) {delayId = delay_id;}
 #endif
 
+    /// hack: whether the connection requires fwdPconnPool->noteUses()
+    bool noteFwdPconnUse;
+
 protected:
     /* AsyncJob API */
     virtual ~Tunneler();
@@ -81,7 +78,7 @@
 
     void handleConnectionClosure(const CommCloseCbParams&);
     void watchForClosures();
-    void handleException(const std::exception&);
+    void handleTimeout(const CommTimeoutCbParams &);
     void startReadingResponse();
     void writeRequest();
     void handleWrittenRequest(const CommIoCbParams&);
@@ -89,9 +86,19 @@
     void readMore();
     void handleResponse(const bool eof);
     void bailOnResponseError(const char *error, HttpReply *);
+
+    /// sends the given error to the initiator
     void bailWith(ErrorState*);
+
+    /// sends the ready-to-use tunnel to the initiator
+    void sendSuccess();
+
+    /// a bailWith(), sendSuccess() helper: sends results to the initiator
     void callBack();
 
+    /// a bailWith(), sendSuccess() helper: stops monitoring the connection
+    void disconnect();
+
     TunnelerAnswer &answer();
 
 private:
diff -u -r -N squid-5.0.2/src/client_side.cc squid-5.0.3/src/client_side.cc
--- squid-5.0.2/src/client_side.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/client_side.cc	2020-06-08 03:33:42.000000000 +1200
@@ -2187,6 +2187,7 @@
     bodyParser(nullptr),
 #if USE_OPENSSL
     sslBumpMode(Ssl::bumpEnd),
+    tlsParser(Security::HandshakeParser::fromClient),
 #endif
     needProxyProtocolHeader_(false),
 #if USE_OPENSSL
diff -u -r -N squid-5.0.2/src/client_side_reply.cc squid-5.0.3/src/client_side_reply.cc
--- squid-5.0.2/src/client_side_reply.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/client_side_reply.cc	2020-06-08 03:33:42.000000000 +1200
@@ -913,7 +913,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);
         }
@@ -1051,7 +1051,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;
@@ -1067,7 +1067,7 @@
     if (newEntry) {
         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;
@@ -1083,7 +1083,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;
@@ -1094,7 +1094,7 @@
         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;
diff -u -r -N squid-5.0.2/src/comm/forward.h squid-5.0.3/src/comm/forward.h
--- squid-5.0.2/src/comm/forward.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/comm/forward.h	2020-06-08 03:33:42.000000000 +1200
@@ -26,8 +26,6 @@
 
 typedef RefCount<Comm::Connection> ConnectionPointer;
 
-typedef std::vector<Comm::ConnectionPointer> ConnectionList;
-
 bool IsConnOpen(const Comm::ConnectionPointer &conn);
 
 // callback handler to process an FD which is available for writing.
diff -u -r -N squid-5.0.2/src/comm.cc squid-5.0.3/src/comm.cc
--- squid-5.0.2/src/comm.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/comm.cc	2020-06-08 03:33:42.000000000 +1200
@@ -1275,30 +1275,38 @@
     assert(clientInfo);
     assert(clientInfo->hasQueue());
     assert(clientInfo->hasQueue(queue));
-    assert(!clientInfo->selectWaiting);
     assert(clientInfo->eventWaiting);
     clientInfo->eventWaiting = false;
 
     do {
-        // check that the head descriptor is still relevant
-        const int head = clientInfo->quotaPeekFd();
-        Comm::IoCallback *ccb = COMMIO_FD_WRITECB(head);
+        clientInfo->writeOrDequeue();
+        if (clientInfo->selectWaiting)
+            return;
+    } while (clientInfo->hasQueue());
 
-        if (fd_table[head].clientInfo == clientInfo &&
-                clientInfo->quotaPeekReserv() == ccb->quotaQueueReserv &&
-                !fd_table[head].closing()) {
+    debugs(77, 3, "emptied queue");
+}
+
+void
+ClientInfo::writeOrDequeue()
+{
+    assert(!selectWaiting);
+    const auto head = quotaPeekFd();
+    const auto &headFde = fd_table[head];
+    CallBack(headFde.codeContext, [&] {
+        const auto ccb = COMMIO_FD_WRITECB(head);
+        // check that the head descriptor is still relevant
+        if (headFde.clientInfo == this &&
+                quotaPeekReserv() == ccb->quotaQueueReserv &&
+                !headFde.closing()) {
 
             // wait for the head descriptor to become ready for writing
             Comm::SetSelect(head, COMM_SELECT_WRITE, Comm::HandleWrite, ccb, 0);
-            clientInfo->selectWaiting = true;
-            return;
+            selectWaiting = true;
+        } else {
+            quotaDequeue(); // remove the no longer relevant descriptor
         }
-
-        clientInfo->quotaDequeue(); // remove the no longer relevant descriptor
-        // and continue looking for a relevant one
-    } while (clientInfo->hasQueue());
-
-    debugs(77,3, HERE << "emptied queue");
+    });
 }
 
 bool
@@ -1474,6 +1482,7 @@
     debugs(77,5, "clt" << (const char*)clientInfo->key <<
            ": FD " << fd << " with qqid" << (ins+1) << ' ' << fds.size());
     fds.push_back(fd);
+    fd_table[fd].codeContext = CodeContext::Current();
     return ++ins;
 }
 
@@ -1639,6 +1648,7 @@
     debugs(5, 5, HERE << "adding FD " << fd << " to " << *TheHalfClosed);
     assert(isOpen(fd) && !commHasHalfClosedMonitor(fd));
     (void)TheHalfClosed->add(fd); // could also assert the result
+    fd_table[fd].codeContext = CodeContext::Current();
     commPlanHalfClosedCheck(); // may schedule check if we added the first FD
 }
 
@@ -1666,10 +1676,12 @@
         Comm::ConnectionPointer c = new Comm::Connection; // XXX: temporary. make HalfClosed a list of these.
         c->fd = *i;
         if (!fd_table[c->fd].halfClosedReader) { // not reading already
-            AsyncCall::Pointer call = commCbCall(5,4, "commHalfClosedReader",
-                                                 CommIoCbPtrFun(&commHalfClosedReader, NULL));
-            Comm::Read(c, call);
-            fd_table[c->fd].halfClosedReader = call;
+            CallBack(fd_table[c->fd].codeContext, [&c] {
+                AsyncCall::Pointer call = commCbCall(5,4, "commHalfClosedReader",
+                                                     CommIoCbPtrFun(&commHalfClosedReader, nullptr));
+                Comm::Read(c, call);
+                fd_table[c->fd].halfClosedReader = call;
+            });
         } else
             c->fd = -1; // XXX: temporary. prevent c replacement erase closing listed FD
     }
diff -u -r -N squid-5.0.2/src/format/Format.cc squid-5.0.3/src/format/Format.cc
--- squid-5.0.2/src/format/Format.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/format/Format.cc	2020-06-08 03:33:42.000000000 +1200
@@ -341,15 +341,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).
@@ -1002,9 +993,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) {
@@ -1312,10 +1301,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-5.0.2/src/fs/rock/RockSwapDir.cc squid-5.0.3/src/fs/rock/RockSwapDir.cc
--- squid-5.0.2/src/fs/rock/RockSwapDir.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/fs/rock/RockSwapDir.cc	2020-06-08 03:33:42.000000000 +1200
@@ -1136,19 +1136,14 @@
             mapOwners.push_back(mapOwner);
 
             // TODO: somehow remove pool id and counters from PageStack?
+            Ipc::Mem::PageStack::Config config;
+            config.poolId = Ipc::Mem::PageStack::IdForSwapDirSpace(i);
+            config.pageSize = 0; // this is an index of slots on _disk_
+            config.capacity = capacity;
+            config.createFull = false; // Rebuild finds and pushes free slots
             Ipc::Mem::Owner<Ipc::Mem::PageStack> *const freeSlotsOwner =
-                shm_new(Ipc::Mem::PageStack)(sd->freeSlotsPath(),
-                                             Ipc::Mem::PageStack::IdForSwapDirSpace(i),
-                                             capacity,
-                                             0);
+                shm_new(Ipc::Mem::PageStack)(sd->freeSlotsPath(), config);
             freeSlotsOwners.push_back(freeSlotsOwner);
-
-            // TODO: add method to initialize PageStack with no free pages
-            while (true) {
-                Ipc::Mem::PageId pageId;
-                if (!freeSlotsOwner->object()->pop(pageId))
-                    break;
-            }
         }
     }
 }
diff -u -r -N squid-5.0.2/src/FwdState.cc squid-5.0.3/src/FwdState.cc
--- squid-5.0.2/src/FwdState.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/FwdState.cc	2020-06-08 03:33:42.000000000 +1200
@@ -119,6 +119,18 @@
 }
 
 void
+FwdState::closePendingConnection(const Comm::ConnectionPointer &conn, const char *reason)
+{
+    debugs(17, 3, "because " << reason << "; " << conn);
+    assert(!serverConn);
+    assert(!closeHandler);
+    if (IsConnOpen(conn)) {
+        fwdPconnPool->noteUses(fd_table[conn->fd].pconn.uses);
+        conn->close();
+    }
+}
+
+void
 FwdState::closeServerConnection(const char *reason)
 {
     debugs(17, 3, "because " << reason << "; " << serverConn);
@@ -753,6 +765,24 @@
     retryOrBail();
 }
 
+/// starts a preparation step for an established connection; retries on failures
+template <typename StepStart>
+void
+FwdState::advanceDestination(const char *stepDescription, const Comm::ConnectionPointer &conn, const StepStart &startStep)
+{
+    // TODO: Extract destination-specific handling from FwdState so that all the
+    // awkward, limited-scope advanceDestination() calls can be replaced with a
+    // single simple try/catch,retry block.
+    try {
+        startStep();
+        // now wait for the step callback
+    } catch (...) {
+        debugs (17, 2, "exception while trying to " << stepDescription << ": " << CurrentException);
+        closePendingConnection(conn, "connection preparation exception");
+        retryOrBail();
+    }
+}
+
 /// called when a to-peer connection has been successfully obtained or
 /// when all candidate destinations have been tried and all have failed
 void
@@ -764,22 +794,31 @@
     Must(n_tries <= answer.n_tries); // n_tries cannot decrease
     n_tries = answer.n_tries;
 
-    if (const auto error = answer.error.get()) {
+    ErrorState *error = nullptr;
+    if ((error = answer.error.get())) {
         flags.dont_retry = true; // or HappyConnOpener would not have given up
         syncHierNote(answer.conn, request->url.host());
-        fail(error);
+        Must(!Comm::IsConnOpen(answer.conn));
         answer.error.clear(); // preserve error for errorSendComplete()
+    } else if (!Comm::IsConnOpen(answer.conn) || fd_table[answer.conn->fd].closing()) {
+        syncHierNote(answer.conn, request->url.host());
+        closePendingConnection(answer.conn, "conn was closed while waiting for noteConnection");
+        error = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request, al);
+    }
+
+    if (error) {
+        fail(error);
         retryOrBail(); // will notice flags.dont_retry and bail
         return;
     }
 
-    syncWithServerConn(answer.conn, request->url.host(), answer.reused);
-
-    if (answer.reused)
+    if (answer.reused) {
+        syncWithServerConn(answer.conn, request->url.host(), answer.reused);
         return dispatch();
+    }
 
     // Check if we need to TLS before use
-    if (const CachePeer *peer = serverConnection()->getPeer()) {
+    if (const auto *peer = answer.conn->getPeer()) {
         // Assume that it is only possible for the client-first from the
         // bumping modes to try connect to a remote server. The bumped
         // requests with other modes are using pinned connections or fails.
@@ -793,20 +832,27 @@
         if (originWantsEncryptedTraffic && // the "encrypted traffic" part
                 !peer->options.originserver && // the "through a proxy" part
                 !peer->secure.encryptTransport) // the "exclude HTTPS proxies" part
-            return establishTunnelThruProxy();
+            return advanceDestination("establish tunnel through proxy", answer.conn, [this,&answer] {
+                establishTunnelThruProxy(answer.conn);
+            });
     }
 
-    secureConnectionToPeerIfNeeded();
+    secureConnectionToPeerIfNeeded(answer.conn);
 }
 
 void
-FwdState::establishTunnelThruProxy()
+FwdState::establishTunnelThruProxy(const Comm::ConnectionPointer &conn)
 {
     AsyncCall::Pointer callback = asyncCall(17,4,
                                             "FwdState::tunnelEstablishmentDone",
                                             Http::Tunneler::CbDialer<FwdState>(&FwdState::tunnelEstablishmentDone, this));
     HttpRequest::Pointer requestPointer = request;
-    const auto tunneler = new Http::Tunneler(serverConnection(), requestPointer, callback, connectingTimeout(serverConnection()), al);
+    const auto tunneler = new Http::Tunneler(conn, requestPointer, callback, connectingTimeout(serverConnection()), al);
+
+    // TODO: Replace this hack with proper Comm::Connection-Pool association
+    // that is not tied to fwdPconnPool and can handle disappearing pools.
+    tunneler->noteFwdPconnUse = true;
+
 #if USE_DELAY_POOLS
     Must(serverConnection()->getPeer());
     if (!serverConnection()->getPeer()->options.no_delay)
@@ -820,11 +866,18 @@
 void
 FwdState::tunnelEstablishmentDone(Http::TunnelerAnswer &answer)
 {
-    if (answer.positive()) {
-        if (answer.leftovers.isEmpty()) {
-            secureConnectionToPeerIfNeeded();
-            return;
-        }
+    ErrorState *error = nullptr;
+    if (!answer.positive()) {
+        Must(!Comm::IsConnOpen(answer.conn));
+        error = answer.squidError.get();
+        Must(error);
+        answer.squidError.clear(); // preserve error for fail()
+    } else if (!Comm::IsConnOpen(answer.conn) || fd_table[answer.conn->fd].closing()) {
+        // The socket could get closed while our callback was queued.
+        // We close Connection here to sync Connection::fd.
+        closePendingConnection(answer.conn, "conn was closed while waiting for tunnelEstablishmentDone");
+        error = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request, al);
+    } else if (!answer.leftovers.isEmpty()) {
         // This should not happen because TLS servers do not speak first. If we
         // have to handle this, then pass answer.leftovers via a PeerConnector
         // to ServerBio. See ClientBio::setReadBufData().
@@ -832,33 +885,26 @@
         const auto level = (occurrences++ < 100) ? DBG_IMPORTANT : 2;
         debugs(17, level, "ERROR: Early data after CONNECT response. " <<
                "Found " << answer.leftovers.length() << " bytes. " <<
-               "Closing " << serverConnection());
-        fail(new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request, al));
-        closeServerConnection("found early data after CONNECT response");
+               "Closing " << answer.conn);
+        error = new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request, al);
+        closePendingConnection(answer.conn, "server spoke before tunnelEstablishmentDone");
+    }
+    if (error) {
+        fail(error);
         retryOrBail();
         return;
     }
 
-    // TODO: Reuse to-peer connections after a CONNECT error response.
-
-    if (const auto peer = serverConnection()->getPeer())
-        peerConnectFailed(peer);
-
-    const auto error = answer.squidError.get();
-    Must(error);
-    answer.squidError.clear(); // preserve error for fail()
-    fail(error);
-    closeServerConnection("Squid-generated CONNECT error");
-    retryOrBail();
+    secureConnectionToPeerIfNeeded(answer.conn);
 }
 
 /// handles an established TCP connection to peer (including origin servers)
 void
-FwdState::secureConnectionToPeerIfNeeded()
+FwdState::secureConnectionToPeerIfNeeded(const Comm::ConnectionPointer &conn)
 {
     assert(!request->flags.pinned);
 
-    const CachePeer *p = serverConnection()->getPeer();
+    const auto p = conn->getPeer();
     const bool peerWantsTls = p && p->secure.encryptTransport;
     // userWillTlsToPeerForUs assumes CONNECT == HTTPS
     const bool userWillTlsToPeerForUs = p && p->options.originserver &&
@@ -872,56 +918,70 @@
     const bool needTlsToOrigin = !p && request->url.getScheme() == AnyP::PROTO_HTTPS && !clientFirstBump;
 
     if (needTlsToPeer || needTlsToOrigin || needsBump) {
-        HttpRequest::Pointer requestPointer = request;
-        AsyncCall::Pointer callback = asyncCall(17,4,
-                                                "FwdState::ConnectedToPeer",
-                                                FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this));
-        const auto sslNegotiationTimeout = connectingTimeout(serverConnection());
-        Security::PeerConnector *connector = nullptr;
-#if USE_OPENSSL
-        if (request->flags.sslPeek)
-            connector = new Ssl::PeekingPeerConnector(requestPointer, serverConnection(), clientConn, callback, al, sslNegotiationTimeout);
-        else
-#endif
-            connector = new Security::BlindPeerConnector(requestPointer, serverConnection(), callback, al, sslNegotiationTimeout);
-        AsyncJob::Start(connector); // will call our callback
-        return;
+        return advanceDestination("secure connection to peer", conn, [this,&conn] {
+            secureConnectionToPeer(conn);
+        });
     }
 
     // if not encrypting just run the post-connect actions
-    successfullyConnectedToPeer();
+    successfullyConnectedToPeer(conn);
+}
+
+/// encrypts an established TCP connection to peer (including origin servers)
+void
+FwdState::secureConnectionToPeer(const Comm::ConnectionPointer &conn)
+{
+    HttpRequest::Pointer requestPointer = request;
+    AsyncCall::Pointer callback = asyncCall(17,4,
+                                            "FwdState::ConnectedToPeer",
+                                            FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this));
+    const auto sslNegotiationTimeout = connectingTimeout(conn);
+    Security::PeerConnector *connector = nullptr;
+#if USE_OPENSSL
+    if (request->flags.sslPeek)
+        connector = new Ssl::PeekingPeerConnector(requestPointer, conn, clientConn, callback, al, sslNegotiationTimeout);
+    else
+#endif
+        connector = new Security::BlindPeerConnector(requestPointer, conn, callback, al, sslNegotiationTimeout);
+    connector->noteFwdPconnUse = true;
+    AsyncJob::Start(connector); // will call our callback
 }
 
 /// called when all negotiations with the TLS-speaking peer have been completed
 void
 FwdState::connectedToPeer(Security::EncryptorAnswer &answer)
 {
-    if (ErrorState *error = answer.error.get()) {
-        fail(error);
+    ErrorState *error = nullptr;
+    if ((error = answer.error.get())) {
+        Must(!Comm::IsConnOpen(answer.conn));
         answer.error.clear(); // preserve error for errorSendComplete()
-        if (CachePeer *p = serverConnection()->getPeer())
-            peerConnectFailed(p);
-        serverConnection()->close();
-        return;
-    }
-
-    if (answer.tunneled) {
+    } else if (answer.tunneled) {
         // TODO: When ConnStateData establishes tunnels, its state changes
         // [in ways that may affect logging?]. Consider informing
         // ConnStateData about our tunnel or otherwise unifying tunnel
         // establishment [side effects].
-        unregister(serverConn); // async call owns it now
         complete(); // destroys us
         return;
+    } else if (!Comm::IsConnOpen(answer.conn) || fd_table[answer.conn->fd].closing()) {
+        closePendingConnection(answer.conn, "conn was closed while waiting for connectedToPeer");
+        error = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request, al);
     }
 
-    successfullyConnectedToPeer();
+    if (error) {
+        fail(error);
+        retryOrBail();
+        return;
+    }
+
+    successfullyConnectedToPeer(answer.conn);
 }
 
 /// called when all negotiations with the peer have been completed
 void
-FwdState::successfullyConnectedToPeer()
+FwdState::successfullyConnectedToPeer(const Comm::ConnectionPointer &conn)
 {
+    syncWithServerConn(conn, request->url.host(), false);
+
     // should reach ConnStateData before the dispatched Client job starts
     CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
                  ConnStateData::notePeerConnection, serverConnection());
@@ -1146,7 +1206,7 @@
             // Set the dont_retry flag because this is not a transient (network) error.
             flags.dont_retry = true;
             if (Comm::IsConnOpen(serverConn)) {
-                serverConn->close();
+                serverConn->close(); // trigger cleanup
             }
             break;
         }
@@ -1353,7 +1413,7 @@
 }
 
 void
-getOutgoingAddress(HttpRequest * request, Comm::ConnectionPointer conn)
+getOutgoingAddress(HttpRequest * request, const Comm::ConnectionPointer &conn)
 {
     // skip if an outgoing address is already set.
     if (!conn->local.isAnyAddr()) return;
diff -u -r -N squid-5.0.2/src/FwdState.h squid-5.0.3/src/FwdState.h
--- squid-5.0.2/src/FwdState.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/FwdState.h	2020-06-08 03:33:42.000000000 +1200
@@ -102,6 +102,9 @@
 
     void dontRetry(bool val) { flags.dont_retry = val; }
 
+    /// get rid of a to-server connection that failed to become serverConn
+    void closePendingConnection(const Comm::ConnectionPointer &conn, const char *reason);
+
     /** return a ConnectionPointer to the current server connection (may or may not be open) */
     Comm::ConnectionPointer const & serverConnection() const { return serverConn; };
 
@@ -131,14 +134,18 @@
     /// (in order to retry or reforward a failed request)
     bool pinnedCanRetry() const;
 
+    template <typename StepStart>
+    void advanceDestination(const char *stepDescription, const Comm::ConnectionPointer &conn, const StepStart &startStep);
+
     ErrorState *makeConnectingError(const err_type type) const;
     void connectedToPeer(Security::EncryptorAnswer &answer);
     static void RegisterWithCacheManager(void);
 
-    void establishTunnelThruProxy();
+    void establishTunnelThruProxy(const Comm::ConnectionPointer &);
     void tunnelEstablishmentDone(Http::TunnelerAnswer &answer);
-    void secureConnectionToPeerIfNeeded();
-    void successfullyConnectedToPeer();
+    void secureConnectionToPeerIfNeeded(const Comm::ConnectionPointer &);
+    void secureConnectionToPeer(const Comm::ConnectionPointer &);
+    void successfullyConnectedToPeer(const Comm::ConnectionPointer &);
 
     /// stops monitoring server connection for closure and updates pconn stats
     void closeServerConnection(const char *reason);
@@ -197,7 +204,7 @@
     PconnRace pconnRace; ///< current pconn race state
 };
 
-void getOutgoingAddress(HttpRequest * request, Comm::ConnectionPointer conn);
+void getOutgoingAddress(HttpRequest * request, const Comm::ConnectionPointer &conn);
 
 /// a collection of previously used persistent Squid-to-peer HTTP(S) connections
 extern PconnPool *fwdPconnPool;
diff -u -r -N squid-5.0.2/src/HappyConnOpener.cc squid-5.0.3/src/HappyConnOpener.cc
--- squid-5.0.2/src/HappyConnOpener.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/HappyConnOpener.cc	2020-06-08 03:33:42.000000000 +1200
@@ -397,15 +397,11 @@
     // TODO: Find an automated, faster way to kill no-longer-needed jobs.
 
     if (prime) {
-        if (prime.connector)
-            prime.connector->cancel("HappyConnOpener object destructed");
-        prime.clear();
+        cancelAttempt(prime, "job finished during a prime attempt");
     }
 
     if (spare) {
-        if (spare.connector)
-            spare.connector->cancel("HappyConnOpener object destructed");
-        spare.clear();
+        cancelAttempt(spare, "job finished during a spare attempt");
         if (gotSpareAllowance) {
             TheSpareAllowanceGiver.jobDroppedAllowance();
             gotSpareAllowance = false;
@@ -484,6 +480,15 @@
     callback_ = nullptr;
 }
 
+/// cancels the in-progress attempt, making its path a future candidate
+void
+HappyConnOpener::cancelAttempt(Attempt &attempt, const char *reason)
+{
+    Must(attempt);
+    destinations->retryPath(attempt.path); // before attempt.cancel() clears path
+    attempt.cancel(reason);
+}
+
 /// inform the initiator about our failure to connect (if needed)
 void
 HappyConnOpener::sendFailure()
@@ -563,6 +568,7 @@
 
     attempt.path = dest;
     attempt.connector = callConnect;
+    attempt.opener = cs;
 
     AsyncJob::Start(cs);
 }
@@ -578,9 +584,9 @@
     Must(itWasPrime != itWasSpare);
 
     if (itWasPrime) {
-        prime.clear();
+        prime.finish();
     } else {
-        spare.clear();
+        spare.finish();
         if (gotSpareAllowance) {
             TheSpareAllowanceGiver.jobUsedAllowance();
             gotSpareAllowance = false;
@@ -869,3 +875,13 @@
     return false;
 }
 
+void
+HappyConnOpener::Attempt::cancel(const char *reason)
+{
+    if (connector) {
+        connector->cancel(reason);
+        CallJobHere(17, 3, opener, Comm::ConnOpener, noteAbort);
+    }
+    clear();
+}
+
diff -u -r -N squid-5.0.2/src/HappyConnOpener.h squid-5.0.3/src/HappyConnOpener.h
--- squid-5.0.2/src/HappyConnOpener.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/HappyConnOpener.h	2020-06-08 03:33:42.000000000 +1200
@@ -157,10 +157,20 @@
     class Attempt {
     public:
         explicit operator bool() const { return static_cast<bool>(path); }
-        void clear() { path = nullptr; connector = nullptr; }
+
+        /// reacts to a natural attempt completion (successful or otherwise)
+        void finish() { clear(); }
+
+        /// aborts an in-progress attempt
+        void cancel(const char *reason);
 
         Comm::ConnectionPointer path; ///< the destination we are connecting to
-        AsyncCall::Pointer connector; ///< our Comm::ConnOpener callback
+        AsyncCall::Pointer connector; ///< our opener callback
+        Comm::ConnOpener::Pointer opener; ///< connects to path and calls us
+
+    private:
+        /// cleans up after the attempt ends (successfully or otherwise)
+        void clear() { path = nullptr; connector = nullptr; opener = nullptr; }
     };
 
     /* AsyncJob API */
@@ -194,6 +204,7 @@
     Answer *futureAnswer(const Comm::ConnectionPointer &);
     void sendSuccess(const Comm::ConnectionPointer &conn, bool reused, const char *connKind);
     void sendFailure();
+    void cancelAttempt(Attempt &, const char *reason);
 
     const time_t fwdStart; ///< requestor start time
 
diff -u -r -N squid-5.0.2/src/htcp.cc squid-5.0.3/src/htcp.cc
--- squid-5.0.2/src/htcp.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/htcp.cc	2020-06-08 03:33:42.000000000 +1200
@@ -145,7 +145,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
@@ -816,6 +816,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;
@@ -850,15 +851,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);
             }
@@ -1563,7 +1564,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;
@@ -1585,14 +1586,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);
@@ -1607,9 +1603,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-5.0.2/src/htcp.h squid-5.0.3/src/htcp.h
--- squid-5.0.2/src/htcp.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/htcp.h	2020-06-08 03:33:42.000000000 +1200
@@ -61,7 +61,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-5.0.2/src/http/ContentLengthInterpreter.cc squid-5.0.3/src/http/ContentLengthInterpreter.cc
--- squid-5.0.2/src/http/ContentLengthInterpreter.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/http/ContentLengthInterpreter.cc	2020-06-08 03:33:42.000000000 +1200
@@ -29,6 +29,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
@@ -53,10 +71,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;
@@ -69,7 +96,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-5.0.2/src/http/ContentLengthInterpreter.h squid-5.0.3/src/http/ContentLengthInterpreter.h
--- squid-5.0.2/src/http/ContentLengthInterpreter.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/http/ContentLengthInterpreter.h	2020-06-08 03:33:42.000000000 +1200
@@ -67,6 +67,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-5.0.2/src/http/url_rewriters/LFS/url_lfs_rewrite.8 squid-5.0.3/src/http/url_rewriters/LFS/url_lfs_rewrite.8
--- squid-5.0.2/src/http/url_rewriters/LFS/url_lfs_rewrite.8	2020-04-20 00:20:17.000000000 +1200
+++ squid-5.0.3/src/http/url_rewriters/LFS/url_lfs_rewrite.8	2020-06-09 19:09:22.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-5.0.2/src/http.cc squid-5.0.3/src/http.cc
--- squid-5.0.2/src/http.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/http.cc	2020-06-08 03:33:42.000000000 +1200
@@ -253,7 +253,7 @@
     if (pe != NULL) {
         assert(e != pe);
 #if USE_HTCP
-        neighborsHtcpClear(e, nullptr, e->mem_obj->request.getRaw(), e->mem_obj->method, HTCP_CLR_INVALIDATION);
+        neighborsHtcpClear(e, e->mem_obj->request.getRaw(), e->mem_obj->method, HTCP_CLR_INVALIDATION);
 #endif
         pe->release(true);
     }
@@ -270,7 +270,7 @@
     if (pe != NULL) {
         assert(e != pe);
 #if USE_HTCP
-        neighborsHtcpClear(e, nullptr, e->mem_obj->request.getRaw(), HttpRequestMethod(Http::METHOD_HEAD), HTCP_CLR_INVALIDATION);
+        neighborsHtcpClear(e, e->mem_obj->request.getRaw(), HttpRequestMethod(Http::METHOD_HEAD), HTCP_CLR_INVALIDATION);
 #endif
         pe->release(true);
     }
diff -u -r -N squid-5.0.2/src/ip/Intercept.cc squid-5.0.3/src/ip/Intercept.cc
--- squid-5.0.2/src/ip/Intercept.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ip/Intercept.cc	2020-06-08 03:33:42.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-5.0.2/src/ipc/Makefile.am squid-5.0.3/src/ipc/Makefile.am
--- squid-5.0.2/src/ipc/Makefile.am	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ipc/Makefile.am	2020-06-08 03:33:42.000000000 +1200
@@ -71,3 +71,6 @@
 
 install-data-local:
 	$(mkinstalldirs) $(DESTDIR)$(localstatedir)/run/squid;
+
+libipc_la_LIBADD = $(ATOMICLIB)
+
diff -u -r -N squid-5.0.2/src/ipc/Makefile.in squid-5.0.3/src/ipc/Makefile.in
--- squid-5.0.2/src/ipc/Makefile.in	2020-04-20 00:08:53.000000000 +1200
+++ squid-5.0.3/src/ipc/Makefile.in	2020-06-09 18:58:23.000000000 +1200
@@ -163,7 +163,8 @@
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 LTLIBRARIES = $(noinst_LTLIBRARIES)
-libipc_la_LIBADD =
+am__DEPENDENCIES_1 =
+libipc_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
 am__dirstamp = $(am__leading_dot)dirstamp
 am_libipc_la_OBJECTS = FdNotes.lo Kid.lo Kids.lo MemMap.lo Queue.lo \
 	ReadWriteLock.lo StartListening.lo StoreMap.lo StrandCoord.lo \
@@ -801,6 +802,7 @@
 	mem/Segment.h \
 	mem/forward.h
 
+libipc_la_LIBADD = $(ATOMICLIB)
 all: all-am
 
 .SUFFIXES:
diff -u -r -N squid-5.0.2/src/ipc/mem/PagePool.cc squid-5.0.3/src/ipc/mem/PagePool.cc
--- squid-5.0.2/src/ipc/mem/PagePool.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ipc/mem/PagePool.cc	2020-06-08 03:33:42.000000000 +1200
@@ -18,7 +18,12 @@
 Ipc::Mem::PagePool::Owner *
 Ipc::Mem::PagePool::Init(const char *const shmId, const Ipc::Mem::PoolId stackId, const unsigned int capacity, const size_t pageSize)
 {
-    return shm_new(PageStack)(shmId, stackId, capacity, pageSize);
+    PageStack::Config config;
+    config.poolId = stackId;
+    config.pageSize = pageSize; // the pages are stored in Ipc::Mem::Pages
+    config.capacity = capacity;
+    config.createFull = true; // all pages are initially available
+    return shm_new(PageStack)(shmId, config);
 }
 
 Ipc::Mem::PagePool::PagePool(const char *const id):
diff -u -r -N squid-5.0.2/src/ipc/mem/PageStack.cc squid-5.0.3/src/ipc/mem/PageStack.cc
--- squid-5.0.2/src/ipc/mem/PageStack.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ipc/mem/PageStack.cc	2020-06-08 03:33:42.000000000 +1200
@@ -10,54 +10,430 @@
 
 #include "squid.h"
 
-#include "base/TextException.h"
 #include "Debug.h"
 #include "ipc/mem/Page.h"
 #include "ipc/mem/PageStack.h"
 
-/* Ipc::Mem::PageStackStorageSlot */
+#include <cmath>
+#include <algorithm>
 
-static_assert(sizeof(Ipc::Mem::PageStackStorageSlot::Pointer) ==
-              sizeof(decltype(Ipc::Mem::PageId::number)), "page indexing types are consistent");
+/*
+
+Ipc::Mem::IdSet and related code maintains a perfect full binary tree structure:
+
+         (l,r)
+           /\
+    (ll,lr)  (rl,rr)
+       /\      /\
+      L1 L2   L3 L4
+
+where
+
+    * (l,r) is an always-present root node;
+    * inner nodes, including the root one, count the total number of available
+      IDs in the leaf nodes of the left and right subtrees (e.g., r = rl + rr);
+    * leaf nodes are bitsets of available IDs (e.g., rl = number of 1s in L3);
+      all leaf nodes are always present.
+
+The above sample tree would be stored as seven 64-bit atomic integers:
+    (l,r), (ll,lr), (rl,rr), L1, L2, L3, L4
+
+*/
+
+namespace Ipc
+{
+
+namespace Mem
+{
+
+/// the maximum number of pages that a leaf node can store
+static const IdSet::size_type BitsPerLeaf = 64;
+
+class IdSetPosition
+{
+public:
+    using size_type = IdSet::size_type;
+
+    IdSetPosition() = default; ///< root node position
+    IdSetPosition(size_type aLevel, size_type anOffset);
+
+    /// whether we are at the top of the tree
+    bool atRoot() const { return !level && !offset; }
+
+    /// which direction is this position from our parent node
+    IdSetNavigationDirection ascendDirection() const;
+
+    /// the number of levels above us (e.g., zero for the root node)
+    IdSet::size_type level = 0;
+    /// the number of nodes (at our level) to the left of us
+    IdSet::size_type offset = 0;
+};
+
+/// a helper class to perform inner node manipulation for IdSet
+class IdSetInnerNode
+{
+public:
+    using size_type = IdSet::size_type;
+    typedef uint64_t Packed; ///< (atomically) stored serialized value
+
+    /// de-serializes a given value
+    static IdSetInnerNode Unpack(Packed packed);
+
+    IdSetInnerNode() = default;
+    IdSetInnerNode(size_type left, size_type right);
+
+    /// returns a serializes value suitable for shared memory storage
+    Packed pack() const { return (static_cast<Packed>(left) << 32) | right; }
+
+    size_type left = 0; ///< the number of available IDs in the left subtree
+    size_type right = 0; ///< the number of available IDs in the right subtree
+};
+
+} // namespace Mem
+
+} // namespace Ipc
+
+/* Ipc::Mem::IdSetPosition */
+
+Ipc::Mem::IdSetPosition::IdSetPosition(size_type aLevel, size_type anOffset):
+    level(aLevel),
+    offset(anOffset)
+{
+}
+
+Ipc::Mem::IdSetNavigationDirection
+Ipc::Mem::IdSetPosition::ascendDirection() const
+{
+    return (offset % 2 == 0) ? dirLeft : dirRight;
+}
+
+/* Ipc::Mem::IdSetMeasurements */
+
+Ipc::Mem::IdSetMeasurements::IdSetMeasurements(const size_type aCapacity)
+{
+    capacity = aCapacity;
+
+    // For simplicity, we want a perfect full binary tree with root and leaves.
+    // We could compute all this with log2() calls, but rounding and honoring
+    // root+leaves minimums make that approach more complex than this fast loop.
+    requestedLeafNodeCount = (capacity + (BitsPerLeaf-1))/BitsPerLeaf;
+    treeHeight = 1+1; // the root level plus the leaf nodes level
+    leafNodeCount = 2; // the root node can have only two leaf nodes
+    while (leafNodeCount < requestedLeafNodeCount) {
+        leafNodeCount *= 2;
+        ++treeHeight;
+    }
+    innerLevelCount = treeHeight - 1;
+
+    debugs(54, 5, "rounded capacity up from " << capacity << " to " << (leafNodeCount*BitsPerLeaf));
+
+    // we do (1 << level) when computing 32-bit IdSetInnerNode::left
+    assert(treeHeight < 32);
+}
+
+/* Ipc::Mem::IdSetInnerNode */
+
+Ipc::Mem::IdSetInnerNode::IdSetInnerNode(size_type aLeft, size_type aRight):
+    left(aLeft),
+    right(aRight)
+{
+}
+
+Ipc::Mem::IdSetInnerNode
+Ipc::Mem::IdSetInnerNode::Unpack(Packed packed)
+{
+    // truncation during the cast is intentional here
+    return IdSetInnerNode(packed >> 32, static_cast<uint32_t>(packed));
+}
+
+/* Ipc::Mem::IdSet */
+
+Ipc::Mem::IdSet::IdSet(const size_type capacity):
+    measurements(capacity),
+    nodes_(capacity)
+{
+    // For valueAddress() to be able to return a raw uint64_t pointer, the
+    // atomic wrappers in nodes_ must be zero-size. Check the best we can. Once.
+    static_assert(sizeof(StoredNode) == sizeof(Node), "atomic locks use no storage");
+    assert(StoredNode().is_lock_free());
+}
 
 void
-Ipc::Mem::PageStackStorageSlot::take()
+Ipc::Mem::IdSet::makeFullBeforeSharing()
 {
-    const auto nxt = nextOrMarker.exchange(TakenPage);
-    assert(nxt != TakenPage);
+    // initially, all IDs are marked as available
+    fillAllNodes();
+
+    // ... but IDs beyond the requested capacity should not be available
+    if (measurements.capacity != measurements.leafNodeCount*BitsPerLeaf)
+        truncateExtras();
 }
 
+/// populates the entire allocated tree with available IDs
+/// may exceed the requested capacity; \see truncateExtras()
 void
-Ipc::Mem::PageStackStorageSlot::put(const PointerOrMarker expected, const Pointer nxt)
+Ipc::Mem::IdSet::fillAllNodes()
 {
-    assert(nxt != TakenPage);
-    const auto old = nextOrMarker.exchange(nxt);
-    assert(old == expected);
+    // leaf nodes
+    auto pos = Position(measurements.treeHeight-1, 0);
+    const auto allOnes = ~uint64_t(0);
+    std::fill_n(valueAddress(pos), measurements.leafNodeCount, allOnes);
+
+    // inner nodes, starting from the bottom of the tree
+    auto nodesAtLevel = measurements.leafNodeCount/2;
+    auto pagesBelow = BitsPerLeaf;
+    do {
+        pos = ascend(pos);
+        const auto value = IdSetInnerNode(pagesBelow, pagesBelow).pack();
+        std::fill_n(valueAddress(pos), nodesAtLevel, value);
+        nodesAtLevel /= 2;
+        pagesBelow *= 2;
+    } while (!pos.atRoot());
 }
 
-/* Ipc::Mem::PageStack */
+/// effectively removes IDs that exceed the requested capacity after makeFull()
+void
+Ipc::Mem::IdSet::truncateExtras()
+{
+    // leaf nodes
+    // start with the left-most leaf that should have some 0s; it may even have
+    // no 1s at all (i.e. be completely unused)
+    auto pos = Position(measurements.treeHeight-1, measurements.capacity/BitsPerLeaf);
+    leafTruncate(pos, measurements.capacity % BitsPerLeaf);
+    const auto rightLeaves = measurements.leafNodeCount - measurements.requestedLeafNodeCount;
+    // this zeroing of the leaf nodes to the right from pos is only necessary to
+    // trigger asserts if the code dealing with the inner node counters is buggy
+    if (rightLeaves > 1)
+        std::fill_n(valueAddress(pos) + 1, rightLeaves-1, 0);
+
+    // inner nodes, starting from the bottom of the tree; optimization: only
+    // adjusting nodes on the way up from the first leaf-with-0s position
+    auto toSubtract = BitsPerLeaf - (measurements.capacity % BitsPerLeaf);
+    do {
+        const auto direction = pos.ascendDirection();
+        pos = ascend(pos);
+        toSubtract = innerTruncate(pos, direction, toSubtract);
+    } while (!pos.atRoot());
+}
 
-Ipc::Mem::PageStack::PageStack(const PoolId aPoolId, const PageCount aCapacity, const size_t aPageSize):
-    thePoolId(aPoolId), capacity_(aCapacity), thePageSize(aPageSize),
-    size_(0),
-    head_(Slot::NilPtr),
-    slots_(aCapacity)
+/// fill the leaf node at a given position with 0s, leaving only idsToKeep IDs
+void
+Ipc::Mem::IdSet::leafTruncate(const Position pos, const size_type idsToKeep)
+{
+    Node &node = *valueAddress(pos); // no auto to simplify the asserts() below
+    assert(node == std::numeric_limits<Node>::max()); // all 1s
+    static_assert(std::is_unsigned<Node>::value, "right shift prepends 0s");
+    node >>= BitsPerLeaf - idsToKeep;
+    // node can be anything here, including all 0s and all 1s
+}
+
+/// accounts for toSubtract IDs removal from a subtree in the given direction of
+/// the given position
+/// \returns the number of IDs to subtract from the parent node
+Ipc::Mem::IdSet::size_type
+Ipc::Mem::IdSet::innerTruncate(const Position pos, const NavigationDirection dir, const size_type toSubtract)
+{
+    auto *valuePtr = valueAddress(pos);
+    auto value = IdSetInnerNode::Unpack(*valuePtr);
+    size_type toSubtractNext = 0;
+    if (dir == dirLeft) {
+        toSubtractNext = toSubtract + value.right;
+        assert(value.left >= toSubtract);
+        value.left -= toSubtract;
+        value.right = 0;
+    } else {
+        assert(dir == dirRight);
+        toSubtractNext = toSubtract;
+        assert(value.right >= toSubtract);
+        // value.left is unchanged; we have only adjusted the right branch
+        value.right -= toSubtract;
+    }
+    *valuePtr = value.pack();
+    return toSubtractNext;
+}
+
+/// accounts for an ID added to subtree in the given dir from the given position
+void
+Ipc::Mem::IdSet::innerPush(const Position pos, const NavigationDirection dir)
+{
+    // either left or right component will be true/1; the other will be false/0
+    const auto increment = IdSetInnerNode(dir == dirLeft, dir == dirRight).pack();
+    const auto previousValue = nodeAt(pos).fetch_add(increment);
+    // no overflows
+    assert(previousValue <= std::numeric_limits<Node>::max() - increment);
+}
+
+/// accounts for future ID removal from a subtree of the given position
+/// \returns the direction of the subtree chosen to relinquish the ID
+Ipc::Mem::IdSet::NavigationDirection
+Ipc::Mem::IdSet::innerPop(const Position pos)
+{
+    NavigationDirection direction = dirNone;
+
+    auto &node = nodeAt(pos);
+    auto oldValue = node.load();
+    IdSetInnerNode newValue;
+    do {
+        newValue = IdSetInnerNode::Unpack(oldValue);
+        if (newValue.left) {
+            --newValue.left;
+            direction = dirLeft;
+        } else if (newValue.right) {
+            --newValue.right;
+            direction = dirRight;
+        } else {
+            return dirEnd;
+        }
+    } while (!node.compare_exchange_weak(oldValue, newValue.pack()));
+
+    assert(direction == dirLeft || direction == dirRight);
+    return direction;
+}
+
+/// adds the given ID to the leaf node at the given position
+void
+Ipc::Mem::IdSet::leafPush(const Position pos, const size_type id)
+{
+    const auto mask = Node(1) << (id % BitsPerLeaf);
+    const auto oldValue = nodeAt(pos).fetch_or(mask);
+    // this was a new entry
+    assert((oldValue & mask) == 0);
+}
+
+// TODO: After switching to C++20, use countr_zero() which may compile to a
+// single TZCNT assembly instruction on modern CPUs.
+/// a temporary C++20 countr_zero() replacement
+static inline
+int trailingZeros(uint64_t x)
+{
+    if (!x)
+        return 64;
+    int count = 0;
+    for (uint64_t mask = 1; !(x & mask); mask <<= 1)
+        ++count;
+    return count;
+}
+
+/// extracts and returns an ID from the leaf node at the given position
+Ipc::Mem::IdSet::size_type
+Ipc::Mem::IdSet::leafPop(const Position pos)
+{
+    auto &node = nodeAt(pos);
+    auto oldValue = node.load();
+    Node newValue;
+    do {
+        assert(oldValue > 0);
+        const auto mask = oldValue - 1; // flips the rightmost 1 and trailing 0s
+        newValue = oldValue & mask; // clears the rightmost 1
+    } while (!node.compare_exchange_weak(oldValue, newValue));
+
+    return pos.offset*BitsPerLeaf + trailingZeros(oldValue);
+}
+
+/// \returns the position of a parent node of the node at the given position
+Ipc::Mem::IdSet::Position
+Ipc::Mem::IdSet::ascend(Position pos)
+{
+    assert(pos.level > 0);
+    --pos.level;
+    pos.offset /= 2;
+    return pos;
+}
+
+/// \returns the position of a child node in the given direction of the parent
+/// node at the given position
+Ipc::Mem::IdSet::Position
+Ipc::Mem::IdSet::descend(Position pos, const NavigationDirection direction)
 {
-    assert(thePoolId);
+    assert(pos.level < measurements.treeHeight);
+    ++pos.level;
 
-    assert(capacity_ < Slot::TakenPage);
-    assert(capacity_ < Slot::NilPtr);
+    pos.offset *= 2;
+    if (direction == dirRight)
+        ++pos.offset;
+    else
+        assert(direction == dirLeft);
 
-    // initially, all pages are free
-    if (capacity_) {
-        const auto lastIndex = capacity_-1;
-        // FlexibleArray cannot construct its phantom elements so, technically,
-        // all slots (except the very first one) are uninitialized until now.
-        for (Slot::Pointer i = 0; i < lastIndex; ++i)
-            (void)new(&slots_[i])Slot(i+1);
-        (void)new(&slots_[lastIndex])Slot(Slot::NilPtr);
-        size_ = capacity_;
-        head_ = 0;
+    return pos;
+}
+
+/// \returns the atomic node (either inner or leaf) at the given position
+Ipc::Mem::IdSet::StoredNode &
+Ipc::Mem::IdSet::nodeAt(const Position pos)
+{
+    assert(pos.level < measurements.treeHeight);
+    // n = 2^(h+1) - 1 with h = level-1
+    const auto nodesAbove = (1U << pos.level) - 1;
+
+    // the second clause is for the special case of a root node
+    assert(pos.offset < nodesAbove*2 || (pos.atRoot() && nodesAbove == 0));
+    const auto nodesToTheLeft = pos.offset;
+
+    const size_t nodesBefore = nodesAbove + nodesToTheLeft;
+    assert(nodesBefore < measurements.nodeCount());
+    return nodes_[nodesBefore];
+}
+
+/// \returns the location of the raw (inner or leaf) node at the given position
+Ipc::Mem::IdSet::Node *
+Ipc::Mem::IdSet::valueAddress(const Position pos)
+{
+    // IdSet() constructor asserts that this frequent reinterpret_cast is safe
+    return &reinterpret_cast<Node&>(nodeAt(pos));
+}
+
+bool
+Ipc::Mem::IdSet::pop(size_type &id)
+{
+    Position rootPos;
+    const auto directionFromRoot = innerPop(rootPos);
+    if (directionFromRoot == dirEnd)
+        return false; // an empty tree
+
+    auto pos = descend(rootPos, directionFromRoot);
+    for (size_t level = 1; level < measurements.innerLevelCount; ++level) {
+        const auto direction = innerPop(pos);
+        pos = descend(pos, direction);
+    }
+
+    id = leafPop(pos);
+    return true;
+}
+
+void
+Ipc::Mem::IdSet::push(const size_type id)
+{
+    const auto offsetAtLeafLevel = id/BitsPerLeaf;
+    auto pos = Position(measurements.innerLevelCount, offsetAtLeafLevel);
+    leafPush(pos, id);
+
+    do {
+        const auto direction = pos.ascendDirection();
+        pos = ascend(pos);
+        innerPush(pos, direction);
+    } while (!pos.atRoot());
+}
+
+size_t
+Ipc::Mem::IdSet::MemorySize(const size_type capacity)
+{
+    const IdSetMeasurements measurements(capacity);
+    // Adding sizeof(IdSet) double-counts the first node but it is better to
+    // overestimate (a little) than to underestimate our memory needs due to
+    // padding, new data members, etc.
+    return sizeof(IdSet) + measurements.nodeCount() * sizeof(StoredNode);
+}
+
+/* Ipc::Mem::PageStack */
+
+Ipc::Mem::PageStack::PageStack(const Config &config):
+    config_(config),
+    size_(0),
+    ids_(config_.capacity)
+{
+    if (config.createFull) {
+        ids_.makeFullBeforeSharing();
+        size_ = config_.capacity;
     }
 }
 
@@ -66,23 +442,21 @@
 {
     assert(!page);
 
-    Slot::Pointer current = head_.load();
+    if (!config_.capacity)
+        return false;
 
-    auto nextFree = Slot::NilPtr;
-    do {
-        if (current == Slot::NilPtr)
-            return false;
-        nextFree = slots_[current].next();
-    } while (!head_.compare_exchange_weak(current, nextFree));
+    IdSet::size_type pageIndex = 0;
+    if (!ids_.pop(pageIndex))
+        return false;
 
     // must decrement after removing the page to avoid underflow
     const auto newSize = --size_;
-    assert(newSize < capacity_);
+    assert(newSize < config_.capacity);
 
-    slots_[current].take();
-    page.number = current + 1;
-    page.pool = thePoolId;
+    page.number = pageIndex + 1;
+    page.pool = config_.poolId;
     debugs(54, 8, page << " size: " << newSize);
+    assert(pageIdIsValid(page));
     return true;
 }
 
@@ -93,19 +467,12 @@
     assert(page);
     assert(pageIdIsValid(page));
 
-    const auto pageIndex = page.number - 1;
-    auto &slot = slots_[pageIndex];
-
     // must increment before inserting the page to avoid underflow in pop()
     const auto newSize = ++size_;
-    assert(newSize <= capacity_);
+    assert(newSize <= config_.capacity);
 
-    auto current = head_.load();
-    auto expected = Slot::TakenPage;
-    do {
-        slot.put(expected, current);
-        expected = current;
-    } while (!head_.compare_exchange_weak(current, pageIndex));
+    const auto pageIndex = page.number - 1;
+    ids_.push(pageIndex);
 
     debugs(54, 8, page << " size: " << newSize);
     page = PageId();
@@ -114,34 +481,37 @@
 bool
 Ipc::Mem::PageStack::pageIdIsValid(const PageId &page) const
 {
-    return page.pool == thePoolId &&
+    return page.pool == config_.poolId &&
            0 < page.number && page.number <= capacity();
 }
 
 size_t
 Ipc::Mem::PageStack::sharedMemorySize() const
 {
-    return SharedMemorySize(thePoolId, capacity_, thePageSize);
+    return SharedMemorySize(config_);
 }
 
 size_t
-Ipc::Mem::PageStack::SharedMemorySize(const PoolId, const PageCount capacity, const size_t pageSize)
+Ipc::Mem::PageStack::SharedMemorySize(const Config &cfg)
 {
     const auto levelsSize = PageId::maxPurpose * sizeof(Levels_t);
-    const size_t pagesDataSize = capacity * pageSize;
-    return StackSize(capacity) + LevelsPaddingSize(capacity) + levelsSize + pagesDataSize;
+    const size_t pagesDataSize = cfg.capacity * cfg.pageSize;
+    return StackSize(cfg.capacity) + pagesDataSize + levelsSize;
 }
 
 size_t
 Ipc::Mem::PageStack::StackSize(const PageCount capacity)
 {
-    return sizeof(PageStack) + capacity * sizeof(Slot);
+    // Adding sizeof(PageStack) double-counts the fixed portion of the ids_ data
+    // member but it is better to overestimate (a little) than to underestimate
+    // our memory needs due to padding, new data members, etc.
+    return sizeof(PageStack) + IdSet::MemorySize(capacity);
 }
 
 size_t
 Ipc::Mem::PageStack::stackSize() const
 {
-    return StackSize(capacity_);
+    return StackSize(config_.capacity);
 }
 
 size_t
diff -u -r -N squid-5.0.2/src/ipc/mem/PageStack.h squid-5.0.3/src/ipc/mem/PageStack.h
--- squid-5.0.2/src/ipc/mem/PageStack.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ipc/mem/PageStack.h	2020-06-08 03:33:42.000000000 +1200
@@ -23,39 +23,83 @@
 
 class PageId;
 
-/// reflects the dual nature of PageStack storage:
-/// - for free pages, this is a pointer to the next free page
-/// - for used pages, this is a "used page" marker
-class PageStackStorageSlot
+class IdSetPosition;
+typedef enum { dirNone, dirLeft, dirRight, dirEnd } IdSetNavigationDirection;
+
+/// basic IdSet storage parameters, extracted here to keep them constant
+class IdSetMeasurements
+{
+public:
+    /// we need to fit two size_type counters into one 64-bit lockless atomic
+    typedef uint32_t size_type;
+
+    explicit IdSetMeasurements(size_type capacity);
+
+    /// the maximum number of pages our tree is allowed to store
+    size_type capacity = 0;
+
+    /// the number of leaf nodes that satisfy capacity requirements
+    size_type requestedLeafNodeCount = 0;
+
+    size_type treeHeight = 0; ///< total number of levels, including the leaf level
+    size_type leafNodeCount = 0; ///< the number of nodes at the leaf level
+    size_type innerLevelCount = 0; ///< all levels except the leaf level
+
+    /// the total number of nodes at all levels
+    size_type nodeCount() const { return leafNodeCount ? leafNodeCount*2 -1 : 0; }
+};
+
+/// a shareable set of positive uint32_t IDs with O(1) insertion/removal ops
+class IdSet
 {
 public:
-    // We are using uint32_t for Pointer because PageId::number is uint32_t.
-    // PageId::number should probably be uint64_t to accommodate caches with
-    // page numbers exceeding UINT32_MAX.
-    typedef uint32_t PointerOrMarker;
-    typedef PointerOrMarker Pointer;
-    typedef PointerOrMarker Marker;
-
-    /// represents a nil next slot pointer
-    static const Pointer NilPtr = std::numeric_limits<PointerOrMarker>::max();
-    /// marks a slot of a used (i.e. take()n) page
-    static const Marker TakenPage = std::numeric_limits<PointerOrMarker>::max() - 1;
-    static_assert(TakenPage != NilPtr, "magic PointerOrMarker values do not clash");
-
-    explicit PageStackStorageSlot(const Pointer nxt = NilPtr): nextOrMarker(nxt) {}
-
-    /// returns a (possibly nil) pointer to the next free page
-    Pointer next() const { return nextOrMarker.load(); }
-
-    /// marks our page as used
-    void take();
-
-    /// marks our page as free, to be used before the given `nxt` page;
-    /// also checks that the slot state matches the caller expectations
-    void put(const PointerOrMarker expected, const Pointer nxt);
+    using size_type = IdSetMeasurements::size_type;
+    using Position = IdSetPosition;
+    using NavigationDirection = IdSetNavigationDirection;
+
+    /// memory size required to store a tree with the given capacity
+    static size_t MemorySize(size_type capacity);
+
+    explicit IdSet(size_type capacity);
+
+    /// populates the allocated tree with the requested capacity IDs
+    /// optimized to run without atomic protection
+    void makeFullBeforeSharing();
+
+    /// finds/extracts (into the given `id`) an ID value and returns true
+    /// \retval false no IDs are left
+    bool pop(size_type &id);
+
+    /// makes `id` value available to future pop() callers
+    void push(size_type id);
+
+    const IdSetMeasurements measurements;
 
 private:
-    std::atomic<PointerOrMarker> nextOrMarker;
+    typedef uint64_t Node; ///< either leaf or intermediate node
+    typedef std::atomic<Node> StoredNode; ///< a Node stored in shared memory
+
+    /* optimization: these initialization methods bypass atomic protections */
+    void fillAllNodes();
+    void truncateExtras();
+    Node *valueAddress(Position);
+    size_type innerTruncate(Position pos, NavigationDirection dir, size_type toSubtract);
+    void leafTruncate(Position pos, size_type idsToKeep);
+
+    void innerPush(Position, NavigationDirection);
+    NavigationDirection innerPop(Position);
+
+    void leafPush(Position, size_type id);
+    size_type leafPop(Position);
+
+    Position ascend(Position);
+    Position descend(Position, NavigationDirection);
+
+    StoredNode &nodeAt(Position);
+
+    /// the entire binary tree flattened into an array
+    FlexibleArray<StoredNode> nodes_;
+    // No more data members should follow! See FlexibleArray<> for details.
 };
 
 /// Atomic container of "free" PageIds. Detects (some) double-free bugs.
@@ -72,10 +116,24 @@
     /// the number of (free and/or used) pages in a stack
     typedef unsigned int PageCount;
 
-    PageStack(const PoolId aPoolId, const PageCount aCapacity, const size_t aPageSize);
+    // XXX: poolId, pageSize look misplaced due to messy separation of PagePool
+    // (which should support multiple Segments but does not) and PageStack
+    // (which should not calculate the Segment size but does) duties.
+    /// PageStack construction and SharedMemorySize calculation parameters
+    class Config {
+    public:
+        uint32_t poolId = 0; ///< pool ID
+        size_t pageSize = 0; ///< page size, used to calculate shared memory size
+        PageCount capacity = 0; ///< the maximum number of pages
+
+        /// whether a newly created PageStack should be prefilled with PageIds
+        bool createFull = false;
+    };
+
+    explicit PageStack(const Config &);
 
-    PageCount capacity() const { return capacity_; }
-    size_t pageSize() const { return thePageSize; }
+    PageCount capacity() const { return config_.capacity; }
+    size_t pageSize() const { return config_.pageSize; }
     /// an approximate number of free pages
     PageCount size() const { return size_.load(); }
 
@@ -87,7 +145,7 @@
     bool pageIdIsValid(const PageId &page) const;
 
     /// total shared memory size required to share
-    static size_t SharedMemorySize(const PoolId aPoolId, const PageCount capacity, const size_t pageSize);
+    static size_t SharedMemorySize(const Config &);
     size_t sharedMemorySize() const;
 
     /// shared memory size required only by PageStack, excluding
@@ -97,7 +155,7 @@
 
     /// \returns the number of padding bytes to align PagePool::theLevels array
     static size_t LevelsPaddingSize(const PageCount capacity);
-    size_t levelsPaddingSize() const { return LevelsPaddingSize(capacity_); }
+    size_t levelsPaddingSize() const { return LevelsPaddingSize(config_.capacity); }
 
     /**
      * The following functions return PageStack IDs for the corresponding
@@ -113,23 +171,12 @@
     static PoolId IdForSwapDirSpace(const int dirIdx) { return 900 + dirIdx + 1; }
 
 private:
-    using Slot = PageStackStorageSlot;
-
-    // XXX: theFoo members look misplaced due to messy separation of PagePool
-    // (which should support multiple Segments but does not) and PageStack
-    // (which should not calculate the Segment size but does) duties.
-    const PoolId thePoolId; ///< pool ID
-    const PageCount capacity_; ///< the maximum number of pages
-    const size_t thePageSize; ///< page size, used to calculate shared memory size
+    const Config config_;
     /// a lower bound for the number of free pages (for debugging purposes)
     std::atomic<PageCount> size_;
 
-    /// the index of the first free stack element or nil
-    std::atomic<Slot::Pointer> head_;
-
-    /// slots indexed using their page number
-    Ipc::Mem::FlexibleArray<Slot> slots_;
-    // No more data members should follow! See Ipc::Mem::FlexibleArray<> for details.
+    IdSet ids_; ///< free pages (valid with positive capacity_)
+    // No more data members should follow! See IdSet for details.
 };
 
 } // namespace Mem
diff -u -r -N squid-5.0.2/src/log/DB/log_db_daemon.8 squid-5.0.3/src/log/DB/log_db_daemon.8
--- squid-5.0.2/src/log/DB/log_db_daemon.8	2020-04-20 00:20:18.000000000 +1200
+++ squid-5.0.3/src/log/DB/log_db_daemon.8	2020-06-09 19:09:22.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-5.0.2/src/Makefile.am squid-5.0.3/src/Makefile.am
--- squid-5.0.2/src/Makefile.am	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/Makefile.am	2020-06-08 03:33:42.000000000 +1200
@@ -576,7 +576,6 @@
 	$(top_builddir)/lib/libmisccontainers.la \
 	$(top_builddir)/lib/libmiscencoding.la \
 	$(top_builddir)/lib/libmiscutil.la \
-	$(ATOMICLIB) \
 	$(SSLLIB) \
 	$(EPOLL_LIBS) \
 	$(MINGW_LIBS) \
diff -u -r -N squid-5.0.2/src/Makefile.in squid-5.0.3/src/Makefile.in
--- squid-5.0.2/src/Makefile.in	2020-04-20 00:08:51.000000000 +1200
+++ squid-5.0.3/src/Makefile.in	2020-06-09 18:58:21.000000000 +1200
@@ -454,8 +454,8 @@
 	$(top_builddir)/lib/libmiscutil.la $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
-	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_3) \
-	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_6)
+	$(am__DEPENDENCIES_3) $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_6)
 squid_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
 	$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
 	$(CXXFLAGS) $(squid_LDFLAGS) $(LDFLAGS) -o $@
@@ -3179,9 +3179,9 @@
 	$(ESI_LIBS) $(SNMP_LIBS) mem/libmem.la store/libstore.la \
 	$(top_builddir)/lib/libmisccontainers.la \
 	$(top_builddir)/lib/libmiscencoding.la \
-	$(top_builddir)/lib/libmiscutil.la $(ATOMICLIB) $(SSLLIB) \
-	$(EPOLL_LIBS) $(MINGW_LIBS) $(KRB5LIBS) $(SYSTEMD_LIBS) \
-	$(COMPAT_LIB) $(XTRA_LIBS) $(am__append_9)
+	$(top_builddir)/lib/libmiscutil.la $(SSLLIB) $(EPOLL_LIBS) \
+	$(MINGW_LIBS) $(KRB5LIBS) $(SYSTEMD_LIBS) $(COMPAT_LIB) \
+	$(XTRA_LIBS) $(am__append_9)
 @ENABLE_LOADABLE_MODULES_TRUE@squid_LDFLAGS = -export-dynamic -dlopen force
 unlinkd_SOURCES = unlinkd_daemon.cc
 unlinkd_LDADD = \
diff -u -r -N squid-5.0.2/src/mem/PoolingAllocator.h squid-5.0.3/src/mem/PoolingAllocator.h
--- squid-5.0.2/src/mem/PoolingAllocator.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/mem/PoolingAllocator.h	2020-06-08 03:33:42.000000000 +1200
@@ -22,6 +22,23 @@
     template <class Other> PoolingAllocator(const PoolingAllocator<Other> &) noexcept {}
     value_type *allocate(std::size_t n) { return static_cast<value_type*>(memAllocRigid(n*sizeof(value_type))); }
     void deallocate(value_type *vp, std::size_t n) noexcept { memFreeRigid(vp, n*sizeof(value_type)); }
+
+    // The following declarations are only necessary for compilers that do not
+    // fully support C++11 Allocator-related APIs, such as GCC v4.8.
+    // TODO: Remove after dropping support for such compilers.
+
+    using size_type = size_t;
+    using pointer = Value*;
+    using const_pointer = const Value*;
+    using reference = Value&;
+    using const_reference = const Value&;
+
+    template <class OtherValue>
+    struct rebind {
+      typedef PoolingAllocator<OtherValue> other;
+    };
+
+    template<typename OtherValue> void destroy(OtherValue *p) { p->~OtherValue(); }
 };
 
 template <class L, class R>
diff -u -r -N squid-5.0.2/src/MemStore.cc squid-5.0.3/src/MemStore.cc
--- squid-5.0.2/src/MemStore.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/MemStore.cc	2020-06-08 03:33:42.000000000 +1200
@@ -1008,10 +1008,14 @@
 
     const int64_t entryLimit = MemStore::EntryLimit();
     assert(entryLimit > 0);
+
+    Ipc::Mem::PageStack::Config spaceConfig;
+    spaceConfig.poolId = Ipc::Mem::PageStack::IdForMemStoreSpace(),
+    spaceConfig.pageSize = 0; // the pages are stored in Ipc::Mem::Pages
+    spaceConfig.capacity = entryLimit;
+    spaceConfig.createFull = true; // all pages are initially available
     Must(!spaceOwner);
-    spaceOwner = shm_new(Ipc::Mem::PageStack)(SpaceLabel,
-                 Ipc::Mem::PageStack::IdForMemStoreSpace(),
-                 entryLimit, 0);
+    spaceOwner = shm_new(Ipc::Mem::PageStack)(SpaceLabel, spaceConfig);
     Must(!mapOwner);
     mapOwner = MemStoreMap::Init(MapLabel, entryLimit);
     Must(!extrasOwner);
diff -u -r -N squid-5.0.2/src/neighbors.cc squid-5.0.3/src/neighbors.cc
--- squid-5.0.2/src/neighbors.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/neighbors.cc	2020-06-08 03:33:42.000000000 +1200
@@ -63,6 +63,8 @@
 static void peerCountMcastPeersDone(void *data);
 static void peerCountMcastPeersStart(void *data);
 static void peerCountMcastPeersSchedule(CachePeer * p, time_t when);
+static void peerCountMcastPeersAbort(PeerSelector *);
+static void peerCountMcastPeersCreateAndSend(CachePeer *p);
 static IRCB peerCountHandleIcpReply;
 
 static void neighborIgnoreNonPeer(const Ip::Address &, icp_opcode);
@@ -1393,9 +1395,19 @@
 static void
 peerCountMcastPeersStart(void *data)
 {
+    const auto peer = static_cast<CachePeer*>(data);
+    CallContextCreator([peer] {
+        peerCountMcastPeersCreateAndSend(peer);
+    });
+    peerCountMcastPeersSchedule(peer, MCAST_COUNT_RATE);
+}
+
+/// initiates an ICP transaction to a multicast peer
+static void
+peerCountMcastPeersCreateAndSend(CachePeer * const p)
+{
     // XXX: Do not create lots of complex fake objects (while abusing their
     // APIs) to pass around a few basic data points like start_ping and ping!
-    CachePeer *p = (CachePeer *)data;
     MemObject *mem;
     int reqnum;
     // TODO: use class AnyP::Uri instead of constructing and re-parsing a string
@@ -1408,6 +1420,9 @@
     const MasterXaction::Pointer mx = new MasterXaction(XactionInitiator::initPeerMcast);
     auto *req = HttpRequest::FromUrlXXX(url, mx);
     assert(req != nullptr);
+    const AccessLogEntry::Pointer ale = new AccessLogEntry;
+    ale->request = req;
+    CodeContext::Reset(ale);
     StoreEntry *fake = storeCreateEntry(url, url, RequestFlags(), Http::METHOD_GET);
     const auto psstate = new PeerSelector(nullptr);
     psstate->request = req;
@@ -1415,6 +1430,7 @@
     psstate->entry = fake;
     psstate->peerCountMcastPeerXXX = cbdataReference(p);
     psstate->ping.start = current_time;
+    psstate->al = ale;
     mem = fake->mem_obj;
     mem->request = psstate->request;
     mem->start_ping = current_time;
@@ -1431,13 +1447,23 @@
              psstate,
              Config.Timeout.mcast_icp_query / 1000.0, 1);
     p->mcast.flags.counting = true;
-    peerCountMcastPeersSchedule(p, MCAST_COUNT_RATE);
 }
 
 static void
 peerCountMcastPeersDone(void *data)
 {
     const auto psstate = static_cast<PeerSelector*>(data);
+    CallBack(psstate->al, [psstate] {
+        peerCountMcastPeersAbort(psstate);
+        delete psstate;
+    });
+}
+
+/// ends counting of multicast ICP replies
+/// to the ICP query initiated by peerCountMcastPeersCreateAndSend()
+static void
+peerCountMcastPeersAbort(PeerSelector * const psstate)
+{
     StoreEntry *fake = psstate->entry;
 
     if (cbdataReferenceValid(psstate->peerCountMcastPeerXXX)) {
@@ -1455,7 +1481,6 @@
     fake->abort(); // sets ENTRY_ABORTED and initiates releated cleanup
     fake->mem_obj->request = nullptr;
     fake->unlock("peerCountMcastPeersDone");
-    delete psstate;
 }
 
 static void
@@ -1786,7 +1811,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];
@@ -1802,7 +1827,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-5.0.2/src/neighbors.h squid-5.0.3/src/neighbors.h
--- squid-5.0.2/src/neighbors.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/neighbors.h	2020-06-08 03:33:42.000000000 +1200
@@ -40,7 +40,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-5.0.2/src/ResolvedPeers.cc squid-5.0.3/src/ResolvedPeers.cc
--- squid-5.0.2/src/ResolvedPeers.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ResolvedPeers.cc	2020-06-08 03:33:42.000000000 +1200
@@ -24,59 +24,117 @@
 {
     debugs(17, 4, path);
     assert(path);
-    paths_.emplace(paths_.begin(), path);
+    // Cannot use pathsToSkip for a faster (reverse) search because there
+    // may be unavailable paths past pathsToSkip. We could remember
+    // the last extraction index, but, to completely avoid a linear search,
+    // extract*() methods should return the path index.
+    const auto found = std::find_if(paths_.begin(), paths_.end(),
+    [path](const ResolvedPeerPath &candidate) {
+        return candidate.connection == path; // (refcounted) pointer comparison
+    });
+    assert(found != paths_.end());
+    assert(!found->available);
+    found->available = true;
+    increaseAvailability();
+
+    // if we restored availability of a path that we used to skip, update
+    const auto pathsToTheLeft = static_cast<size_type>(found - paths_.begin());
+    if (pathsToTheLeft < pathsToSkip) {
+        pathsToSkip = pathsToTheLeft;
+    } else {
+        // *found was unavailable so pathsToSkip could not end at it
+        Must(pathsToTheLeft != pathsToSkip);
+    }
 }
 
 void
 ResolvedPeers::addPath(const Comm::ConnectionPointer &path)
 {
     paths_.emplace_back(path);
+    Must(paths_.back().available); // no pathsToSkip updates are needed
+    increaseAvailability();
+}
+
+/// \returns the beginning iterator for any available-path search
+ResolvedPeers::Paths::iterator
+ResolvedPeers::start()
+{
+    Must(pathsToSkip <= paths_.size());
+    return paths_.begin() + pathsToSkip; // may return end()
+}
+
+/// finalizes the iterator part of the given preliminary find*() result
+ResolvedPeers::Finding
+ResolvedPeers::makeFinding(const Paths::iterator &path, bool foundOther)
+{
+    return std::make_pair((foundOther ? paths_.end() : path), foundOther);
+}
+
+/// \returns the first available same-peer same-family Finding or <end,...>
+ResolvedPeers::Finding
+ResolvedPeers::findPrime(const Comm::Connection &currentPeer)
+{
+    const auto path = start();
+    const auto foundNextOrSpare = path != paths_.end() &&
+        (currentPeer.getPeer() != path->connection->getPeer() || // next peer
+            ConnectionFamily(currentPeer) != ConnectionFamily(*path->connection));
+    return makeFinding(path, foundNextOrSpare);
+}
+
+/// \returns the first available same-peer different-family Finding or <end,...>
+ResolvedPeers::Finding
+ResolvedPeers::findSpare(const Comm::Connection &currentPeer)
+{
+    const auto primeFamily = ConnectionFamily(currentPeer);
+    const auto primePeer = currentPeer.getPeer();
+    const auto path = std::find_if(start(), paths_.end(),
+    [primeFamily, primePeer](const ResolvedPeerPath &candidate) {
+        if (!candidate.available)
+            return false;
+        if (primePeer != candidate.connection->getPeer())
+            return true; // found next peer
+        if (primeFamily != ConnectionFamily(*candidate.connection))
+            return true; // found spare
+        return false;
+    });
+    const auto foundNext = path != paths_.end() &&
+        primePeer != path->connection->getPeer();
+    return makeFinding(path, foundNext);
+}
+
+/// \returns the first available same-peer Finding or <end,...>
+ResolvedPeers::Finding
+ResolvedPeers::findPeer(const Comm::Connection &currentPeer)
+{
+    const auto path = start();
+    const auto foundNext = path != paths_.end() &&
+        currentPeer.getPeer() != path->connection->getPeer();
+    return makeFinding(path, foundNext);
 }
 
 Comm::ConnectionPointer
 ResolvedPeers::extractFront()
 {
     Must(!empty());
-    return extractFound("first: ", paths_.begin());
+    return extractFound("first: ", start());
 }
 
 Comm::ConnectionPointer
 ResolvedPeers::extractPrime(const Comm::Connection &currentPeer)
 {
-    if (!empty()) {
-        const auto peerToMatch = currentPeer.getPeer();
-        const auto familyToMatch = ConnectionFamily(currentPeer);
-        const auto &conn = paths_.front();
-        if (conn->getPeer() == peerToMatch && familyToMatch == ConnectionFamily(*conn))
-            return extractFound("same-peer same-family match: ", paths_.begin());
-    }
+    const auto found = findPrime(currentPeer).first;
+    if (found != paths_.end())
+        return extractFound("same-peer same-family match: ", found);
 
     debugs(17, 7, "no same-peer same-family paths");
     return nullptr;
 }
 
-/// If spare paths exist for currentPeer, returns the first spare path iterator.
-/// Otherwise, if there are paths for other peers, returns one of those.
-/// Otherwise, returns the end() iterator.
-Comm::ConnectionList::iterator
-ResolvedPeers::findSpareOrNextPeer(const Comm::Connection &currentPeer)
-{
-    const auto peerToMatch = currentPeer.getPeer();
-    const auto familyToAvoid = ConnectionFamily(currentPeer);
-    // Optimization: Also stop at the first mismatching peer because all
-    // same-peer paths are grouped together.
-    return std::find_if(paths_.begin(), paths_.end(),
-    [peerToMatch, familyToAvoid](const Comm::ConnectionPointer &conn) {
-        return peerToMatch != conn->getPeer() ||
-               familyToAvoid != ConnectionFamily(*conn);
-    });
-}
-
 Comm::ConnectionPointer
 ResolvedPeers::extractSpare(const Comm::Connection &currentPeer)
 {
-    auto found = findSpareOrNextPeer(currentPeer);
-    if (found != paths_.end() && currentPeer.getPeer() == (*found)->getPeer())
+    const auto found = findSpare(currentPeer).first;
+    if (found != paths_.end())
         return extractFound("same-peer different-family match: ", found);
 
     debugs(17, 7, "no same-peer different-family paths");
@@ -85,48 +143,58 @@
 
 /// convenience method to finish a successful extract*() call
 Comm::ConnectionPointer
-ResolvedPeers::extractFound(const char *description, const Comm::ConnectionList::iterator &found)
+ResolvedPeers::extractFound(const char *description, const Paths::iterator &found)
 {
-    const auto path = *found;
-    paths_.erase(found);
-    debugs(17, 7, description << path);
-    return path;
+    auto &path = *found;
+    debugs(17, 7, description << path.connection);
+    assert(path.available);
+    path.available = false;
+    decreaseAvailability();
+
+    // if we extracted the left-most available path, find the next one
+    if (static_cast<size_type>(found - paths_.begin()) == pathsToSkip) {
+        while (++pathsToSkip < paths_.size() && !paths_[pathsToSkip].available) {}
+    }
+
+    return path.connection;
 }
 
 bool
 ResolvedPeers::haveSpare(const Comm::Connection &currentPeer)
 {
-    const auto found = findSpareOrNextPeer(currentPeer);
-    return found != paths_.end() &&
-           currentPeer.getPeer() == (*found)->getPeer();
+    return findSpare(currentPeer).first != paths_.end();
+}
+
+/// whether paths_ have no (and will have no) Xs for the current peer based on
+/// the given findX(current peer) result
+bool
+ResolvedPeers::doneWith(const Finding &findings) const
+{
+    if (findings.first != paths_.end())
+        return false; // not done because the caller found a viable path X
+
+    // The caller did not find any path X. If the caller found any "other"
+    // paths, then we are done with paths X. If there are no "other" paths,
+    // then destinationsFinalized is the answer.
+    return findings.second ? true : destinationsFinalized;
 }
 
 bool
 ResolvedPeers::doneWithSpares(const Comm::Connection &currentPeer)
 {
-    const auto found = findSpareOrNextPeer(currentPeer);
-    if (found == paths_.end())
-        return destinationsFinalized;
-    return currentPeer.getPeer() != (*found)->getPeer();
+    return doneWith(findSpare(currentPeer));
 }
 
 bool
-ResolvedPeers::doneWithPrimes(const Comm::Connection &currentPeer) const
+ResolvedPeers::doneWithPrimes(const Comm::Connection &currentPeer)
 {
-    const auto first = paths_.begin();
-    if (first == paths_.end())
-        return destinationsFinalized;
-    return currentPeer.getPeer() != (*first)->getPeer() ||
-           ConnectionFamily(currentPeer) != ConnectionFamily(**first);
+    return doneWith(findPrime(currentPeer));
 }
 
 bool
-ResolvedPeers::doneWithPeer(const Comm::Connection &currentPeer) const
+ResolvedPeers::doneWithPeer(const Comm::Connection &currentPeer)
 {
-    const auto first = paths_.begin();
-    if (first == paths_.end())
-        return destinationsFinalized;
-    return currentPeer.getPeer() != (*first)->getPeer();
+    return doneWith(findPeer(currentPeer));
 }
 
 int
@@ -135,6 +203,22 @@
     return conn.remote.isIPv4() ? AF_INET : AF_INET6;
 }
 
+/// increments the number of available paths
+void
+ResolvedPeers::increaseAvailability()
+{
+    ++availablePaths;
+    assert(availablePaths <= paths_.size());
+}
+
+/// decrements the number of available paths
+void
+ResolvedPeers::decreaseAvailability()
+{
+    assert(availablePaths > 0);
+    --availablePaths;
+}
+
 std::ostream &
 operator <<(std::ostream &os, const ResolvedPeers &peers)
 {
diff -u -r -N squid-5.0.2/src/ResolvedPeers.h squid-5.0.3/src/ResolvedPeers.h
--- squid-5.0.2/src/ResolvedPeers.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ResolvedPeers.h	2020-06-08 03:33:42.000000000 +1200
@@ -13,6 +13,16 @@
 #include "comm/forward.h"
 
 #include <iosfwd>
+#include <utility>
+
+class ResolvedPeerPath
+{
+public:
+    explicit ResolvedPeerPath(const Comm::ConnectionPointer &conn) : connection(conn), available(true) {}
+
+    Comm::ConnectionPointer connection; ///< (the address of) a path
+    bool available; ///< whether this path may be used (i.e., has not been tried already)
+};
 
 /// cache_peer and origin server addresses (a.k.a. paths)
 /// selected and resolved by the peering code
@@ -21,17 +31,20 @@
     MEMPROXY_CLASS(ResolvedPeers);
 
 public:
+    // ResolvedPeerPaths in addPath() call order
+    typedef std::vector<ResolvedPeerPath> Paths;
+    using size_type = Paths::size_type;
     typedef RefCount<ResolvedPeers> Pointer;
 
     ResolvedPeers();
 
     /// whether we lack any known candidate paths
-    bool empty() const { return paths_.empty(); }
+    bool empty() const { return !availablePaths; }
 
     /// add a candidate path to try after all the existing paths
     void addPath(const Comm::ConnectionPointer &);
 
-    /// add a candidate path to try before all the existing paths
+    /// re-inserts the previously extracted address into the same position
     void retryPath(const Comm::ConnectionPointer &);
 
     /// extracts and returns the first queued address
@@ -49,16 +62,16 @@
     bool haveSpare(const Comm::Connection &currentPeer);
 
     /// whether extractPrime() returns and will continue to return nil
-    bool doneWithPrimes(const Comm::Connection &currentPeer) const;
+    bool doneWithPrimes(const Comm::Connection &currentPeer);
 
     /// whether extractSpare() returns and will continue to return nil
     bool doneWithSpares(const Comm::Connection &currentPeer);
 
     /// whether doneWithPrimes() and doneWithSpares() are true for currentPeer
-    bool doneWithPeer(const Comm::Connection &currentPeer) const;
+    bool doneWithPeer(const Comm::Connection &currentPeer);
 
     /// the current number of candidate paths
-    Comm::ConnectionList::size_type size() const { return paths_.size(); }
+    size_type size() const { return availablePaths; }
 
     /// whether all of the available candidate paths received from DNS
     bool destinationsFinalized = false;
@@ -67,13 +80,34 @@
     bool notificationPending = false;
 
 private:
+    /// A find*() result: An iterator of the found path (or paths_.end()) and
+    /// whether the "other" path was found instead.
+    typedef std::pair<Paths::iterator, bool> Finding;
+
     /// The protocol family of the given path, AF_INET or AF_INET6
     static int ConnectionFamily(const Comm::Connection &conn);
 
-    Comm::ConnectionList::iterator findSpareOrNextPeer(const Comm::Connection &currentPeer);
-    Comm::ConnectionPointer extractFound(const char *description, const Comm::ConnectionList::iterator &found);
+    Paths::iterator start();
+    Finding findSpare(const Comm::Connection &currentPeer);
+    Finding findPrime(const Comm::Connection &currentPeer);
+    Finding findPeer(const Comm::Connection &currentPeer);
+    Comm::ConnectionPointer extractFound(const char *description, const Paths::iterator &found);
+    Finding makeFinding(const Paths::iterator &found, bool foundOther);
+
+    bool doneWith(const Finding &findings) const;
+
+    void increaseAvailability();
+    void decreaseAvailability();
+
+    Paths paths_; ///< resolved addresses in (peer, family) order
+
+    /// the number of leading paths_ elements that are all currently unavailable
+    /// i.e. the size of the front paths_ segment comprised of unavailable items
+    /// i.e. the position of the first available path (or paths_.size())
+    size_type pathsToSkip = 0;
 
-    Comm::ConnectionList paths_;
+    /// the total number of currently available elements in paths_
+    size_type availablePaths = 0;
 };
 
 /// summarized ResolvedPeers (for debugging)
diff -u -r -N squid-5.0.2/src/security/BlindPeerConnector.cc squid-5.0.3/src/security/BlindPeerConnector.cc
--- squid-5.0.2/src/security/BlindPeerConnector.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/security/BlindPeerConnector.cc	2020-06-08 03:33:42.000000000 +1200
@@ -7,6 +7,7 @@
  */
 
 #include "squid.h"
+#include "AccessLogEntry.h"
 #include "CachePeer.h"
 #include "comm/Connection.h"
 #include "errorpage.h"
diff -u -r -N squid-5.0.2/src/security/cert_validators/fake/security_fake_certverify.8 squid-5.0.3/src/security/cert_validators/fake/security_fake_certverify.8
--- squid-5.0.2/src/security/cert_validators/fake/security_fake_certverify.8	2020-04-20 00:20:18.000000000 +1200
+++ squid-5.0.3/src/security/cert_validators/fake/security_fake_certverify.8	2020-06-09 19:09:23.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-5.0.2/src/security/Handshake.cc squid-5.0.3/src/security/Handshake.cc
--- squid-5.0.2/src/security/Handshake.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/security/Handshake.cc	2020-06-08 03:33:42.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-5.0.2/src/security/Handshake.h squid-5.0.3/src/security/Handshake.h
--- squid-5.0.2/src/security/Handshake.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/security/Handshake.h	2020-06-08 03:33:42.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-5.0.2/src/security/NegotiationHistory.cc squid-5.0.3/src/security/NegotiationHistory.cc
--- squid-5.0.2/src/security/NegotiationHistory.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/security/NegotiationHistory.cc	2020-06-08 03:33:42.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-5.0.2/src/security/PeerConnector.cc squid-5.0.3/src/security/PeerConnector.cc
--- squid-5.0.2/src/security/PeerConnector.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/security/PeerConnector.cc	2020-06-08 03:33:42.000000000 +1200
@@ -15,8 +15,11 @@
 #include "Downloader.h"
 #include "errorpage.h"
 #include "fde.h"
+#include "FwdState.h"
 #include "http/Stream.h"
 #include "HttpRequest.h"
+#include "neighbors.h"
+#include "pconn.h"
 #include "security/NegotiationHistory.h"
 #include "security/PeerConnector.h"
 #include "SquidConfig.h"
@@ -31,6 +34,7 @@
 
 Security::PeerConnector::PeerConnector(const Comm::ConnectionPointer &aServerConn, AsyncCall::Pointer &aCallback, const AccessLogEntryPointer &alp, const time_t timeout) :
     AsyncJob("Security::PeerConnector"),
+    noteFwdPconnUse(false),
     serverConn(aServerConn),
     al(alp),
     callback(aCallback),
@@ -39,14 +43,17 @@
     useCertValidator_(true),
     certsDownloads(0)
 {
-    debugs(83, 5, "Security::PeerConnector constructed, this=" << (void*)this);
+    debugs(83, 5, serverConn);
+
     // if this throws, the caller's cb dialer is not our CbDialer
     Must(dynamic_cast<CbDialer*>(callback->getDialer()));
-}
 
-Security::PeerConnector::~PeerConnector()
-{
-    debugs(83, 5, "Security::PeerConnector destructed, this=" << (void*)this);
+    // watch for external connection closures
+    Must(Comm::IsConnOpen(serverConn));
+    Must(!fd_table[serverConn->fd].closing());
+    typedef CommCbMemFunT<Security::PeerConnector, CommCloseCbParams> Dialer;
+    closeHandler = JobCallback(9, 5, Dialer, this, Security::PeerConnector::commCloseHandler);
+    comm_add_close_handler(serverConn->fd, closeHandler);
 }
 
 bool Security::PeerConnector::doneAll() const
@@ -61,8 +68,16 @@
     AsyncJob::start();
     debugs(83, 5, "this=" << (void*)this);
 
+    // we own this Comm::Connection object and its fd exclusively, but must bail
+    // if others started closing the socket while we were waiting to start()
+    assert(Comm::IsConnOpen(serverConn));
+    if (fd_table[serverConn->fd].closing()) {
+        bail(new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw(), al));
+        return;
+    }
+
     Security::SessionPointer tmp;
-    if (prepareSocket() && initialize(tmp))
+    if (initialize(tmp))
         negotiate();
     else
         mustStop("Security::PeerConnector TLS socket initialize failed");
@@ -71,34 +86,25 @@
 void
 Security::PeerConnector::commCloseHandler(const CommCloseCbParams &params)
 {
+    closeHandler = nullptr;
+
     debugs(83, 5, "FD " << params.fd << ", Security::PeerConnector=" << params.data);
-    connectionClosed("Security::PeerConnector::commCloseHandler");
+    const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw(), al);
+#if USE_OPENSSL
+    err->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, nullptr, nullptr);
+#endif
+    bail(err);
 }
 
 void
-Security::PeerConnector::connectionClosed(const char *reason)
+Security::PeerConnector::commTimeoutHandler(const CommTimeoutCbParams &)
 {
-    debugs(83, 5, reason << " socket closed/closing. this=" << (void*)this);
-    mustStop(reason);
-    callback = NULL;
-}
-
-bool
-Security::PeerConnector::prepareSocket()
-{
-    debugs(83, 5, serverConnection() << ", this=" << (void*)this);
-    if (!Comm::IsConnOpen(serverConnection()) || fd_table[serverConnection()->fd].closing()) {
-        connectionClosed("Security::PeerConnector::prepareSocket");
-        return false;
-    }
-
-    debugs(83, 5, serverConnection());
-
-    // watch for external connection closures
-    typedef CommCbMemFunT<Security::PeerConnector, CommCloseCbParams> Dialer;
-    closeHandler = JobCallback(9, 5, Dialer, this, Security::PeerConnector::commCloseHandler);
-    comm_add_close_handler(serverConnection()->fd, closeHandler);
-    return true;
+    debugs(83, 5, serverConnection() << " timedout. this=" << (void*)this);
+    const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scGatewayTimeout, request.getRaw(), al);
+#if USE_OPENSSL
+    err->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, nullptr, nullptr);
+#endif
+    bail(err);
 }
 
 bool
@@ -163,9 +169,6 @@
 void
 Security::PeerConnector::negotiate()
 {
-    if (!Comm::IsConnOpen(serverConnection()))
-        return;
-
     const int fd = serverConnection()->fd;
     if (fd_table[fd].closing())
         return;
@@ -204,7 +207,7 @@
     if (!sslFinalized())
         return;
 
-    callBack();
+    sendSuccess();
 }
 
 bool
@@ -241,7 +244,6 @@
 
             noteNegotiationDone(anErr);
             bail(anErr);
-            serverConn->close();
             return true;
         }
     }
@@ -259,9 +261,6 @@
 
     Ssl::ErrorDetail *errDetails = NULL;
     bool validatorFailed = false;
-    if (!Comm::IsConnOpen(serverConnection())) {
-        return;
-    }
 
     if (Debug::Enabled(83, 5)) {
         Security::SessionPointer ssl(fd_table[serverConnection()->fd].ssl);
@@ -281,7 +280,7 @@
 
     if (!errDetails && !validatorFailed) {
         noteNegotiationDone(NULL);
-        callBack();
+        sendSuccess();
         return;
     }
 
@@ -296,7 +295,6 @@
 
     noteNegotiationDone(anErr);
     bail(anErr);
-    serverConn->close();
     return;
 }
 #endif
@@ -473,9 +471,11 @@
 #endif
 
     // read timeout to avoid getting stuck while reading from a silent server
-    AsyncCall::Pointer nil;
+    typedef CommCbMemFunT<Security::PeerConnector, CommTimeoutCbParams> TimeoutDialer;
+    AsyncCall::Pointer timeoutCall = JobCallback(83, 5,
+                                     TimeoutDialer, this, Security::PeerConnector::commTimeoutHandler);
     const auto timeout = Comm::MortalReadTimeout(startTime, negotiationTimeout);
-    commSetConnTimeout(serverConnection(), timeout, nil);
+    commSetConnTimeout(serverConnection(), timeout, timeoutCall);
 
     Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, new Pointer(this), 0);
 }
@@ -545,13 +545,34 @@
     Must(dialer);
     dialer->answer().error = error;
 
+    if (const auto p = serverConnection()->getPeer())
+        peerConnectFailed(p);
+
+    callBack();
+    disconnect();
+
+    if (noteFwdPconnUse)
+        fwdPconnPool->noteUses(fd_table[serverConn->fd].pconn.uses);
+    serverConn->close();
+    serverConn = nullptr;
+}
+
+void
+Security::PeerConnector::sendSuccess()
+{
     callBack();
-    // Our job is done. The callabck recepient will probably close the failed
-    // peer connection and try another peer or go direct (if possible). We
-    // can close the connection ourselves (our error notification would reach
-    // the recepient before the fd-closure notification), but we would rather
-    // minimize the number of fd-closure notifications and let the recepient
-    // manage the TCP state of the connection.
+    disconnect();
+}
+
+void
+Security::PeerConnector::disconnect()
+{
+    if (closeHandler) {
+        comm_remove_close_handler(serverConnection()->fd, closeHandler);
+        closeHandler = nullptr;
+    }
+
+    commUnsetConnTimeout(serverConnection());
 }
 
 void
@@ -563,10 +584,6 @@
     // Do this now so that if we throw below, swanSong() assert that we _tried_
     // to call back holds.
     callback = NULL; // this should make done() true
-
-    // remove close handler
-    comm_remove_close_handler(serverConnection()->fd, closeHandler);
-
     CbDialer *dialer = dynamic_cast<CbDialer*>(cb->getDialer());
     Must(dialer);
     dialer->answer().conn = serverConnection();
diff -u -r -N squid-5.0.2/src/security/PeerConnector.h squid-5.0.3/src/security/PeerConnector.h
--- squid-5.0.2/src/security/PeerConnector.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/security/PeerConnector.h	2020-06-08 03:33:42.000000000 +1200
@@ -31,34 +31,12 @@
 {
 
 /**
- * Initiates encryption on a connection to peers or servers.
- * Despite its name does not perform any connect(2) operations.
+ * Initiates encryption of a given open TCP connection to a peer or server.
+ * Despite its name does not perform any connect(2) operations. Owns the
+ * connection during TLS negotiations. The caller receives EncryptorAnswer.
  *
  * Contains common code and interfaces of various specialized PeerConnector's,
  * including peer certificate validation code.
- \par
- * The caller receives a call back with Security::EncryptorAnswer. If answer.error
- * is not nil, then there was an error and the encryption to the peer or server
- * was not fully established. The error object is suitable for error response
- * generation.
- \par
- * The caller must monitor the connection for closure because this
- * job will not inform the caller about such events.
- \par
- * PeerConnector class currently supports a form of TLS negotiation timeout,
- * which is accounted only when sets the read timeout from encrypted peers/servers.
- * For a complete solution, the caller must monitor the overall connection
- * establishment timeout and close the connection on timeouts. This is probably
- * better than having dedicated (or none at all!) timeouts for peer selection,
- * DNS lookup, TCP handshake, SSL handshake, etc. Some steps may have their
- * own timeout, but not all steps should be forced to have theirs.
- * XXX: tunnel.cc and probably other subsystems do not have an "overall
- * connection establishment" timeout. We need to change their code so that they
- * start monitoring earlier and close on timeouts. This change may need to be
- * discussed on squid-dev.
- \par
- * This job never closes the connection, even on errors. If a 3rd-party
- * closes the connection, this job simply quits without informing the caller.
  */
 class PeerConnector: virtual public AsyncJob
 {
@@ -81,7 +59,10 @@
                   AsyncCall::Pointer &aCallback,
                   const AccessLogEntryPointer &alp,
                   const time_t timeout = 0);
-    virtual ~PeerConnector();
+    virtual ~PeerConnector() = default;
+
+    /// hack: whether the connection requires fwdPconnPool->noteUses()
+    bool noteFwdPconnUse;
 
 protected:
     // AsyncJob API
@@ -90,17 +71,12 @@
     virtual void swanSong();
     virtual const char *status() const;
 
+    /// The connection read timeout callback handler.
+    void commTimeoutHandler(const CommTimeoutCbParams &);
+
     /// The comm_close callback handler.
     void commCloseHandler(const CommCloseCbParams &params);
 
-    /// Inform us that the connection is closed. Does the required clean-up.
-    void connectionClosed(const char *reason);
-
-    /// Sets up TCP socket-related notification callbacks if things go wrong.
-    /// If socket already closed return false, else install the comm_close
-    /// handler to monitor the socket.
-    bool prepareSocket();
-
     /// \returns true on successful TLS session initialization
     virtual bool initialize(Security::SessionPointer &);
 
@@ -160,12 +136,18 @@
     /// mimics FwdState to minimize changes to FwdState::initiate/negotiateSsl
     Comm::ConnectionPointer const &serverConnection() const { return serverConn; }
 
-    void bail(ErrorState *error); ///< Return an error to the PeerConnector caller
+    /// sends the given error to the initiator
+    void bail(ErrorState *error);
+
+    /// sends the encrypted connection to the initiator
+    void sendSuccess();
 
-    /// Callback the caller class, and pass the ready to communicate secure
-    /// connection or an error if PeerConnector failed.
+    /// a bail(), sendSuccess() helper: sends results to the initiator
     void callBack();
 
+    /// a bail(), sendSuccess() helper: stops monitoring the connection
+    void disconnect();
+
     /// If called the certificates validator will not used
     void bypassCertValidator() {useCertValidator_ = false;}
 
diff -u -r -N squid-5.0.2/src/ssl/bio.cc squid-5.0.3/src/ssl/bio.cc
--- squid-5.0.2/src/ssl/bio.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ssl/bio.cc	2020-06-08 03:33:42.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-5.0.2/src/ssl/bio.h squid-5.0.3/src/ssl/bio.h
--- squid-5.0.2/src/ssl/bio.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ssl/bio.h	2020-06-08 03:33:42.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-5.0.2/src/ssl/ErrorDetail.cc squid-5.0.3/src/ssl/ErrorDetail.cc
--- squid-5.0.2/src/ssl/ErrorDetail.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ssl/ErrorDetail.cc	2020-06-08 03:33:42.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-5.0.2/src/ssl/ErrorDetail.h squid-5.0.3/src/ssl/ErrorDetail.h
--- squid-5.0.2/src/ssl/ErrorDetail.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ssl/ErrorDetail.h	2020-06-08 03:33:42.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-5.0.2/src/ssl/PeekingPeerConnector.cc squid-5.0.3/src/ssl/PeekingPeerConnector.cc
--- squid-5.0.2/src/ssl/PeekingPeerConnector.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ssl/PeekingPeerConnector.cc	2020-06-08 03:33:42.000000000 +1200
@@ -92,8 +92,9 @@
     al->ssl.bumpMode = finalAction;
 
     if (finalAction == Ssl::bumpTerminate) {
-        serverConn->close();
+        bail(new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scForbidden, request.getRaw(), al));
         clientConn->close();
+        clientConn = nullptr;
     } else if (finalAction != Ssl::bumpSplice) {
         //Allow write, proceed with the connection
         srvBio->holdWrite(false);
@@ -140,11 +141,13 @@
     if (!Security::PeerConnector::initialize(serverSession))
         return false;
 
+    // client connection supplies TLS client details and is also used if we
+    // need to splice or terminate the client and server connections
+    if (!Comm::IsConnOpen(clientConn))
+        return false;
+
     if (ConnStateData *csd = request->clientConnectionManager.valid()) {
 
-        // client connection is required in the case we need to splice
-        // or terminate client and server connections
-        assert(clientConn != NULL);
         SBuf *hostName = NULL;
 
         //Enable Status_request TLS extension, required to bump some clients
@@ -245,6 +248,10 @@
     if (!error) {
         serverCertificateVerified();
         if (splice) {
+            if (!Comm::IsConnOpen(clientConn)) {
+                bail(new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al));
+                throw TextException("from-client connection gone", Here());
+            }
             switchToTunnel(request.getRaw(), clientConn, serverConn);
             tunnelInsteadOfNegotiating();
         }
@@ -276,18 +283,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-5.0.2/src/ssl/PeekingPeerConnector.h squid-5.0.3/src/ssl/PeekingPeerConnector.h
--- squid-5.0.2/src/ssl/PeekingPeerConnector.h	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/ssl/PeekingPeerConnector.h	2020-06-08 03:33:42.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-5.0.2/src/store/id_rewriters/file/storeid_file_rewrite.8 squid-5.0.3/src/store/id_rewriters/file/storeid_file_rewrite.8
--- squid-5.0.2/src/store/id_rewriters/file/storeid_file_rewrite.8	2020-04-20 00:20:16.000000000 +1200
+++ squid-5.0.3/src/store/id_rewriters/file/storeid_file_rewrite.8	2020-06-09 19:09:21.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-5.0.2/src/tests/stub_libanyp.cc squid-5.0.3/src/tests/stub_libanyp.cc
--- squid-5.0.2/src/tests/stub_libanyp.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/tests/stub_libanyp.cc	2020-06-08 03:33:42.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 *, enum MatchDomainNameFlags) 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-5.0.2/src/tests/stub_libsecurity.cc squid-5.0.3/src/tests/stub_libsecurity.cc
--- squid-5.0.2/src/tests/stub_libsecurity.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/tests/stub_libsecurity.cc	2020-06-08 03:33:42.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"
@@ -50,14 +50,12 @@
 {
 PeerConnector::PeerConnector(const Comm::ConnectionPointer &, AsyncCall::Pointer &, const AccessLogEntryPointer &, const time_t) :
     AsyncJob("Security::PeerConnector") {STUB}
-PeerConnector::~PeerConnector() {STUB}
 void PeerConnector::start() STUB
 bool PeerConnector::doneAll() const STUB_RETVAL(true)
 void PeerConnector::swanSong() STUB
 const char *PeerConnector::status() const STUB_RETVAL("")
 void PeerConnector::commCloseHandler(const CommCloseCbParams &) STUB
-void PeerConnector::connectionClosed(const char *) STUB
-bool PeerConnector::prepareSocket() STUB_RETVAL(false)
+void PeerConnector::commTimeoutHandler(const CommTimeoutCbParams &) STUB
 bool PeerConnector::initialize(Security::SessionPointer &) STUB_RETVAL(false)
 void PeerConnector::negotiate() STUB
 bool PeerConnector::sslFinalized() STUB_RETVAL(false)
@@ -67,7 +65,9 @@
 void PeerConnector::noteNegotiationError(const int, const int, const int) STUB
 //    virtual Security::ContextPointer getTlsContext() = 0;
 void PeerConnector::bail(ErrorState *) STUB
+void PeerConnector::sendSuccess() STUB
 void PeerConnector::callBack() STUB
+void PeerConnector::disconnect() STUB
 void PeerConnector::recordNegotiationDetails() STUB
 }
 
diff -u -r -N squid-5.0.2/src/tests/stub_StatHist.cc squid-5.0.3/src/tests/stub_StatHist.cc
--- squid-5.0.2/src/tests/stub_StatHist.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/tests/stub_StatHist.cc	2020-06-08 03:33:42.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_NOP
diff -u -r -N squid-5.0.2/src/tunnel.cc squid-5.0.3/src/tunnel.cc
--- squid-5.0.2/src/tunnel.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/tunnel.cc	2020-06-08 03:33:42.000000000 +1200
@@ -111,9 +111,12 @@
     /// starts connecting to the next hop, either for the first time or while
     /// recovering from the previous connect failure
     void startConnecting();
+    void closePendingConnection(const Comm::ConnectionPointer &conn, const char *reason);
+
+    void retryOrBail();
 
     /// called when negotiations with the peer have been successfully completed
-    void notePeerReadyToShovel();
+    void notePeerReadyToShovel(const Comm::ConnectionPointer &);
 
     class Connection
     {
@@ -179,7 +182,8 @@
     void copyRead(Connection &from, IOCB *completion);
 
     /// continue to set up connection to a peer, going async for SSL peers
-    void connectToPeer();
+    void connectToPeer(const Comm::ConnectionPointer &);
+    void secureConnectionToPeer(const Comm::ConnectionPointer &);
 
     /* PeerSelectionInitiator API */
     virtual void noteDestination(Comm::ConnectionPointer conn) override;
@@ -233,8 +237,15 @@
 
     void usePinned();
 
-    /// callback handler after connection setup (including any encryption)
-    void connectedToPeer(Security::EncryptorAnswer &answer);
+    /// callback handler for the Security::PeerConnector encryptor
+    void noteSecurityPeerConnectorAnswer(Security::EncryptorAnswer &);
+
+    /// called after connection setup (including any encryption)
+    void connectedToPeer(const Comm::ConnectionPointer &);
+    void establishTunnelThruProxy(const Comm::ConnectionPointer &);
+
+    template <typename StepStart>
+    void advanceDestination(const char *stepDescription, const Comm::ConnectionPointer &conn, const StepStart &startStep);
 
     /// details of the "last tunneling attempt" failure (if it failed)
     ErrorState *savedError = nullptr;
@@ -680,6 +691,15 @@
 }
 
 void
+TunnelStateData::closePendingConnection(const Comm::ConnectionPointer &conn, const char *reason)
+{
+    debugs(26, 3, "because " << reason << "; " << conn);
+    assert(!server.conn);
+    if (IsConnOpen(conn))
+        conn->close();
+}
+
+void
 TunnelStateData::Connection::closeIfOpen()
 {
     if (Comm::IsConnOpen(conn))
@@ -776,6 +796,11 @@
 tunnelStartShoveling(TunnelStateData *tunnelState)
 {
     assert(!tunnelState->waitingForConnectExchange);
+    assert(tunnelState->server.conn);
+    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
+                                     CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
+    commSetConnTimeout(tunnelState->server.conn, Config.Timeout.read, timeoutCall);
+
     *tunnelState->status_ptr = Http::scOkay;
     if (tunnelState->logTag_ptr)
         tunnelState->logTag_ptr->update(LOG_TCP_TUNNEL);
@@ -838,35 +863,51 @@
 
     waitingForConnectExchange = false;
 
-    if (answer.positive()) {
+    auto sawProblem = false;
+
+    if (!answer.positive()) {
+        sawProblem = true;
+        Must(!Comm::IsConnOpen(answer.conn));
+    } else if (!Comm::IsConnOpen(answer.conn) || fd_table[answer.conn->fd].closing()) {
+        sawProblem = true;
+        closePendingConnection(answer.conn, "conn was closed while waiting for tunnelEstablishmentDone");
+    }
+
+    if (!sawProblem) {
+        assert(answer.positive()); // paranoid
         // copy any post-200 OK bytes to our buffer
         preReadServerData = answer.leftovers;
-        notePeerReadyToShovel();
+        notePeerReadyToShovel(answer.conn);
         return;
     }
 
-    // TODO: Reuse to-peer connections after a CONNECT error response.
-
-    // TODO: We can and, hence, should close now, but tunnelServerClosed()
-    // cannot yet tell whether ErrorState is still writing an error response.
-    // server.closeIfOpen();
-
     if (!clientExpectsConnectResponse()) {
         // closing the non-HTTP client connection is the best we can do
-        debugs(50, 3, server.conn << " closing on CONNECT-to-peer error");
-        server.closeIfOpen();
+        debugs(50, 3, client.conn << " closing on CONNECT-to-peer error");
+        client.closeIfOpen();
         return;
     }
 
-    ErrorState *error = answer.squidError.get();
-    Must(error);
-    answer.squidError.clear(); // preserve error for errorSendComplete()
-    sendError(error, "tunneler returns error");
+    ErrorState *error = nullptr;
+    if (answer.positive()) {
+        error = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw(), al);
+    } else {
+        error = answer.squidError.get();
+        Must(error);
+        answer.squidError.clear(); // preserve error for errorSendComplete()
+    }
+    assert(error);
+    saveError(error);
+    retryOrBail();
 }
 
 void
-TunnelStateData::notePeerReadyToShovel()
+TunnelStateData::notePeerReadyToShovel(const Comm::ConnectionPointer &conn)
 {
+    server.conn = conn;
+    // Start monitoring for server-side connection problems
+    comm_add_close_handler(server.conn->fd, tunnelServerClosed, this);
+
     if (!clientExpectsConnectResponse())
         tunnelStartShoveling(this); // ssl-bumped connection, be quiet
     else {
@@ -896,17 +937,42 @@
         tunnelState->server.conn->close();
 }
 
+/// reacts to the current destination failure
+void
+TunnelStateData::retryOrBail()
+{
+    // Since no TCP payload has been passed to client or server, we may
+    // TCP-connect to other destinations (including alternate IPs).
+
+    if (!FwdState::EnoughTimeToReForward(startTime))
+        return sendError(savedError, "forwarding timeout");
+
+    if (!destinations->empty())
+        return startConnecting();
+
+    if (!PeerSelectionInitiator::subscribed)
+        return sendError(savedError, "tried all destinations");
+}
+
 void
 TunnelStateData::noteConnection(HappyConnOpener::Answer &answer)
 {
     calls.connector = nullptr;
     connOpener.clear();
 
-    if (const auto error = answer.error.get()) {
+    ErrorState *error = nullptr;
+    if ((error = answer.error.get())) {
+        Must(!Comm::IsConnOpen(answer.conn));
         syncHierNote(answer.conn, request->url.host());
+        answer.error.clear();
+    } else if (!Comm::IsConnOpen(answer.conn) || fd_table[answer.conn->fd].closing()) {
+        error = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw(), al);
+        closePendingConnection(answer.conn, "conn was closed while waiting for  noteConnection");
+    }
+
+    if (error) {
         saveError(error);
-        answer.error.clear(); // savedError has it now
-        sendError(savedError, "tried all destinations");
+        retryOrBail();
         return;
     }
 
@@ -917,17 +983,12 @@
 TunnelStateData::connectDone(const Comm::ConnectionPointer &conn, const char *origin, const bool reused)
 {
     Must(Comm::IsConnOpen(conn));
-    server.conn = conn;
 
     if (reused)
         ResetMarkingsToServer(request.getRaw(), *conn);
     // else Comm::ConnOpener already applied proper/current markings
 
-    syncHierNote(server.conn, request->url.host());
-
-    request->hier.resetPeerNotes(conn, origin);
-    if (al)
-        al->hier.resetPeerNotes(conn, origin);
+    syncHierNote(conn, origin);
 
 #if USE_DELAY_POOLS
     /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
@@ -938,7 +999,6 @@
     netdbPingSite(request->url.host());
 
     request->peer_host = conn->getPeer() ? conn->getPeer()->host : nullptr;
-    comm_add_close_handler(conn->fd, tunnelServerClosed, this);
 
     bool toOrigin = false; // same semantics as StateFlags::toOrigin
     if (const auto * const peer = conn->getPeer()) {
@@ -950,14 +1010,10 @@
     }
 
     if (!toOrigin)
-        connectToPeer();
+        connectToPeer(conn);
     else {
-        notePeerReadyToShovel();
+        notePeerReadyToShovel(conn);
     }
-
-    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
-                                     CommTimeoutCbPtrFun(tunnelTimeout, this));
-    commSetConnTimeout(conn, Config.Timeout.read, timeoutCall);
 }
 
 void
@@ -1007,38 +1063,85 @@
 }
 
 void
-TunnelStateData::connectToPeer()
+TunnelStateData::connectToPeer(const Comm::ConnectionPointer &conn)
 {
-    if (CachePeer *p = server.conn->getPeer()) {
-        if (p->secure.encryptTransport) {
-            AsyncCall::Pointer callback = asyncCall(5,4,
-                                                    "TunnelStateData::ConnectedToPeer",
-                                                    MyAnswerDialer(&TunnelStateData::connectedToPeer, this));
-            auto *connector = new Security::BlindPeerConnector(request, server.conn, callback, al);
-            AsyncJob::Start(connector); // will call our callback
-            return;
-        }
+    if (const auto p = conn->getPeer()) {
+        if (p->secure.encryptTransport)
+            return advanceDestination("secure connection to peer", conn, [this,&conn] {
+                secureConnectionToPeer(conn);
+            });
     }
 
-    Security::EncryptorAnswer nil;
-    connectedToPeer(nil);
+    connectedToPeer(conn);
 }
 
+/// encrypts an established TCP connection to peer
 void
-TunnelStateData::connectedToPeer(Security::EncryptorAnswer &answer)
+TunnelStateData::secureConnectionToPeer(const Comm::ConnectionPointer &conn)
+{
+    AsyncCall::Pointer callback = asyncCall(5,4, "TunnelStateData::noteSecurityPeerConnectorAnswer",
+                                            MyAnswerDialer(&TunnelStateData::noteSecurityPeerConnectorAnswer, this));
+    const auto connector = new Security::BlindPeerConnector(request, conn, callback, al);
+    AsyncJob::Start(connector); // will call our callback
+}
+
+/// starts a preparation step for an established connection; retries on failures
+template <typename StepStart>
+void
+TunnelStateData::advanceDestination(const char *stepDescription, const Comm::ConnectionPointer &conn, const StepStart &startStep)
+{
+    // TODO: Extract destination-specific handling from TunnelStateData so that
+    // all the awkward, limited-scope advanceDestination() calls can be replaced
+    // with a single simple try/catch,retry block.
+    try {
+        startStep();
+        // now wait for the step callback
+    } catch (...) {
+        debugs (26, 2, "exception while trying to " << stepDescription << ": " << CurrentException);
+        closePendingConnection(conn, "connection preparation exception");
+        if (!savedError)
+            saveError(new ErrorState(ERR_CANNOT_FORWARD, Http::scInternalServerError, request.getRaw(), al));
+        retryOrBail();
+    }
+}
+
+/// callback handler for the connection encryptor
+void
+TunnelStateData::noteSecurityPeerConnectorAnswer(Security::EncryptorAnswer &answer)
 {
     if (ErrorState *error = answer.error.get()) {
+        Must(!Comm::IsConnOpen(answer.conn));
         answer.error.clear(); // sendError() will own the error
         sendError(error, "TLS peer connection error");
         return;
     }
 
+    if (!Comm::IsConnOpen(answer.conn) || fd_table[answer.conn->fd].closing()) {
+        sendError(new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw(), al), "connecion gone");
+        closePendingConnection(answer.conn, "conn was closed while waiting for noteSecurityPeerConnectorAnswer");
+        return;
+    }
+
+    connectedToPeer(answer.conn);
+}
+
+void
+TunnelStateData::connectedToPeer(const Comm::ConnectionPointer &conn)
+{
+    advanceDestination("establish tunnel through proxy", conn, [this,&conn] {
+        establishTunnelThruProxy(conn);
+    });
+}
+
+void
+TunnelStateData::establishTunnelThruProxy(const Comm::ConnectionPointer &conn)
+{
     assert(!waitingForConnectExchange);
 
     AsyncCall::Pointer callback = asyncCall(5,4,
                                             "TunnelStateData::tunnelEstablishmentDone",
                                             Http::Tunneler::CbDialer<TunnelStateData>(&TunnelStateData::tunnelEstablishmentDone, this));
-    const auto tunneler = new Http::Tunneler(server.conn, request, callback, Config.Timeout.lifetime, al);
+    const auto tunneler = new Http::Tunneler(conn, request, callback, Config.Timeout.lifetime, al);
 #if USE_DELAY_POOLS
     tunneler->setDelayId(server.delayId);
 #endif
@@ -1240,6 +1343,9 @@
 void
 switchToTunnel(HttpRequest *request, Comm::ConnectionPointer &clientConn, Comm::ConnectionPointer &srvConn)
 {
+    Must(Comm::IsConnOpen(clientConn));
+    Must(Comm::IsConnOpen(srvConn));
+
     debugs(26,5, "Revert to tunnel FD " << clientConn->fd << " with FD " << srvConn->fd);
 
     /* Create state structure. */
@@ -1277,10 +1383,6 @@
     else
         request->prepForDirect();
 
-    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
-                                     CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
-    commSetConnTimeout(srvConn, Config.Timeout.read, timeoutCall);
-
     // we drain any already buffered from-server data below (rBufData)
     fd_table[srvConn->fd].useDefaultIo();
 
diff -u -r -N squid-5.0.2/src/urn.cc squid-5.0.3/src/urn.cc
--- squid-5.0.2/src/urn.cc	2020-04-19 22:50:48.000000000 +1200
+++ squid-5.0.3/src/urn.cc	2020-06-08 03:33:42.000000000 +1200
@@ -387,7 +387,6 @@
 {
     char *buf = xstrdup(inbuf);
     char *token;
-    char *host;
     url_entry *list;
     url_entry *old;
     int n = 32;
@@ -406,24 +405,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-5.0.2/tools/CharacterSet.cc squid-5.0.3/tools/CharacterSet.cc
--- squid-5.0.2/tools/CharacterSet.cc	2020-04-20 00:20:18.000000000 +1200
+++ squid-5.0.3/tools/CharacterSet.cc	2020-06-09 19:09:23.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-5.0.2/tools/helper-mux/helper-mux.8 squid-5.0.3/tools/helper-mux/helper-mux.8
--- squid-5.0.2/tools/helper-mux/helper-mux.8	2020-04-20 00:20:19.000000000 +1200
+++ squid-5.0.3/tools/helper-mux/helper-mux.8	2020-06-09 19:09:23.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
