From: Abhijith PA Date: Sat, 29 Oct 2022 12:33:47 +0000 (+0100) Subject: Import trafficserver_8.0.2+ds-1+deb10u7.debian.tar.xz X-Git-Tag: archive/raspbian/8.0.2+ds-1+rpi1+deb10u7^2~28^2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=72fca7405f158fc5a95738903998944397530dc8;p=trafficserver.git Import trafficserver_8.0.2+ds-1+deb10u7.debian.tar.xz [dgit import tarball trafficserver 8.0.2+ds-1+deb10u7 trafficserver_8.0.2+ds-1+deb10u7.debian.tar.xz] --- 72fca7405f158fc5a95738903998944397530dc8 diff --git a/CONFIGURATION.Debian b/CONFIGURATION.Debian new file mode 100644 index 00000000..206614a7 --- /dev/null +++ b/CONFIGURATION.Debian @@ -0,0 +1,99 @@ +trafficserver for Debian +------------------------ + +Here are some configuration hints related to Traffic Server. Unless you already +noted, configuration files are located in /etc/trafficserver. Traffic Server +has some uncommon behaviour here: Please note, those files are generated auto- +matically and some may not even be intended to be edited manually. Moreover it +will rotate and synchronize configuration files if you run a cluster. Hence be +careful when editing them, chances are Traffic Server WILL OVERWRITE them. This +is especially true for comments which are not kept when using `traffic_line' +(as explained below). + +* To get a general idea about traffic server and its architecture, read [4]. It + is crucial to understand the internal architecture before trying to start. + +* The main configuration file is `records.config'. You may either edit the main + configuration file by hand, but it is suggested to use the `traffic_line' + command instead. More on editing configuration files can be found on [1]. The + `traffic_line' command is used like this: + + traffic_line -s proxy.config.proxy_name -v www.example.com + + You might need to tell Traffic Server about your changes. If it is already + running use `traffic_line -x' to inform it about your changes. Please note + that you must restart your `traffic_server' daemon for some changes. + +* If you used Squid before you might find [2] helpful which lists Traffic Serv- + er configuration directives along their Squid equivalent. Use this as resour- + ce to understand Traffic Server. + +* To help you to get started, I am going to introduce you very briefly to Traf- + fic Server. Note, lines starting with `#' are remarks: + + Open `records.config'. + + # Configure the cluster interface + CONFIG proxy.config.cluster.ethernet_interface STRING eth0 + + + # You may also want to bind your proxy server to a specific IP: + # Please note the security warning below as well + CONFIG proxy.local.incoming_ip_to_bind 1.2.3.4 + + # Next configure the listening port for incoming connections + CONFIG proxy.config.http.server_port INT 80 + + # Now tell Traffic Server how much Memory it is allowed to consume + # Set this value to -1 for best results which will instruct Traffic Server + # to consume roughly 1 MB for each GB of your disk backend storage size + # for memory caching. If you can afford it, go on and trash as much as + # you want and put here any positive value indicating the memory cache + # in bytes. + CONFIG proxy.config.cache.ram_cache.size LLONG 131072 # (128 MB) + + Next edit `storage.config'. Debian ships with a default configuration poin- + ting to a cache file in `/var/cache/trafficserver' sized 256MB. This is fair- + ly ok for testing, otherwise feel free to change (value is in KB): + + /var/cache/trafficserver 262144 + + Finally open `remap.config' where you need to tell Traffic Server about + your origin server(s): + + map http://www.example.com/ http://www.example.com:8080/ + # ---------------------- ---------------------------- + # ^- This is your client This is your origin -^ + # Host: header that server. + # is to be mappend + +* For a more comprehensive documentation about Traffic Server and its configu- + ration directives consult [3]. + + Yet the documentation on the web page can not be considered up to date for some + parts. Feel free to ask for help on the Apache Traffic Server mailing list: + + E-Mail + URL: + + More information may be found on either resource among those: + Project Website: + IRC: #traffic-server on irc.freenode.net. + Project Wiki: https://cwiki.apache.org/TS/traffic-server.html + +* Traffic Server can basically act in two modes: As forward proxy and as a rever- + se proxy. Traffic Servers primary purpose is to act as reverse proxy (e.g. + different to Squid). Nonetheless you can Traffic Server still instruct to act + as forward proxy as well. If you know what you are doing, you need to set + + CONFIG proxy.config.url_remap.remap_required INT 0 + + for that purpose. + + +[1] https://docs.trafficserver.apache.org/en/latest/admin-guide/configuring-traffic-server.en.html +[2] https://cwiki.apache.org/confluence/display/TS/SquidConfigTranslation +[3] https://docs.trafficserver.apache.org/en/latest/admin-guide/files/records.config.en.html +[4] https://docs.trafficserver.apache.org/en/latest/admin-guide/introduction.en.html + + -- Arno Töll Wed, 21 Mar 2012 14:10:21 +0100 diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..d4253168 --- /dev/null +++ b/NEWS @@ -0,0 +1,23 @@ +trafficserver (6.0.0-3) unstable; urgency=medium + + If you are using 32-bits package, please not that upstream has + discontinued 32-bit support. + 32-bits Debian package will still be built, but is subject to removal + whenever 32-bit fails. + + -- Jean Baptiste Favre Mon, 14 Dec 2015 10:29:12 +0100 + +trafficserver (3.0.0-1) unstable; urgency=low + + If you are upgrading from v2.1.8 or earlier to ATS 3.0 you need to migrate + to the new `records.config' configuration file. If possible, let Debian + overwrite your `records.config' configuration file. + + Moreover you should clear all caches after upgrading (e.g. do "traffic_server + -Cclear"). + + The host.db data abse isn't required anymore. Therefore you can delete + `/etc/trafficserver/internal/hostdb.config' and `/var/cache/trafficserver/ + host.db'. + + -- Arno Töll Wed, 15 Jun 2011 16:23:13 +0200 diff --git a/README.Debian b/README.Debian new file mode 100644 index 00000000..cd6a3cae --- /dev/null +++ b/README.Debian @@ -0,0 +1,44 @@ +trafficserver for Debian +------------------------ + +* ATS 8.0.0 comes with slight changes in config files. + Following ones has been removed: + - /etc/trafficserver/vaddrs.config + - /etc/trafficserver/metrics.config + - /etc/trafficserver/logging.config + - /etc/trafficserver/log_hosts.config + - /etc/trafficserver/congestion.config + - /etc/trafficserver/cluster.config + - /etc/trafficserver/body_factory/default/congestion#retryAfter + +* New config files have been added and now use yaml format. + More files will be migrated in the future: + - /etc/trafficserver/logging.yaml + - /etc/trafficserver/ssl_server_name.yaml + +* gzip pluging has been renamed into compress. + Existing configuration won't be updated + +* traffic_cop has been removed. Now, init and systemd files use traffic_manager + + -- Jean Baptiste Favre Fri, 21 Sep 2018 09:04:07 +0200 + +* If you are using 32-bits package, please note that upstream has + discontinued 32-bit support. + 32-bits Debian package will still be built, but is subject to removal + whenever 32-bit fails. + + -- Jean Baptiste Favre Mon, 14 Dec 2015 10:29:12 +0100 + +* Please read the CONFIGURATION.Debian file to get started with Traffic Server. + +* Currently Apache Traffic Server lacks man pages of binaries shipped along the + package. That's a known problem which is being worked on. Please read the + upstream documentation on the website instead. + +* If you read upstream's documentation about Traffic Server you will sometimes + find references to a program `/usr/bin/trafficserver`. Debian does not ship + this script. This is intentional. Use Debian's /etc/init.d/trafficserver + script instead. + + -- Arno Töll Tue, 31 Dec 2011 13:54:18 +0100 diff --git a/README.conf-remap.Debian b/README.conf-remap.Debian new file mode 100644 index 00000000..28d9e4dc --- /dev/null +++ b/README.conf-remap.Debian @@ -0,0 +1,26 @@ +Trafficserver-plugin-conf-remap for Debian +----------------------------------------- + +* The conf_remap plug-in allows you to override configuration directives + dependent on actual remapping rules. +* This plug-in replaces older legacy functionality which allowed to achieve a + similar behaviour previously by configuring remap rules for example with + switches like "@pristine_host_hdr=1" in your remap configuration. +* If you want to achieve this behaviour now, configure a remap rule like this: + + map http://cdn.example.com/ http://some-server.example.com \ + @plugin=conf_remap.so @pparam=/etc/trafficserver/cdn.conf + + where cdn.conf would look like records.config, e.g. + + CONFIG proxy.config.url_remap.pristine_host_hdr INT 1 + + Doing this, you will override your global default configuration on a per map- + ping rule. + +* You may want to look on + + for a full list of options which are allowed to be overridden and perhaps + some updated information. + + -- Arno Toell Tue, 12 Jan 2011 19:30:18 +0100 diff --git a/change_config.pl b/change_config.pl new file mode 100755 index 00000000..01072dfb --- /dev/null +++ b/change_config.pl @@ -0,0 +1,32 @@ +#! /usr/bin/perl + +use strict; + +our @config_lines; +our %defaults = ( + "proxy.config.alarm_email" => "trafficserver", + "proxy.config.admin.user_id" => "trafficserver", + "proxy.config.log.max_space_mb_for_logs" => 2000, +); + +die("Usage: $0 ") unless $#ARGV == 0; + +open(F, "+<", $ARGV[0]) || die("Cannot open $ARGV[0]: $!"); +while(my $line = ) +{ + if ($line =~ /^CONFIG/) + { + foreach my $pattern (keys %defaults) + { + $line =~ s/(?<=CONFIG $pattern)(\s+[A-Z]+)\s+.+$/$1 $defaults{$pattern}/; + } + } + push(@config_lines, $line); +} +seek(F, 0, 0); +foreach my $line (@config_lines) +{ + print F $line; +} +print F "CONFIG proxy.config.admin.user_id STRING trafficserver\n"; +close(F); diff --git a/changelog b/changelog new file mode 100644 index 00000000..6910cf99 --- /dev/null +++ b/changelog @@ -0,0 +1,834 @@ +trafficserver (8.0.2+ds-1+deb10u7) buster-security; urgency=medium + + * Non-maintainer upload by the Debian LTS Team. + * Multiple CVE fixes + + CVE-2021-37150: Protocol vs scheme mismatch + + CVE-2022-25763 Improper input validation on HTTP/2 headers + + CVE-2022-28129 Insufficient Validation of HTTP/1.x Headers + + CVE-2022-31780 HTTP/2 framing vulnerabilities + + -- Abhijith PA Sat, 29 Oct 2022 18:03:47 +0530 + +trafficserver (8.0.2+ds-1+deb10u6) buster-security; urgency=high + + * Multiple CVE fixes for 8.0.x + + CVE-2021-37147: Improper input validation vulnerability + + CVE-2021-37148: Improper input validation vulnerability + + CVE-2021-37149: Improper Input Validation vulnerability + + CVE-2021-38161: Improper Authentication vulnerability in TLS origin verification + + CVE-2021-44040: Improper Input Validation vulnerability in request line parsing + + CVE-2021-44759: Improper Authentication vulnerability in TLS origin validation + + -- Jean Baptiste Favre Sat, 21 May 2022 21:14:28 +0200 + +trafficserver (8.0.2+ds-1+deb10u5) buster-security; urgency=medium + + * CVE-2021-35474 CVE-2021-32567 CVE-2021-32566 CVE_2021-32565 + CVE-2021-27577 (Closes: #990303) + + -- Moritz Mühlenhoff Mon, 26 Jul 2021 22:59:59 +0200 + +trafficserver (8.0.2+ds-1+deb10u4) buster-security; urgency=high + + * Add fix from upstream for CVE-2020-17508 + * Add fix from upstream for CVE-2020-17509 + + -- Jean Baptiste Favre Sun, 06 Dec 2020 17:00:17 +0100 + +trafficserver (8.0.2+ds-1+deb10u3) buster-security; urgency=high + + * Add fix from upstream for CVE-2020-9494 (Closes: #963629) + + -- Jean Baptiste Favre Thu, 25 Jun 2020 19:58:34 +0200 + +trafficserver (8.0.2+ds-1+deb10u2) buster-security; urgency=medium + + * Add fix from upstream for CVE-2019-17559 + * Add fix from upstream for CVE-2019-17565 + * Add fix from upstream for CVE-2020-1944 + * Add fix from upstream for CVE-2020-9481 + + -- Jean Baptiste Favre Thu, 16 Apr 2020 21:36:40 +0200 + +trafficserver (8.0.2+ds-1+deb10u1) buster-security; urgency=high + + * Add patch for security backport from 8.0.4 for CVE-2019-9512, + CVE-2019-9514, and CVE-2019-9515. (Closes: #934887) + * Add patch for security backport from 8.0.5 for fixes + CVE-2019-9518 (Closes: #935314) + + -- Jean Baptiste Favre Mon, 26 Aug 2019 13:55:33 +0200 + +trafficserver (8.0.2+ds-1) unstable; urgency=medium + + * Disable testing build in d/gitlab-ci.yml + * Update d/copyright to remove unneeded files when importing upstream release + * Update upstream GPG keys list + * New upstream version 8.0.2+ds + * Patches refresh for 8.0.2 + * Update patch. Add dep3 headers + * Update d/control. Mark trafficserver-dev architecture all + * Remove signature from upstream keys. Fix lintian info + * Move dpkg-maintscript-helper commands to d/trafficserver.maintscript + + -- Jean Baptiste Favre Wed, 30 Jan 2019 14:45:09 +0100 + +trafficserver (8.0.1-4) unstable; urgency=medium + + * Add patch and update d/control, d/rules to use system provided yaml-cpp + + -- Jean Baptiste Favre Fri, 11 Jan 2019 19:07:28 +0100 + +trafficserver (8.0.1-3) unstable; urgency=medium + + * Add missed experimental plugin certifier + * Add man pages paths to d/not-installed to avoid too verbose dh_missing list. + Manpages are compressed before being install. Non-compressed version is in + turn erroneously detected as not installed. + * Update patch which has been merged upstream + + -- Jean Baptiste Favre Mon, 07 Jan 2019 15:24:38 +0100 + +trafficserver (8.0.1-2) unstable; urgency=medium + + * Update Standards Version in d/control. No other changes needed + * Update trafficserver-experimental-plugins package descriptions + in d/control to fix lintian warnings + * Add patch 0012-fix-spelling-checks to fix spellings. Forwarded upstream + * Update patch 0006-fix-doc-build to include missing manpages + * Remove d/trafficserver.lintian-overrides + * Add patch 0013-fix-perl-interpreter-path. Fix lintian error which used + to be overridden in d/trafficserver.lintian-overrides + * Update d/trafficserver-dev.lintian-overrides + + -- Jean Baptiste Favre Thu, 03 Jan 2019 10:52:44 +0100 + +trafficserver (8.0.1-1) unstable; urgency=medium + + * New upstream version 8.0.1 + * Patches refresh for 8.0.1 + * Removed patches which have been merged upstream: + - 0002-add-mips64-support + - 0004-fix_arm_build + - 0005-fix_build_kfreebsd + - 0007-fix-uri_signing + - 0010-fix-32bits-build + - 0012-fix-traffic_via + * Fix lintian warning public-upstream-key-not-minimal + + -- Jean Baptiste Favre Mon, 03 Dec 2018 14:15:07 +0100 + +trafficserver (8.0.0-4) unstable; urgency=medium + + [ Emanuele Rocca ] + * Update descriptions in d/control + + [ Jean Baptiste Favre ] + * Update patch to fix kfreebsd FTBFS + + -- Jean Baptiste Favre Mon, 05 Nov 2018 10:13:03 +0100 + +trafficserver (8.0.0-3) unstable; urgency=medium + + * Remove webptransform experimental plugin + * Update patch to fix kfreebsd FTBFS + * Update d/copyright to fix lintian error + * Update d/rules to fix FTBFS for mips & mipsel archs + * Update d/rules to spare call to dpkg-parsechangelog + * Update d/copyright after 8.0.0 file moves + + -- Jean Baptiste Favre Fri, 02 Nov 2018 14:24:38 +0100 + +trafficserver (8.0.0-2) unstable; urgency=medium + + * Add a patch to fix FTBFS with MySQL 8 (LP: #1795362) + * Update 0009-fix-mysql-8-build.patch which has been merged upstream + * Bump Standards-Version in d/control + * Add patch to fix 32bits arm & mips arch build + * Backport upstream patch to fix segmentation fault + * Backport upstream patch to fix traffic_via + + -- Jean Baptiste Favre Wed, 17 Oct 2018 20:40:51 +0200 + +trafficserver (8.0.0-1) unstable; urgency=medium + + [ Aron Xu ] + * Remove .vscode directory + * systemd: After=network-online.target + + [ Jean Baptiste Favre ] + * Update d/changelog for 8.0.0 + * Update init and service file after traffic_cop removal + * Update d/README.Debian to reflect configuration changes + * Add d/gitlab-ci.yml to use Salsa CI + * Update d/control + * Add build-deps against graphviz, python3-sphinxcontrib.plantuml + and plantuml to build documentation & manpages + * Add build-deps against libcrypto++-dev, libjansson-dev, libcjose-dev to + build uri_signing plugin + * Update Breaks to manage plugins transition from experimental to stable + * Update postinst maintainer script, add postrm & preinst scripts to remove + obsolete config files + - /etc/trafficserver/vaddrs.config, + - /etc/trafficserver/metrics.config, + - /etc/trafficserver/logging.config, + - /etc/trafficserver/log_hosts.config, + - /etc/trafficserver/congestion.config, + - /etc/trafficserver/cluster.config, + - /etc/trafficserver/body_factory/default/congestion#retryAfter + * Update d/trafficserver and d/trafficserver-experimental-plugins install + files + - Stable plugin gzip renamed to compress + - Promoted stable plugins: + - cachekey, + - cache_promote, + - escalate, + - test_cppapi, + - tslua + - Added new experimental plugins: + - access_control, + - fq_pacing, + - prefetch, + - server_push_preload, + - system_stats, + - tls_bridge, + - traffic_dump, + - uri_signing + - Remove deprecated plugins collapsed_connection, epic + * Remove obsolete config option --enable-system-luajit from d/rules + * Add --with-build-version option to d/rule to have a deterministic + build-version + * Update d/copyright to remove .vscode during import at repack stage + * Update patches for 8.0.0 + - Refresh patches for 8.0.0 + - Removed patches: + - 0004-force-use-luajit-system*, updated upstream + - 0005-fix_documentation_build_option, updated upstream + - 0008-fix_build_lua, updated upstream + - 0009-fix-doc-python3, update upstream + - 0010-Remove-custom-memory-barriers-from-header_rewrite-an patches, + updated upstream + - Renamed remaining patches: + - 0006-fix_arm_build -> 0004-fix_arm_build, + - 0007-fix_build_kfreebsd -> 0005-fix_build_kfreebsd, + - 0009-fix-doc-git -> 0006-fix-doc-build + - Add patch to fix uri_signing experimental plugin build + - Add patch to fix check-unused-dependencies + * New upstream version 8.0.0 + + -- Jean Baptiste Favre Sat, 06 Oct 2018 12:43:13 +0200 + +trafficserver (7.1.4+ds-1) unstable; urgency=medium + + [ Aron Xu ] + * Use libunwind for mips* + + [ Jean Baptiste Favre ] + * Update Debian standards version + * New upstream version 7.1.4+ds + * Patches refresh for 7.1.4 + * Add lintian override for trafficserver package + + -- Jean Baptiste Favre Mon, 06 Aug 2018 10:56:14 +0200 + +trafficserver (7.1.3+ds-4) unstable; urgency=medium + + * Fix FTBS on armel (Closes: #902112) + + -- Jean Baptiste Favre Fri, 22 Jun 2018 17:13:53 +0200 + +trafficserver (7.1.3+ds-3) unstable; urgency=medium + + * Add systemd tmpfile support (LP: #1756207) + * Update Build-Depends (Closes: #887503) + + -- Jean Baptiste Favre Wed, 13 Jun 2018 22:39:58 +0200 + +trafficserver (7.1.3+ds-2) unstable; urgency=medium + + * Update build-deps to libncurses6 + * Update patch to fix arm* builds + + -- Jean Baptiste Favre Fri, 18 May 2018 17:25:22 +0200 + +trafficserver (7.1.3+ds-1) unstable; urgency=medium + + * Update Vcs URLs. Replace alioth with salsa + * Add patch to make documentation build with python3. Fix lintian warning + * New upstream version 7.1.3+ds + * Update d/gbp.conf + * Patches refresh for 7.1.3 + * Add a patch to fix doc build outside of git repository + * Update standards version + * Remove chown command from postinst script. Fix lintian warning + + -- Jean Baptiste Favre Tue, 24 Apr 2018 10:17:21 +0200 + +trafficserver (7.1.2+ds-3) unstable; urgency=medium + + * Add upstream changelog file to trafficserver package + * Add systemd units file + * Fix documentation. Update links + * Update d/rules to remove examples binary files from package + + -- Jean Baptiste Favre Fri, 16 Feb 2018 15:05:07 +0100 + +trafficserver (7.1.2+ds-2) unstable; urgency=medium + + * Drop patch for lua source code removal (Handled with repack) + * Drop patch for documentation build (merged upstream) + * Add a patch to make man pages being built + * Update d/compat + * Fix lintian warnings + * Update d/rule to fix lintian warning. + * Disable auto_test (again) + * Install CHANGELOG file in /usr/share/doc + * Enable documentation built (if not, man pages aren't either) + + -- Jean Baptiste Favre Thu, 25 Jan 2018 23:38:51 +0100 + +trafficserver (7.1.2+ds-1) unstable; urgency=medium + + * New upstream version 7.1.2+ds + * Update d/patches for 7.1.2+ds + * Update build dependencies + * Update compilation flags + * Enable experimental plugin cache_key_genid + + -- Jean Baptiste Favre Tue, 16 Jan 2018 18:18:40 +0100 + +trafficserver (7.1.1-1) unstable; urgency=medium + + * Fix trafficserver-dev dependencies. (Closes: #877457) + * Fix d/gbp.conf. Remove duplicate filter option + * Update standards version in d/control + * Fix debian-rules-sets-dpkg-architecture-variable lintian warning + * Fix debian-watch-uses-insecure-uri lintian info + * Update d/patches + * Update d/rules to reflect healthcheck being managed as a stable plugin + * Add a patch to fix kfreebsd build + * Add a patch to fix arm build + * New upstream version 7.1.0 + * Remove broken 0008-fix_build_armel patch + * Patches refresh for 7.1.0 + * Add new patch to fix build with luajit 2.1 (Closes: #873328) + * Update experimental modules list + * Update Debian Standards-Version & d/compat + * Update Vcs-* fields to use secure communication + * Lintian fix for d/NEWS + * Add new build option to use system luajit + * Update build dependencies (Closes: #859750) + * Fix lintian warning in d/copyright + * New upstream version 7.1.1 + * Patches refresh for 7.1.1 + + -- Jean Baptiste Favre Tue, 02 Jan 2018 13:30:43 +0100 + +trafficserver (7.0.0-5) unstable; urgency=medium + + * Add patch to fix arm* build. (Closes: #857389) + + -- Jean Baptiste Favre Wed, 22 Mar 2017 09:31:14 +0100 + +trafficserver (7.0.0-4) unstable; urgency=medium + + * Add Conflicts for file overwrites (Closes: #850775) + + -- Aron Xu Wed, 11 Jan 2017 14:49:15 +0800 + +trafficserver (7.0.0-3) unstable; urgency=medium + + * Fix documentation build for docutils >= 0.13 (Closes: #848800) + * Update LuaJIT patches serie to fallback using Lua if LuaJIT not found + * Update luajit patch. + - Remove hardcoded value + - Add luajit dynamic detection + + -- Jean Baptiste Favre Wed, 04 Jan 2017 15:13:43 +0100 + +trafficserver (7.0.0-2) unstable; urgency=medium + + * Add patches to use system luajit and not the internal one any more + * Update d/rules to remove luajit exception. + + -- Jean Baptiste Favre Fri, 25 Nov 2016 17:51:25 +0100 + +trafficserver (7.0.0-1) unstable; urgency=medium + + [ Arno Töll ] + * Remove myself as maintainer. Thanks to Aron and Jean Baptiste for stepping + in on my place and keeping the package up to date. It makes the confident + the package is in good hands now. + + [ Jean Baptiste Favre ] + * New upstream release 7.0.0 + * Patches refresh for 7.0.0 + * Update plugins list + * Update build dependencies + * Add pkgconfig .pc file in trafficserver-dev package + * Enable experimental plugins webp_transform + * Enable traffic_top build (Closes: #836126) + * Remove Linux AIO support (Closes: #803661, #836124) + * Fix FTBFS for mips64el (Closes: #830856) + * Add patch to make the build reproductible (Closes: #833176) + + [ Aron Xu ] + * Set myself as Maintainer at the moment + * Build-Depends on default-libmysqlclient-dev + + -- Jean Baptiste Favre Wed, 16 Nov 2016 09:48:10 +0100 + +trafficserver (6.2.0-1) unstable; urgency=medium + + * Add upstream gpg keys for uscan pgpsigurlmangle option + * Imported Upstream version 6.2.0 + * Patch refresh for 6.2.0 + * Update trafficserver manpages list + * Update trafficserver install list + * Update trafficserver-experimental-plugins install list + * Update build dependencies (Add libmariadbclient-dev in experimental) + * Bump standards version + + -- Jean Baptiste Favre Sat, 23 Jul 2016 19:15:58 +0200 + +trafficserver (6.1.1-1) unstable; urgency=medium + + * Imported Upstream version 6.1.1 + * Update d/watch for gpg signature check + + -- Jean Baptiste Favre Sat, 06 Feb 2016 10:37:41 +0100 + +trafficserver (6.1.0-1) unstable; urgency=medium + + * New upstream release + * Use -mcx16 on x86 platforms only (Closes: #805124) + + -- Aron Xu Tue, 02 Feb 2016 12:18:10 +0800 + +trafficserver (6.0.0-3) unstable; urgency=medium + + * Update d/NEWS & d/README.Debian with 32-bit upstream support drop + + -- Jean Baptiste Favre Mon, 14 Dec 2015 10:48:20 +0100 + +trafficserver (6.0.0-2) unstable; urgency=medium + + * Re-enable 32-bit builds + * Add some optional build dependencies + * Use -O3 by default + + -- Aron Xu Sat, 31 Oct 2015 03:05:20 +0800 + +trafficserver (6.0.0-1) unstable; urgency=medium + + [ Aron Xu ] + * Avoid possible-missing-colon-in-closes + + [ Jean Baptiste Favre ] + * Imported Upstream version 6.0.0 + * Update experimental plugins list + - Remove channel_stats + - Add cache promote cache_promote.so + - Add cache ranage request cache_range_requests.so + - Add mp4 streaming media mp4.so + - Add Stream editor stream_editor.so + + -- Aron Xu Sat, 10 Oct 2015 11:19:19 +0200 + +trafficserver (5.3.1-1) unstable; urgency=medium + + [ Arno Töll ] + * Add proxy.config.admin.user_id setting + + [ Aron Xu ] + * Add myself back to Uploaders + * Use dh compat 9 + + [ Jean Baptiste Favre ] + * Imported Upstream version 5.3.1 + * Removed patch for TS-3632 (merged upstream) + * Upstream changes allow ftbfs with GCC-5 (Closes: #778148) + + -- Jean Baptiste Favre Sat, 04 Jul 2015 23:20:49 +0200 + +trafficserver (5.3.0-2) unstable; urgency=medium + + * Avoid installing tslua.so on archs built without LuaJIT + (Closes: #770353) + + -- Aron Xu Thu, 11 Jun 2015 15:28:39 +0800 + +trafficserver (5.3.0-1) unstable; urgency=medium + + * Imported Upstream version 5.3.0 + - CVE-2014-10022: remote DoS + * Remove fix_TS3316_i386_build patch + * Fix traffic_wccp build, missing OpenSSL symbol + * Add experimental plugin generator + * Format patch for TS-3632 + * Update Uploader field in d/control + + -- Jean Baptiste Favre Wed, 03 Jun 2015 20:06:13 +0200 + +trafficserver (5.2.0-2) unstable; urgency=medium + + * Disable LuaJIT for arm64 + + -- Aron Xu Sat, 28 Feb 2015 23:51:00 +0800 + +trafficserver (5.2.0-1) unstable; urgency=medium + + [ Jean Baptiste Favre ] + * New upstream release. + * Add support for following experimental modules: + - epic + - header_normalize + - mysql_remap + - ssl_cert_loader + - sslheaders + * Remove missing file related to removed traffic_shell + * Backport patch for TS-3316 to solve i386 build issue + * Fix misconfigured call to dh_makeshlibs (used system dir instead + of temporary build one) + + [ Aron Xu ] + * Enable all hardening features. + * Update copyright file. + + -- Aron Xu Thu, 12 Feb 2015 22:22:44 +0100 + +trafficserver (5.1.1-1) unstable; urgency=medium + + * New upstream release. This release includes fixes for these security + related vulnerabilities: + - CVE-2014-3566: Do not enable SSL3 by default + - CVE-2014-3624: Ensure remap requests are properly tunneled using CONNECT + requests to avoid an open relay + * Add support for ppc64el, patch merged upstream (Closes: #754134, #754808) + * Fix "ftbfs on kfreebsd" by conditionally building healthchecks.so on Linux + only. Patch supplied by Steven Chamberlain, thanks (Closes: #767287) + + -- Arno Töll Sun, 02 Nov 2014 12:40:44 -1100 + +trafficserver (5.1.0-1) unstable; urgency=medium + + * New upstream release + * Bump standards version to 3.9.6 (no changes needed) + + [Jean Baptiste Favre] + * Add a debug package with debug symbols for those who need it. + * Split out experimental trafficserver plugins into a separate package so + that users are aware of their experimental character before using them. + * Add a libhwloc b-d so that ATS has a better idea about the underlying + hardware it runs at to improve the runtime performance + + -- Arno Töll Wed, 08 Oct 2014 23:50:49 -1100 + +trafficserver (5.0.1-1) unstable; urgency=medium + + * New upstream release including a fix for CVE-2014-3525 that allowed + attackers by special crafted packets to obtain privileges for services bound + to localhost + + -- Arno Töll Wed, 23 Jul 2014 04:43:00 -1100 + +trafficserver (5.0.0-1) unstable; urgency=medium + + * Acknowledge previous NMUs, thanks to Anibal Monsalve Salazar for + coordination. + * New upstream version. Patch changes: + + drop 0001-TS-1821.patch: released upstream in 5.0.0 + + drop 0001-TS-2454-Fix-undefined-reference-to-__sync_fetch_and_.patch: + released upstream in 4.2 + + drop add-mips-support.patch: released upstream in 5.0.0 + + drop pthread_setname_np.patch: This was a Debian specific issue which is + being worked around in eglibc's commit r5460. + * Add "support for mips64": merged upstream (Closes: #750807) + * Build with dh-autoreconf to avoid build time issues with Automake 1.13 + which is not in Debian yet. + + -- Arno Töll Sun, 06 Jul 2014 00:20:59 -1100 + +trafficserver (4.1.2-1.2) unstable; urgency=medium + + * Non-maintainer upload. + * Add missing prototype for pthread_setname_np() + Add pthread_setname_np.patch + Patch by Petr Salinger + Closes: #743584 + + -- Anibal Monsalve Salazar Fri, 04 Apr 2014 08:59:48 +0100 + +trafficserver (4.1.2-1.1) unstable; urgency=medium + + * Non-maintainer upload. + * Fix undefined reference to `__sync_fetch_and_sub_8' on ARM 32bit + Add 0001-TS-2454-Fix-undefined-reference-to-__sync_fetch_and_.patch + from https://issues.apache.org/jira/browse/TS-2454 + Patch by Yunkai Zhang + * Add support for MIPS + Add add-mips-support.patch + Submitted: https://issues.apache.org/jira/browse/TS-2687 + Merged: https://git-wip-us.apache.org/repos/asf?p=trafficserver.git;h=2f81790 + Patch by Dejan Latinovic + Closes: 743395 + * Build-depend on libboost-dev + Patch by Dejan Latinovic + Closes: #737510 + + -- Anibal Monsalve Salazar Thu, 03 Apr 2014 04:18:23 +0100 + +trafficserver (4.1.2-1) unstable; urgency=medium + + * Merge the experimental branch of trafficserver to unstable + * New upstram release (Closes: #711530, #733377) + + Refresh 0001-TS-1821.patch until it is fixed upstream + + Don't run autoreconf anymore, we do not need it anymore + + build depend on libaio-dev to support AIO on Linux systems + * Push standards version + + -- Arno Töll Wed, 29 Jan 2014 03:01:12 -1100 + +trafficserver (3.3.2-1) experimental; urgency=low + + [ Arno Töll ] + * Drop --with-arg-max from configure + * Update changelog in view of the new upstream version + + [ Aron Xu ] + * Imported Upstream version 3.3.2 + * Drop patch for enabling experimental plugins + * Add liblua5.1-dev and liboost1.53-dev to B-D + * Try on all archs for experimental builds + * Permit parallel building + * Fix typo in dep5 copyright file + * Do not install staticly linked library + * Enable Linux native AIO support for linux-any + * Enable reclaimable freelist + + -- Aron Xu Thu, 09 May 2013 01:00:04 +0800 + +trafficserver (3.3.0+git20121208-0exp1) experimental; urgency=low + + * Upstream git snapshot. + * Enable most of experimental plugins, install related libraries. + * Run dh_autoreconf. + * Make dh_auto_test errors non-fatal. + + -- Aron Xu Thu, 06 Dec 2012 23:32:25 +0800 + +trafficserver (3.3.0-1) experimental; urgency=low + + * Upload upstream development release to experimental. + + -- Aron Xu Thu, 29 Nov 2012 22:13:55 +0800 + +trafficserver (3.2.5-1) unstable; urgency=low + + * New upstream release + + Fix FTBFS on ARM (Closes: #691179) + + Fix FTBS with gcc 4.8 (Closes: #701427) + * Promote trafficserver to depend for trafficserver-dev to fix a broken + library symlink. The library is not required for all users, but those who + need it don't need to install it manually anymore (Closes: #715134) + + -- Arno Töll Sun, 21 Jul 2013 11:55:38 +0200 + +trafficserver (3.2.4-1) unstable; urgency=low + + * New upstream release + + Delete upstream's .gitignore file in our source tree + * Switch packaging repository to Git. + + Add gbp.conf file for those using git-buildpackage + * Fix "Upgrade fails if purging of cache fails" by not dying in a fire when + the postinst fails to purge the cache (Closes: #687698) + * Drop --with-arg-max from ./configure, it's not needed anymore. + + -- Arno Töll Tue, 29 Jan 2013 23:54:44 +0100 + +trafficserver (3.2.0-1) unstable; urgency=low + + * New upstream release + + If you are using SSL or HTTP filtering, please update your configuration. + proxy.config.http.quick_filter.mask and + proxy.config.ssl.server.cert.filename is not recognized anymore. Please + use ip_allow.config and ssl_multicert.config respectively instead. + There is no automated migration for this in Debian, as this affects your + site-specific configuration files. + + See https://cwiki.apache.org/confluence/display/TS/Upgrading+to+3.2 for + full upgrade instructions. + * Upstream decided to ship more plug-ins with the trafficserver core + distribution. These are all bundled into the main package now. Therefore, + the trafficserver-plugin-conf-remap package is not provided anymore. + * Update the default configuration file to ship with more moderate values + for the log configuration. + * Now do start ATS by default for fresh installations. The default + out-of-the box configuration is much more secure than past defaults. + * Purge the host and data cache on upgrades + * Let's welcome Aron Xu to the Uploaders of Trafficserver. Hi Aron! :) + + -- Arno Töll Fri, 14 Sep 2012 22:56:29 +0200 + +trafficserver (3.0.5-1) unstable; urgency=low + + * New upstream release. + * No kudos for the previous hostile NMU, but include the changelog to denote + this upload does not introduce a regression. + * Update my maintainer address + * Make the init script look much better when using fancy outputs. + * Fix "status" output of the init script + * Remove "DM-Upload-Allowed". I don't need that flag anymore. + + + -- Arno Töll Sat, 09 Jun 2012 18:48:23 +0200 + +trafficserver (3.0.4-1.1) unstable; urgency=low + + * Non maintainer upload + * Fix build failure with GCC 4.7. Closes: #667396. + + -- Matthias Klose Wed, 30 May 2012 04:40:28 +0000 + +trafficserver (3.0.4-1) unstable; urgency=high + + * New upstream release + + Fix CVE-2012-0256: A request with a very large Host header caused ATS to + crash. + * Setting urgency to high because of security updates + * Push standards to 3.9.3 - no further changes + * Stilistic adaptions in debian/copyright, but not content changes + * Remove cluster interface warning from README.Configuration. ATS now binds on + lo by default + + -- Arno Töll Wed, 21 Mar 2012 12:34:35 +0100 + +trafficserver (3.0.2-1) unstable; urgency=low + + * New upstream release + + Includes former Debian specific patch which makes sure the upstream + configure script does not override any -O flags passed by the user + anymore. + * Adapt to dpkg 1.16.1 API changes regarding build flags. This enables + hardening build flags. This means, trafficserver is now being built with + -fstack-protector and other security related build flags. + * Add dpkg-dev (>= 1.16.1~) to build-depends to make sure our buildflags are + properly supported. That's guaranteed for Testing, but might be helpful to + know for backporters. + * Fix several issues in the DEP-5 syntax. Unfortunately there is no way to + express that a file is subject to different license agreements so far. + * Do not install the upstream changelog twice anymore + * Finally run regression checks again, now as build failures are sorted out. + + -- Arno Töll Sun, 11 Dec 2011 00:45:45 +0100 + +trafficserver (3.0.1-2) unstable; urgency=low + + * Fix "please add armhf to the arch list" - add armhf to the list of supported + architectures. Thanks Konstantinos Margaritis for the hint (Closes: #636338) + * Remove IA64 from the list of supported architectures. The upgrade to the gcc + 4.6 toolchain disclosed portability issues with it, which caused the resul- + ting binary package to produce no-op code in some functions. + + -- Arno Töll Tue, 02 Aug 2011 22:58:37 +0200 + +trafficserver (3.0.1-1) unstable; urgency=low + + * New upstream release. Fixes several important issues which caused + `traffic_cop' and `traffic_server' to crash. + * Fix "FTBFS with ld --as-needed" re-order libraries upon linkage, patch + committed upstream. Thanks Ilya Barygin (Closes: #632546) + * Fix "trafficserver: Getting rid of unneeded *.la / emptying + dependency_libs", remove *.la files from the installation target completely + (Closes: #633192) + * Set "DM-Upload-Allowed: yes" in agreement with Asheesh Laroia + + + -- Arno Töll Mon, 01 Aug 2011 19:29:58 +0200 + +trafficserver (3.0.0-1) unstable; urgency=low + + * New upstream release. Major changes (since 2.1.9): + + `traffic_server' won't crash anymore when using non-existent plugin in + remap rule + + Don't cache HTTP 401, 303 and 407 error responses anymore, when negative + caching is enabled. + * Re-enable kfreebsd support, it was accidentally not available in + 2.1.9-unstable-1 because of non installable dependencies, as libcap-dev is + installable (and required) on Linux only + * debian/rules: + + Simplify dh_auto_configure flags (upstream incorporated our build layout) + + Enable WCCP (Web Cache Communication Protocol; Linux only) + * debian/control: + + Add flex and bison to build dependencies, both are required for WCCP + (Linux only) + + -- Arno Töll Wed, 15 Jun 2011 15:56:29 +0200 + +trafficserver (2.1.9-unstable-1) unstable; urgency=low + + * New upstream release. Major features (since 2.1.8): + + Bring back support for $DESTDIR and "make check" which makes Debian + patches obsolete + + Allow larger working sets than 512G + + Disable cluster autodiscovery when cluster mode is disabled + + Cleanup `records.config' + + Disable SSLv2 by default + * debian/control: Add build dependency to libcap-dev, because when running + traffic_server standalone, it is unable to bind restricted ports otherwise + (Upstream: TS-804) + * debian/rules: + + Remove override for dh_clean, but put options to debian/source/options + instead + + Remove DH_OPTIONS (unused anyway) + * Source package: Minor change to improving package quality and usability + (i.e. grammar, verbosity of comments) + * Make the init script more robust + * Base the origin of the package source on the untouched upstream tarball, + instead of the versioned SVN branch. + * Bring back IA64 support, this time actually working (upstream merged my + patch TS-783) + * Remove patch `build-quirks.patch'. Changes have been committed upstream + by now. + + -- Arno Töll Tue, 31 May 2011 21:56:12 +0200 + +trafficserver (2.1.8-unstable-1) unstable; urgency=low + + * New upstream release. Major features (since 2.1.5): + + Many bug fixes (none reported in Debian's BTS) + + Set source address for origin Servers + + Major API changes for the SDK + + Provide traffic_logstats + + traffic_shell does not hang anymore on any command + * Fix "FTBFS on architectures not supported upstream": (Closes: #622800) + + Don't execute regression checks for now (fixes x86) + + Upstream merges a Debian patch originally for 2.1.7 which enables + kFreeBSD support (originally provided by myself) + + Restrict Architectures (drop S390, IA64, MIPS[EL], PPC, SPARC) + * Remove ts-ui-disable-conf.patch (applied upstream) + * Update `build-quirks.patch' to make TS handle $DESTDIR correctly + (upstream: TS-759) + * Remove .deps from SDK binary package examples (they were incidentally + included before). + * Fix permissions for /var/cache/trafficserver in postinst + * Bump standards to 3.9.2, depend on debhelper 8.0, adapt VCS links + * Simplify debian/rules + + -- Arno Töll Thu, 05 May 2011 21:49:52 +0200 + +trafficserver (2.1.5-unstable-1) unstable; urgency=low + + * Initial release (Closes: #609285) + * Added some configuration and informational hints + * New upstream release. Major features: + + Better AMD64 support + + Override configuration per transaction + + IPv6 + + Support ARM architectures + + SDK-API changes + * Differences to upstream version: + + Ship some documentation. Well, really a few hints + + Split source into three packages (core, plug-in, SDK) + + Ship our own init script + + -- Arno Toell Tue, 13 Jan 2011 11:49:18 +0100 diff --git a/compat b/compat new file mode 100644 index 00000000..b4de3947 --- /dev/null +++ b/compat @@ -0,0 +1 @@ +11 diff --git a/control b/control new file mode 100644 index 00000000..2765cb09 --- /dev/null +++ b/control @@ -0,0 +1,66 @@ +Source: trafficserver +Section: web +Priority: optional +Maintainer: Aron Xu +Uploaders: Jean Baptiste Favre +Build-Depends: debhelper (>= 11), libssl-dev, tcl-dev, libexpat1-dev, + libpcre3-dev, libtool, libcap-dev [linux-any], graphviz, + bison [linux-any], flex [linux-any], dpkg-dev (>= 1.16.1~), pkg-config, libgeoip-dev, + libluajit-5.1-dev, libboost-dev, libhwloc-dev, default-libmysqlclient-dev, + python3-sphinx [!kfreebsd-any], python-sphinx [kfreebsd-any], plantuml, + python3-sphinxcontrib.plantuml [!kfreebsd-any], python-sphinxcontrib.plantuml [kfreebsd-any], + libxml2-dev, libncurses-dev, libcurl4-openssl-dev, + libkyotocabinet-dev, libmemcached-dev, libbrotli-dev, + libcrypto++-dev, libjansson-dev, libcjose-dev, libyaml-cpp-dev (>= 0.6.2~), + libunwind-dev [i386 amd64 ppc64el armhf arm64 mipsel mips64el mips] +Standards-Version: 4.3.0 +Homepage: http://trafficserver.apache.org/ +Vcs-Git: https://salsa.debian.org/debian/trafficserver.git +Vcs-Browser: https://salsa.debian.org/debian/trafficserver + +Package: trafficserver +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base (>= 3.2-14), adduser, + ${perl:Depends} +Provides: trafficserver-plugin-conf-remap +Replaces: trafficserver-plugin-conf-remap (<< 3.2~), + trafficserver-experimental-plugins (<< 8.0.0~) +Suggests: trafficserver-experimental-plugins (= ${binary:Version}) +Breaks: trafficserver-plugin-conf-remap (<< 3.2~), + trafficserver-experimental-plugins (<< 8.0.0~) +Description: fast, scalable and extensible HTTP/1.1 and HTTP/2.0 caching proxy server + This package provides the Apache Traffic Server, a fast, scalable reverse + proxy server which may operate as forward proxy as well. Apache Traffic Server + supports: + . + * Caching: improve response time while reducing server load and bandwidth + needs by caching and reusing frequently-requested web pages, images, and + web service calls. + * Proxying: add keep-alive, filter or anonymize content requests, or add + load balancing by adding a proxy layer. + * Scaling: handle 10s of thousands of requests per second on modern SMP + hardware. + * Extensions: use the API to do anything from modifying HTTP headers to + handling ESI requests to writing your own cache algorithm. + +Package: trafficserver-experimental-plugins +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Pre-Depends: trafficserver (= ${binary:Version}) +Breaks: trafficserver (<< 8.0.0~) +Description: experimental plugins for Apache Traffic Server + This package provides the Apache Traffic Server plugins marked as + experimental. + . + Please note that these plugins can be removed without priori notice, or + promoted as stable plugin. In the last case, they'll be moved from + trafficserver-experimental-plugins to trafficserver package. + +Package: trafficserver-dev +Architecture: any +Depends: ${misc:Depends}, trafficserver (= ${binary:Version}) +Description: Apache Traffic Server Software Developers Kit (SDK) + This package provides the Apache Traffic Server Software Developers Kit, which + consists of: a collection of development header and bindings for the C + programming language, the tsxs linking helper and examples to write your own + plug-ins for the Apache Traffic Server. diff --git a/copyright b/copyright new file mode 100644 index 00000000..eddb9830 --- /dev/null +++ b/copyright @@ -0,0 +1,130 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: Traffic Server +Upstream-Contact: dev@trafficserver.apache.org +Source: http://trafficserver.apache.org/ +Files-Excluded: + .vscode + lib/yamlcpp +Comment: + The upstream repository provides libraries which are also available + in Debian. Because we'll use libraries packaged in Debian, we don't + need embedded ones. + +Files: * +Copyright: 2010 - 2011 The Apache Software Foundation + 2009 Yahoo! Inc. +License: Apache-2.0 + +Files: debian/* +Copyright: 2011 Arno Töll +License: Apache-2.0 +Comment: Packaging for Debian was done by Arno Toell, and I hereby grant + distribution of it under the same terms as Apache Traffic Server itself. + +License: Apache-2.0 + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + . + http://www.apache.org/licenses/LICENSE-2.0 + . + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + . + On Debian systems, the full text of the e Apache License, Version 2.0 + can be found in the file `/usr/share/common-licenses/Apache-2.0'. + +Files: include/tscore/ink_rand.* +Copyright: 1997 - 2002, Makoto Matsumoto and Takuji Nishimura +License: BSD-3-clause + +Files: include/tscore/ink_resolver.h src/tscore/ink_res_init.cc src/tscore/ink_res_mkquery.cc +Copyright: 1983, 1987, 1989 The Regents of the University of California +License: BSD-3-clause + +License: BSD-3-clause + For the University of California part: + . + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + . + For the Internet Systems Consortium, Inc. part: + See Apache-2.0 + . + For the Digital Equipment Corporation part: + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies, and that + the name of Digital Equipment Corporation not be used in advertising or + publicity pertaining to distribution of the document or software without + specific, written prior permission. + . + THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + SOFTWARE. + +Files: src/tscore/ink_string.cc +Comment: For the strlcat, strlcpy in inktomi++/ink_string.cc +Copyright: 1998 Todd C. Miller +License: ISC + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + . + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Files: include/tscore/fastlz.h src/tscore/fastlz.c +Copyright: 2005-2007 Ariya Hidayat (ariya@kde.org) +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. diff --git a/docs b/docs new file mode 100644 index 00000000..356cb60e --- /dev/null +++ b/docs @@ -0,0 +1,9 @@ +LAYOUT +NOTICE +STATUS +REVIEWERS +README +README-EC2 +debian/README.Debian +debian/CONFIGURATION.Debian +debian/README.conf-remap.Debian diff --git a/gbp.conf b/gbp.conf new file mode 100644 index 00000000..b0d19e7a --- /dev/null +++ b/gbp.conf @@ -0,0 +1,8 @@ +[DEFAULT] +pristine-tar = True +builder=dpkg-buildpackage -i\.git -I.git +#cleaner=true + +[import-orig] +filter = [ '.gitignore', '.git', '.vscode' ] +merge = True diff --git a/gitlab-ci.yml b/gitlab-ci.yml new file mode 100644 index 00000000..5a4e7143 --- /dev/null +++ b/gitlab-ci.yml @@ -0,0 +1,102 @@ +stages: + - build + - lintian + - piuparts +# - reprotest + +.build: &build + before_script: + - apt-get update + - apt-get -qy upgrade + - apt-get -qy install devscripts autoconf automake adduser fakeroot sudo + - mk-build-deps -t "apt-get -y -o Debug::pkgProblemResolver=yes --no-install-recommends" -i -r + - adduser --disabled-password --gecos "" builduser + - chown -R builduser:builduser . + - chown builduser:builduser .. + stage: build + script: + - sudo -u builduser dpkg-buildpackage -b -rfakeroot + +.lintian: &lintian + before_script: + - apt-get update + - apt-get -y install lintian + stage: lintian + script: + - echo "performing general and experimental lintian checks" + - lintian --color always -EviIL +pedantic built/*.changes || true + - echo "performing ftp-master-rejects lintian checks" + - lintian --color always -viF built/*.changes + +.piuparts: &piuparts + stage: piuparts + services: + - docker:dind + before_script: + - export CHROOT_PATH=/tmp/debian-sid + - CONTAINER_ID=$(docker run --rm -d debian:sid sleep infinity) + - docker exec ${CONTAINER_ID} bash -c "apt-get update && apt-get -qy dist-upgrade && apt-get -qy install eatmydata" + - mkdir -p ${CHROOT_PATH} + - docker export ${CONTAINER_ID} | tar -C ${CHROOT_PATH} -xf - + - mknod -m 666 ${CHROOT_PATH}/dev/urandom c 1 9 + artifacts: + name: "$CI_BUILD_NAME" + expire_in: 180 day + paths: + - ./piuparts.log + when: always + script: + - piuparts --hard-link -e ${CHROOT_PATH} -l piuparts.log built/*.changes + +#.reprotest: &reprotest +# stage: reprotest +# before_script: +# - apt-get update +# - apt-get -qy dist-upgrade +# - eatmydata apt-get build-dep -y . +# artifacts: +# name: "$CI_BUILD_NAME" +# expire_in: 180 day +# paths: +# - ./reprotest.log +# when: always +# script: +# - export DEB_BUILD_OPTIONS=nocheck +# - eatmydata reprotest --min-cpus $(nproc --all) . -- null &> reprotest.log + +### End of generic stage declaration + +#build:testing: +# <<: *build +# image: debian:testing + +build:unstable: + <<: *build + image: debian:sid + artifacts: + name: "$CI_BUILD_NAME" + expire_in: 180 day + paths: + - built + when: always + after_script: + - mkdir built + - dcmd mv ../*.changes built/ + +lintian:unstable: + <<: *lintian + dependencies: + - build:unstable + image: debian:sid + +piuparts:unstable: + <<: *piuparts + dependencies: + - build:unstable + image: registry.salsa.debian.org/salsa-ci-team/images/piuparts + +#reprotest:unstable: +# <<: *reprotest +# dependencies: +# - build:unstable +# image: registry.salsa.debian.org/salsa-ci-team/images/reprotest diff --git a/not-installed b/not-installed new file mode 100644 index 00000000..b97cd0e7 --- /dev/null +++ b/not-installed @@ -0,0 +1,2 @@ +usr/man/man3 +usr/share/man diff --git a/patches/0001-Use-mcx16-on-x86-platforms-only.patch b/patches/0001-Use-mcx16-on-x86-platforms-only.patch new file mode 100644 index 00000000..4852f89c --- /dev/null +++ b/patches/0001-Use-mcx16-on-x86-platforms-only.patch @@ -0,0 +1,26 @@ +From: Aron Xu +Date: Tue, 2 Feb 2016 12:17:24 +0800 +Subject: Use -mcx16 on x86 platforms only + +--- + configure.ac | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/configure.ac ++++ b/configure.ac +@@ -1495,12 +1495,15 @@ CFLAGS="${__saved_CFLAGS}" + AC_LANG_POP + AC_SUBST(has_128bit_cas) + ++case "$host_cpu" in ++ amd64|x86_64|i*86) + AS_IF([test "x$has_128bit_cas" = "x1"], [ + AS_IF([test "x$ax_cv_c_compiler_vendor" != "xintel"], [ + TS_ADDTO(AM_CFLAGS, [-mcx16]) + TS_ADDTO(AM_CXXFLAGS, [-mcx16]) + ]) + ]) ++esac + + # Check for POSIX capabilities library. + # If we don't find it, disable checking for header. diff --git a/patches/0003-reproductible-build.patch b/patches/0003-reproductible-build.patch new file mode 100644 index 00000000..dc2cc8d9 --- /dev/null +++ b/patches/0003-reproductible-build.patch @@ -0,0 +1,22 @@ +Description: make the build reproducible +Author: Reiner Herrmann +Origin: other, https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833176 +Reviewed-by: Jean Baptiste Favre +Last-Update: 2016-11-18 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/configure.ac ++++ b/configure.ac +@@ -144,9 +144,9 @@ AC_ARG_WITH([build-number], + # + # Build environment + # +-build_person="`id -nu | sed -e 's/\\\\/\\\\\\\\/g'`" +-build_group="`id -ng | sed -e 's/\\\\/\\\\\\\\/g'`" +-build_machine="`uname -n | sed -e 's/\\\\/\\\\\\\\/g'`" ++build_person="root" ++build_group="root" ++build_machine="localhost" + AC_SUBST([build_machine]) + AC_SUBST([build_person]) + AC_SUBST([build_group]) diff --git a/patches/0006-fix-doc-build.patch b/patches/0006-fix-doc-build.patch new file mode 100644 index 00000000..29e76561 --- /dev/null +++ b/patches/0006-fix-doc-build.patch @@ -0,0 +1,63 @@ +Description: Make documentation build works outside of git repository + Current documentation build requires git and curl to get some stuff from the internet + This patch aims to delete those dependencies, forcing git branch to master, + and using Debian provided plantuml instead of downloading it from apache mirror +Author: Jean Baptiste Favre +Origin: other +Last-Update: 2019-01-03 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/doc/ext/traffic-server.py ++++ b/doc/ext/traffic-server.py +@@ -380,7 +380,7 @@ with open('../configure.ac', 'r') as f: + autoconf_version = '.'.join(match.group(1).split('.', 2)[:2] + ['x']) + + # get the current branch the local repository is on +-git_branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) ++git_branch = "master" + + + def make_github_link(name, rawtext, text, lineno, inliner, options={}, content=[]): +--- a/doc/Makefile.am ++++ b/doc/Makefile.am +@@ -61,7 +61,7 @@ I18NSPHINXOPTS = $(SPHINXOPTS) + # The PAPER setting variables requires recursive make variable expansion, which automake + # detects as non-portable. We bounce this through a shell script and do the expansion there. + SBUILD = PAPEROPT_a4="$(PAPEROPT_a4)" PAPEROPT_letter="$(PAPEROPT_letter)" PAPER="$(PAPER)" \ +- PLANTUML_JAR="$(shell ext/plantuml_fetch.sh | tail -1)" \ ++ PLANTUML_JAR="/usr/share/plantuml/plantuml.jar" \ + $(srcdir)/sbuild $(SPHINXBUILD) \ + -c $(srcdir) \ + $(ALLSPHINXOPTS) +--- a/doc/uml/Makefile.am ++++ b/doc/uml/Makefile.am +@@ -18,7 +18,7 @@ + + if BUILD_DOCS + images := $(patsubst %.uml,images/%.svg,$(wildcard *.uml)) +-PLANTUML_JAR := $(shell ../ext/plantuml_fetch.sh | tail -1) ++PLANTUML_JAR := /usr/share/plantuml/plantuml.jar + + all-am: jar-check $(images) + endif +@@ -39,7 +39,7 @@ latex: all-am + man: all-am + + images/%.svg : %.uml +- $(JAVA) -jar $(PLANTUML_JAR) -o images -tsvg $< ++ $(JAVA) -jar $(PLANTUML_JAR) -graphvizdot /usr/bin/dot -o images -tsvg $< + + clean-local: + rm -f images/*.svg +--- a/doc/manpages.py ++++ b/doc/manpages.py +@@ -33,6 +33,9 @@ man_pages = [ + ('appendices/command-line/traffic_top.en', 'traffic_top', u'Display Traffic Server statistics', None, '1'), + ('appendices/command-line/tsxs.en', 'tsxs', u'Traffic Server plugin tool', None, '1'), + ('appendices/command-line/traffic_via.en', 'traffic_via', u'Traffic Server Via header decoder', None, '1'), ++ ('appendices/command-line/traffic_layout.en', 'traffic_layout', u'Traffic Server sandbox management tool', None, '1'), ++ ('appendices/command-line/traffic_cache_tool.en', 'traffic_cache_tool', u'Traffic Server cache management tool', None, '1'), ++ ('appendices/command-line/traffic_wccp.en', 'traffic_wccp', u'Traffic Server WCCP client', None, '1'), + + ('admin-guide/files/cache.config.en', 'cache.config', u'Traffic Server cache configuration file', None, '5'), + ('admin-guide/files/hosting.config.en', 'hosting.config', u'Traffic Server domain hosting configuration file', None, '5'), diff --git a/patches/0008-fix-python-check-unused-dependencies.patch b/patches/0008-fix-python-check-unused-dependencies.patch new file mode 100644 index 00000000..ff100004 --- /dev/null +++ b/patches/0008-fix-python-check-unused-dependencies.patch @@ -0,0 +1,24 @@ +Description: Force python3 usage, add libfakeroot-sysv to blacklist +Author: Jean Baptiste Favre +Origin: other +Last-Update: 2018-09-24 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/tools/check-unused-dependencies ++++ b/tools/check-unused-dependencies +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/env python3 + + # Licensed to the Apache Software Foundation (ASF) under one or more + # contributor license agreements. See the NOTICE file distributed with +@@ -53,7 +53,8 @@ def get_dependencies(program): + 'libdl.so.', # Because we add -ldl to LIBS + 'libgcc_s.so.', + 'libm.so.', # Why does Libtool call ld with -lm? +- 'libpthread.so.' # Because we add -lpthread to LIBS ++ 'libpthread.so.', # Because we add -lpthread to LIBS ++ 'libfakeroot-sysv.so' + ])): + continue + diff --git a/patches/0009-fix-mysql-8-build.patch b/patches/0009-fix-mysql-8-build.patch new file mode 100644 index 00000000..b2615e37 --- /dev/null +++ b/patches/0009-fix-mysql-8-build.patch @@ -0,0 +1,23 @@ +Description: Fix build issue with MySQL 8 + The my_bool type is no longer used in MySQL source code. + Any third-party code that used this type to represent C + boolean variables should use the bool or int C type instead. +Author: Jean Baptiste Favre +Origin: other, https://bugs.launchpad.net/ubuntu/+source/trafficserver/+bug/1795362 +Forwarded: https://github.com/apache/trafficserver/pull/4360 +Applied-Upstream: https://github.com/apache/trafficserver/commit/05b30527974416768515506f69da338652c23260 +Reviewed-by: Jean Baptiste Favre +Last-Update: 2018-10-06 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/plugins/experimental/mysql_remap/mysql_remap.cc ++++ b/plugins/experimental/mysql_remap/mysql_remap.cc +@@ -187,7 +187,7 @@ TSPluginInit(int argc, const char *argv[ + my_data *data = (my_data *)malloc(1 * sizeof(my_data)); + + TSPluginRegistrationInfo info; +- my_bool reconnect = 1; ++ bool reconnect = 1; + + info.plugin_name = const_cast(PLUGIN_NAME); + info.vendor_name = const_cast("Apache Software Foundation"); diff --git a/patches/0011-fix-segfault.patch b/patches/0011-fix-segfault.patch new file mode 100644 index 00000000..d3db7fff --- /dev/null +++ b/patches/0011-fix-segfault.patch @@ -0,0 +1,25 @@ +Description: Fix Segmentation fault in ShowCache::handleCacheEvent +Author: zhang <15535135608@163.com> +Origin: upstream +Bug: https://github.com/apache/trafficserver/issues/4328 +Applied-Upstream: https://github.com/apache/trafficserver/commit/616eb10bfc35599a2c93ff30879d584a05ddf83e +Reviewed-by: Jean Baptiste Favre +Last-Update: 2018-10-17 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/iocore/cache/CachePages.cc ++++ b/iocore/cache/CachePages.cc +@@ -337,6 +337,13 @@ ShowCache::handleCacheEvent(int event, E + CacheHTTPInfoVector *vec = &(cache_vc->vector); + int alt_count = vec->count(); + if (alt_count) { ++ // check cache_vc->first_buf is NULL, response cache lookup busy. ++ if (cache_vc->first_buf == nullptr) { ++ cache_vc->do_io_close(-1); ++ CHECK_SHOW(show("

Cache Lookup Busy, please try again

\n")); ++ return complete(event, e); ++ } ++ + Doc *d = (Doc *)(cache_vc->first_buf->data()); + time_t t; + char tmpstr[4096]; diff --git a/patches/0012-fix-spelling-checks.patch b/patches/0012-fix-spelling-checks.patch new file mode 100644 index 00000000..db36cc6c --- /dev/null +++ b/patches/0012-fix-spelling-checks.patch @@ -0,0 +1,867 @@ +Description: Fix various speeling issues +Author: Jean Baptiste Favre +Forwarded: https://github.com/apache/trafficserver/pull/4750 +Applied-Upstream: https://github.com/apache/trafficserver/commit/af0ad4a1880a21743e98331855bb78e15d5406ef +Last-Update: 2019-01-03 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/doc/admin-guide/files/ip_allow.config.en.rst ++++ b/doc/admin-guide/files/ip_allow.config.en.rst +@@ -55,7 +55,7 @@ range with the lower and upper values eq + The value of ``method`` is a string which must consist of either HTTP method names separated by the + character '|' or the keyword literal ``ALL``. This keyword may omitted in which case it is treated + as if it were ``method=ALL``. Methods can also be specified by having multiple instances of the +-``method`` keyword, each specifiying a single method. E.g., ``method=GET|HEAD`` is the same as ++``method`` keyword, each specifying a single method. E.g., ``method=GET|HEAD`` is the same as + ``method=GET method=HEAD``. The method names are not validated which means non-standard method names + can be specified. + +@@ -104,7 +104,7 @@ If the entire subnet were to be denied, + + src_ip=123.45.6.0/24 action=ip_deny + +-The following example allows to any upstream servers:: ++The following example allows one to any upstream servers:: + + dest_ip=0.0.0.0-255.255.255.255 action=ip_allow + +--- a/doc/admin-guide/files/parent.config.en.rst ++++ b/doc/admin-guide/files/parent.config.en.rst +@@ -189,7 +189,7 @@ The following list shows the possible ac + - ``simple_retry`` - If the parent origin server returns a 404 response on a request + a new parent is selected and the request is retried. The number of retries is controlled + by ``max_simple_retries`` which is set to 1 by default. +- - ``unavailable_server_retry`` - If the parent returns a 503 response or if the reponse matches ++ - ``unavailable_server_retry`` - If the parent returns a 503 response or if the response matches + a list of http 5xx responses defined in ``unavailable_server_retry_responses``, the currently selected + parent is marked down and a new parent is selected to retry the request. The number of + retries is controlled by ``max_unavailable_server_retries`` which is set to 1 by default. +@@ -207,7 +207,7 @@ The following list shows the possible ac + + ``max_simple_retries`` + By default the value for ``max_simple_retries`` is 1. It may be set to any value in the range 1 to 5. +- If ``parent_retry`` is set to ``simple_retry`` or ``both`` a 404 reponse ++ If ``parent_retry`` is set to ``simple_retry`` or ``both`` a 404 response + from a parent origin server will cause the request to be retried using a new parent at most 1 to 5 + times as configured by ``max_simple_retries``. + +@@ -215,7 +215,7 @@ The following list shows the possible ac + + ``max_unavailable_server_retries`` + By default the value for ``max_unavailable_server_retries`` is 1. It may be set to any value in the range 1 to 5. +- If ``parent_retry`` is set to ``unavailable_server_retries`` or ``both`` a 503 reponse ++ If ``parent_retry`` is set to ``unavailable_server_retries`` or ``both`` a 503 response + by default or any http 5xx response listed in the list ``unavailable_server_retry_responses`` from a parent origin server will + cause the request to be retried using a new parent after first marking the current parent down. The request + will be retried at most 1 to 5 times as configured by ``max_unavailable_server_retries``. +--- a/doc/admin-guide/files/records.config.en.rst ++++ b/doc/admin-guide/files/records.config.en.rst +@@ -802,7 +802,7 @@ ip-resolve + ===== ====================================================================== + Value Description + ===== ====================================================================== +- ``0`` |TS| will buffer the request until the post body has been recieved and ++ ``0`` |TS| will buffer the request until the post body has been received and + then send the request to the origin server. + ``1`` Immediately return a ``100 Continue`` from |TS| without waiting for + the post body. +@@ -1608,7 +1608,7 @@ Proxy User Variables + connection=full Full user agent connection :ref:`protocol tags ` + ================== =============================================================== + +- Each paramater in the list must be separated by ``|`` or ``:``. For example, ``for|by=uuid|proto`` is ++ Each parameter in the list must be separated by ``|`` or ``:``. For example, ``for|by=uuid|proto`` is + a valid value for this variable. Note that the ``connection`` parameter is a non-standard extension to + RFC 7239. Also note that, while Traffic Server allows multiple ``by`` parameters for the same proxy, this + is prohibited by RFC 7239. Currently, for the ``host`` parameter to provide the original host from the +@@ -1636,7 +1636,7 @@ Proxy User Variables + information. + See :ts:cv:`proxy.config.http.server_ports` for information on how to enable Proxy Protocol on a port. + +- See :ref:`proxy-protocol` for more discussion on how |TS| tranforms the `Forwarded: header. ++ See :ref:`proxy-protocol` for more discussion on how |TS| transforms the `Forwarded: header. + + .. ts:cv:: CONFIG proxy.config.http.normalize_ae INT 1 + :reloadable: +@@ -2386,7 +2386,7 @@ DNS + + .. ts:cv:: CONFIG proxy.config.dns.resolv_conf STRING /etc/resolv.conf + +- Allows to specify which ``resolv.conf`` file to use for finding resolvers. While the format of this file must be the same as the ++ Allows one to specify which ``resolv.conf`` file to use for finding resolvers. While the format of this file must be the same as the + standard ``resolv.conf`` file, this option allows an administrator to manage the set of resolvers in an external configuration file, + without affecting how the rest of the operating system uses DNS. + +@@ -2404,7 +2404,7 @@ DNS + :reloadable: + :overridable: + +- Indicates whether to use SRV records for orgin server lookup. ++ Indicates whether to use SRV records for origin server lookup. + + .. ts:cv:: CONFIG proxy.config.dns.dedicated_thread INT 0 + +@@ -2637,7 +2637,7 @@ HostDB + Set the frequency (in seconds) to sync hostdb to disk. + + Note: hostdb is syncd to disk on a per-partition basis (of which there are 64). +- This means that the minumum time to sync all data to disk is :ts:cv:`proxy.config.cache.hostdb.sync_frequency` * 64 ++ This means that the minimum time to sync all data to disk is :ts:cv:`proxy.config.cache.hostdb.sync_frequency` * 64 + + Logging Configuration + ===================== +@@ -2951,7 +2951,7 @@ Diagnostic Logging Configuration + + .. ts:cv:: CONFIG proxy.config.diags.debug.tags STRING http|dns + +- Each |TS| `diag` and `debug` level message is annotated with a subsytem tag. This configuration ++ Each |TS| `diag` and `debug` level message is annotated with a subsystem tag. This configuration + contains an anchored regular expression that filters the messages based on the tag. The + expressions are prefix matched which creates an implicit ``.*`` at the end. Therefore the default + value ``http|dns`` will match tags such as ``http``, ``http_hdrs``, ``dns``, and ``dns_recv``. +@@ -2959,7 +2959,7 @@ Diagnostic Logging Configuration + Some commonly used debug tags are: + + ============ ===================================================== +- Tag Subsytem usage ++ Tag Subsystem usage + ============ ===================================================== + dns DNS query resolution + http_hdrs Logs the headers for HTTP requests and responses +@@ -3115,7 +3115,7 @@ SSL Termination + .. ts:cv:: CONFIG proxy.config.ssl.client.groups_list STRING + + Configures the list of supported groups provided by OpenSSL which +- |TS| will use for the "key_share" and "supported groups" extention ++ |TS| will use for the "key_share" and "supported groups" extension + of TLSv1.3 connections. The value is a colon separated list of + group NIDs or names, for example "P-521:P-384:P-256". For + instructions, see "Groups" section of `TLS1.3 - OpenSSLWiki `_. +@@ -3254,7 +3254,7 @@ SSL Termination + ``0`` Disables the session cache entirely. + ``1`` Enables the session cache using OpenSSL's implementation. + ``2`` Default. Enables the session cache using |TS|'s implementation. This +- implentation should perform much better than the OpenSSL ++ implementation should perform much better than the OpenSSL + implementation. + ===== ====================================================================== + +--- a/doc/admin-guide/files/remap.config.en.rst ++++ b/doc/admin-guide/files/remap.config.en.rst +@@ -415,7 +415,7 @@ Acl Filters + + Acl filters can be created to control access of specific remap lines. The markup + is very similar to that of :file:`ip_allow.config`, with slight changes to +-accomodate remap markup ++accommodate remap markup + + Examples + -------- +--- a/doc/admin-guide/files/storage.config.en.rst ++++ b/doc/admin-guide/files/storage.config.en.rst +@@ -92,7 +92,7 @@ which will effectively clear most of the + reboot causes the path names to change. + + The :arg:`id` option can be used to create a fixed string that an administrator can use to keep the +-assignment table consistent by maintaing the mapping from physical device to base string even in the presence of hardware changes and failures. ++assignment table consistent by maintaining the mapping from physical device to base string even in the presence of hardware changes and failures. + + Examples + ======== +--- a/doc/appendices/command-line/traffic_ctl.en.rst ++++ b/doc/appendices/command-line/traffic_ctl.en.rst +@@ -255,7 +255,7 @@ traffic_ctl host + .. program:: traffic_ctl host + .. option:: status HOSTNAME [HOSTNAME ...] + +- Get the current status of the hosts used in parent.config as a next hop in a multi-tiered cache heirarchy. The value 0 or 1 is returned indicating that the host is marked as down '0' or marked as up '1'. If a host is marked as down, it will not be used as the next hop parent, another host marked as up will be chosen. ++ Get the current status of the hosts used in parent.config as a next hop in a multi-tiered cache hierarchy. The value 0 or 1 is returned indicating that the host is marked as down '0' or marked as up '1'. If a host is marked as down, it will not be used as the next hop parent, another host marked as up will be chosen. + + .. program:: traffic_ctl host + .. option:: down --time seconds HOSTNAME [HOSTNAME ...] +--- a/doc/developer-guide/api/functions/TSCacheRemove.en.rst ++++ b/doc/developer-guide/api/functions/TSCacheRemove.en.rst +@@ -41,4 +41,4 @@ the cache calls :arg:`contp` back with t + In both of these callbacks, the user (:arg:`contp`) does not have to do + anything. The user does not get any vconnection from the cache, since + no data needs to be transferred. When the cache calls :arg:`contp` back with +-:data:`TS_EVENT_CACHE_REMOVE`, the remove has already been commited. ++:data:`TS_EVENT_CACHE_REMOVE`, the remove has already been committed. +--- a/doc/developer-guide/api/functions/TSContSchedule.en.rst ++++ b/doc/developer-guide/api/functions/TSContSchedule.en.rst +@@ -32,7 +32,7 @@ Description + =========== + + Schedules :arg:`contp` to run :arg:`delay` milliseconds in the future. This is approximate. The delay +-will be at least :arg:`delay` but possibly more. Resultions finer than roughly 5 milliseconds will ++will be at least :arg:`delay` but possibly more. Resolutions finer than roughly 5 milliseconds will + not be effective. :arg:`contp` is required to have a mutex, which is provided to + :func:`TSContCreate`. + +--- a/doc/developer-guide/api/functions/TSHttpConnectWithPluginId.en.rst ++++ b/doc/developer-guide/api/functions/TSHttpConnectWithPluginId.en.rst +@@ -79,7 +79,7 @@ virtual connection. + + The combination of :arg:`tag` and :arg:`id` is intended to enable correlation + in log post processing. The :arg:`tag` identifies the connection as related +-to the plugin and the :arg:`id` can be used in conjuction with plugin ++to the plugin and the :arg:`id` can be used in conjunction with plugin + generated logs to correlate the log records. + + Notes +--- a/doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst ++++ b/doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst +@@ -43,7 +43,7 @@ Description + + Some of the values that are set in :file:`records.config` can be changed for a + specific transaction. It is important to note that these functions change the +-configuration values stored for the transation, which is not quite the same as ++configuration values stored for the transaction, which is not quite the same as + changing the actual operating values of the transaction. The critical effect is + the value must be changed before it is used by the transaction - after that, + changes will not have any effect. +--- a/doc/developer-guide/api/functions/TSHttpTxnErrorBodySet.en.rst ++++ b/doc/developer-guide/api/functions/TSHttpTxnErrorBodySet.en.rst +@@ -36,4 +36,4 @@ Description + Note that both string arguments must be allocated with :c:func:`TSmalloc` or + :c:func:`TSstrdup`. The :arg:`mimetype` is optional, and if not provided it + defaults to :literal:`text/html`. Sending an empty string would prevent setting +-a content type header (but that is not adviced). ++a content type header (but that is not advised). +--- a/doc/developer-guide/api/functions/TSHttpTxnMilestoneGet.en.rst ++++ b/doc/developer-guide/api/functions/TSHttpTxnMilestoneGet.en.rst +@@ -138,7 +138,7 @@ is successful. + + .. macro:: TS_MILESTONE_LAST_ENTRY + +- A psuedo index which is set to be one more than the last valid index. This is useful for looping over the data. ++ A pseudo index which is set to be one more than the last valid index. This is useful for looping over the data. + + + * The server connect times predate the transmission of the :literal:`SYN` +--- a/doc/developer-guide/api/functions/TSHttpTxnServerIntercept.en.rst ++++ b/doc/developer-guide/api/functions/TSHttpTxnServerIntercept.en.rst +@@ -53,7 +53,7 @@ The response from the plugin is cached s + caching rules. Should the plugin wish the response not be cached, the plugin + must use appropriate HTTP response headers to prevent caching. The primary + purpose of :func:`TSHttpTxnServerIntercept` is allow plugins to provide gateways +-to other protocols or to allow to plugin to its own transport for the next hop ++to other protocols or to allow one to plugin to its own transport for the next hop + to the server. :func:`TSHttpTxnServerIntercept` overrides parent cache + configuration. + +--- a/doc/developer-guide/api/functions/TSIOBufferReader.en.rst ++++ b/doc/developer-guide/api/functions/TSIOBufferReader.en.rst +@@ -59,7 +59,7 @@ has two very important consequences -- + * Conversely keeping a reader around unused will pin the buffer data in memory. This can be useful or harmful. + + A buffer has a fixed amount of possible readers (currently 5) which is determined at compile +-time. Reader allocation is fast and cheap until this maxium is reached at which point it fails. ++time. Reader allocation is fast and cheap until this maximum is reached at which point it fails. + + :func:`TSIOBufferReaderAlloc` allocates a reader for the IO buffer :arg:`bufp`. This should only be + called on a newly allocated buffer. If not the location of the reader in the buffer will be +--- a/doc/developer-guide/api/functions/TSSslContext.en.rst ++++ b/doc/developer-guide/api/functions/TSSslContext.en.rst +@@ -36,11 +36,11 @@ Description + =========== + + :func:`TSSslContextFindByName` searches for a SSL server context +-created from :file:`ssl_multicert.config`, matching against the ++created from :file:`ssl_multicert.config`, matchingg against the + server :arg:`name`. + + :func:`TSSslContextFindByAddr` searches for a SSL server context +-created from :file:`ssl_multicert.config` matchin against the server ++created from :file:`ssl_multicert.config` matching against the server + :arg:`address`. + + +--- a/doc/developer-guide/api/functions/TSSslSession.en.rst ++++ b/doc/developer-guide/api/functions/TSSslSession.en.rst +@@ -46,7 +46,7 @@ The functions also work with the :type:` + + These functions perform the appropriate locking on the session cache to avoid errors. + +-The :func:`TSSslSessionGet` and :func:`TSSslSessionGetBuffer` functions retreive the :type:`TSSslSession` object that is identifed by the ++The :func:`TSSslSessionGet` and :func:`TSSslSessionGetBuffer` functions retrieve the :type:`TSSslSession` object that is identifed by the + :type:`TSSslSessionID` object. If there is no matching sesion object, :func:`TSSslSessionGet` returns NULL and :func:`TSSslSessionGetBuffer` + returns 0. + +--- a/doc/developer-guide/api/functions/TSStat.en.rst ++++ b/doc/developer-guide/api/functions/TSStat.en.rst +@@ -46,9 +46,9 @@ Description + + A plugin statistic is created by :func:`TSStatCreate`. The :arg:`name` must be globally unique and + should follow the standard dotted tag form. To avoid collisions and for easy of use the first tag +-should be the plugin name or something easily derived from it. Currently only integers are suppored ++should be the plugin name or something easily derived from it. Currently only integers are supported + therefore :arg:`type` must be :macro:`TS_RECORDDATATYPE_INT`. The return value is the index of the +-statistic. In general thsi should work but if it doesn't it will :code:`assert`. In particular, ++statistic. In general this should work but if it doesn't it will :code:`assert`. In particular, + creating the same statistic twice will fail in this way, which can happen if statistics are created + as part of or based on configuration files and |TS| is reloaded. + +--- a/doc/developer-guide/api/functions/TSVConnReenable.en.rst ++++ b/doc/developer-guide/api/functions/TSVConnReenable.en.rst +@@ -32,7 +32,7 @@ Description + =========== + + Reenable the SSL connection :arg:`svc`. If a plugin hook is called, ATS +-processing on that connnection will not resume until this is invoked for that ++processing on that connection will not resume until this is invoked for that + connection. + + If the server is running OpenSSL 1.0.1 with the appropraite patch installed or +--- a/doc/developer-guide/api/functions/TSfwrite.en.rst ++++ b/doc/developer-guide/api/functions/TSfwrite.en.rst +@@ -44,4 +44,4 @@ The behavior is undefined if length is g + Return Value + ============ + +-Returns the number of bytes actually written, or -1 if an error occured. ++Returns the number of bytes actually written, or -1 if an error occurred. +--- a/iocore/cache/CacheHosting.cc ++++ b/iocore/cache/CacheHosting.cc +@@ -715,7 +715,7 @@ ConfigVolumes::BuildListFromString(char + // added by YTS Team, yamsat for bug id 59632 + total += size; + if (size > 100 || total > 100) { +- err = "Total volume size added upto more than 100 percent, No volumes created"; ++ err = "Total volume size added up to more than 100 percent, No volumes created"; + break; + } + // ends here +--- a/iocore/eventsystem/IOBuffer.cc ++++ b/iocore/eventsystem/IOBuffer.cc +@@ -179,7 +179,7 @@ MIOBuffer::puts(char *s, int64_t len) + } + if (!*pb || *pb == '\n') { + int64_t n = (int64_t)(pb - s); +- memcpy(end(), s, n + 1); // Upto and including '\n' ++ memcpy(end(), s, n + 1); // Up to and including '\n' + end()[n + 1] = 0; + fill(n + 1); + return n + 1; +--- a/plugins/esi/lib/EsiProcessor.cc ++++ b/plugins/esi/lib/EsiProcessor.cc +@@ -305,7 +305,7 @@ EsiProcessor::process(const char *&data, + + /* FAILURE CACHE */ + FailureData *data = static_cast(pthread_getspecific(threadKey)); +- _debugLog("plugin_esi_failureInfo", "[%s]Fetched data related to thread specfic %p", __FUNCTION__, data); ++ _debugLog("plugin_esi_failureInfo", "[%s]Fetched data related to thread specific %p", __FUNCTION__, data); + + for (iter = try_iter->attempt_nodes.begin(); iter != try_iter->attempt_nodes.end(); ++iter) { + if ((iter->type == DocNode::TYPE_INCLUDE) || iter->type == DocNode::TYPE_SPECIAL_INCLUDE) { +@@ -342,7 +342,7 @@ EsiProcessor::process(const char *&data, + } + } + if (attempt_succeeded) { +- _debugLog(_debug_tag, "[%s] attempt section succeded; using attempt section", __FUNCTION__); ++ _debugLog(_debug_tag, "[%s] attempt section succeeded; using attempt section", __FUNCTION__); + _node_list.splice(try_iter->pos, try_iter->attempt_nodes); + } else { + _debugLog(_debug_tag, "[%s] attempt section errored; trying except section", __FUNCTION__); +@@ -436,7 +436,7 @@ EsiProcessor::flush(string &data, int &o + + /* FAILURE CACHE */ + FailureData *fdata = static_cast(pthread_getspecific(threadKey)); +- _debugLog("plugin_esi_failureInfo", "[%s]Fetched data related to thread specfic %p", __FUNCTION__, fdata); ++ _debugLog("plugin_esi_failureInfo", "[%s]Fetched data related to thread specific %p", __FUNCTION__, fdata); + + for (iter = try_iter->attempt_nodes.begin(); iter != try_iter->attempt_nodes.end(); ++iter) { + if ((iter->type == DocNode::TYPE_INCLUDE) || iter->type == DocNode::TYPE_SPECIAL_INCLUDE) { +@@ -473,7 +473,7 @@ EsiProcessor::flush(string &data, int &o + } + } + if (attempt_succeeded) { +- _debugLog(_debug_tag, "[%s] attempt section succeded; using attempt section", __FUNCTION__); ++ _debugLog(_debug_tag, "[%s] attempt section succeeded; using attempt section", __FUNCTION__); + _n_prescanned_nodes = _n_prescanned_nodes + try_iter->attempt_nodes.size(); + _node_list.splice(try_iter->pos, try_iter->attempt_nodes); + } else { +--- a/plugins/esi/lib/Variables.cc ++++ b/plugins/esi/lib/Variables.cc +@@ -437,18 +437,18 @@ Variables::_parseDictVariable(const std: + for (int i = 0; i < (var_size - 1); ++i) { + if (variable[i] == '{') { + if (paranth_index != -1) { +- _debugLog(_debug_tag, "[%s] Cannot have multiple paranthesis in dict variable [%.*s]", __FUNCTION__, var_size, var_ptr); ++ _debugLog(_debug_tag, "[%s] Cannot have multiple parenthesis in dict variable [%.*s]", __FUNCTION__, var_size, var_ptr); + return false; + } + paranth_index = i; + } + if (variable[i] == '}') { +- _debugLog(_debug_tag, "[%s] Cannot have multiple paranthesis in dict variable [%.*s]", __FUNCTION__, var_size, var_ptr); ++ _debugLog(_debug_tag, "[%s] Cannot have multiple parenthesis in dict variable [%.*s]", __FUNCTION__, var_size, var_ptr); + return false; + } + } + if (paranth_index == -1) { +- _debugLog(_debug_tag, "[%s] Could not find opening paranthesis in variable [%.*s]", __FUNCTION__, var_size, var_ptr); ++ _debugLog(_debug_tag, "[%s] Could not find opening parenthesis in variable [%.*s]", __FUNCTION__, var_size, var_ptr); + return false; + } + if (paranth_index == 0) { +--- a/plugins/experimental/collapsed_forwarding/collapsed_forwarding.cc ++++ b/plugins/experimental/collapsed_forwarding/collapsed_forwarding.cc +@@ -353,7 +353,7 @@ TSRemapInit(TSRemapInterface * /* api_in + TSError("Cannot initialize %s as both global and remap plugin", DEBUG_TAG); + return TS_ERROR; + } else { +- TSDebug(DEBUG_TAG, "plugin is succesfully initialized for remap"); ++ TSDebug(DEBUG_TAG, "plugin is successfully initialized for remap"); + return TS_SUCCESS; + } + } +--- a/plugins/experimental/fq_pacing/fq_pacing.c ++++ b/plugins/experimental/fq_pacing/fq_pacing.c +@@ -118,7 +118,7 @@ TSRemapInit(TSRemapInterface *api_info, + return TS_ERROR; + } + +- TSDebug(PLUGIN_NAME, "plugin is succesfully initialized"); ++ TSDebug(PLUGIN_NAME, "plugin is successfully initialized"); + return TS_SUCCESS; + } + +--- a/plugins/experimental/header_normalize/header_normalize.cc ++++ b/plugins/experimental/header_normalize/header_normalize.cc +@@ -158,7 +158,7 @@ TSRemapInit(TSRemapInterface *api_info, + return TS_ERROR; + } + buildHdrMap(); +- TSDebug(PLUGIN_NAME, "plugin is succesfully initialized"); ++ TSDebug(PLUGIN_NAME, "plugin is successfully initialized"); + return TS_SUCCESS; + } + +--- a/plugins/experimental/prefetch/plugin.cc ++++ b/plugins/experimental/prefetch/plugin.cc +@@ -202,7 +202,7 @@ evaluate(const String &v) + } else { + stmt.assign(v); + } +- PrefetchDebug("statement: '%s', formating length: %zu", stmt.c_str(), len); ++ PrefetchDebug("statement: '%s', formatting length: %zu", stmt.c_str(), len); + + int result = 0; + pos = stmt.find_first_of("+-"); +--- a/plugins/experimental/uri_signing/parse.c ++++ b/plugins/experimental/uri_signing/parse.c +@@ -142,7 +142,7 @@ validate_jws(cjose_jws_t *jws, struct co + PluginDebug("Initial validation of JWT failed for %16p", jws); + goto jwt_fail; + } +- TimerDebug("inital validation of jwt"); ++ TimerDebug("initial validation of jwt"); + + cjose_header_t *hdr = cjose_jws_get_protected(jws); + TimerDebug("getting header of jws"); +--- a/plugins/experimental/uri_signing/uri_signing.c ++++ b/plugins/experimental/uri_signing/uri_signing.c +@@ -46,7 +46,7 @@ TSRemapInit(TSRemapInterface *api_info, + return TS_ERROR; + } + +- TSDebug(PLUGIN_NAME, "plugin is succesfully initialized"); ++ TSDebug(PLUGIN_NAME, "plugin is successfully initialized"); + return TS_SUCCESS; + } + +--- a/plugins/experimental/url_sig/url_sig.c ++++ b/plugins/experimental/url_sig/url_sig.c +@@ -98,7 +98,7 @@ TSRemapInit(TSRemapInterface *api_info, + return TS_ERROR; + } + +- TSDebug(PLUGIN_NAME, "plugin is succesfully initialized"); ++ TSDebug(PLUGIN_NAME, "plugin is successfully initialized"); + return TS_SUCCESS; + } + +--- a/plugins/generator/generator.cc ++++ b/plugins/generator/generator.cc +@@ -609,7 +609,7 @@ GeneratorTxnHook(TSCont contp, TSEvent e + TSReleaseAssert(TSHttpTxnCacheLookupStatusGet(arg.txn, &status) == TS_SUCCESS); + if (status != TS_CACHE_LOOKUP_HIT_FRESH) { + // This transaction is going to be a cache miss, so intercept it. +- VDEBUG("intercepting orgin server request for txn=%p", arg.txn); ++ VDEBUG("intercepting origin server request for txn=%p", arg.txn); + TSHttpTxnServerIntercept(TSContCreate(GeneratorInterceptionHook, TSMutexCreate()), arg.txn); + } + +--- a/plugins/header_rewrite/header_rewrite.cc ++++ b/plugins/header_rewrite/header_rewrite.cc +@@ -330,7 +330,7 @@ TSPluginInit(int argc, const char *argv[ + // just appended to the configurations. + TSDebug(PLUGIN_NAME, "Loading global configuration file %s", argv[i]); + if (conf->parse_config(argv[i], TS_HTTP_READ_RESPONSE_HDR_HOOK)) { +- TSDebug(PLUGIN_NAME, "Succesfully loaded global config file %s", argv[i]); ++ TSDebug(PLUGIN_NAME, "Successfully loaded global config file %s", argv[i]); + got_config = true; + } else { + TSError("[header_rewrite] failed to parse configuration file %s", argv[i]); +@@ -401,7 +401,7 @@ TSRemapNewInstance(int argc, char *argv[ + delete conf; + return TS_ERROR; + } else { +- TSDebug(PLUGIN_NAME, "Succesfully loaded remap config file %s", argv[i]); ++ TSDebug(PLUGIN_NAME, "Successfully loaded remap config file %s", argv[i]); + } + } + +@@ -470,6 +470,6 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, T + rule = rule->next; + } + +- TSDebug(PLUGIN_NAME_DBG, "Returing from TSRemapDoRemap with status: %d", rval); ++ TSDebug(PLUGIN_NAME_DBG, "Returning from TSRemapDoRemap with status: %d", rval); + return rval; + } +--- a/plugins/s3_auth/s3_auth.cc ++++ b/plugins/s3_auth/s3_auth.cc +@@ -871,7 +871,7 @@ event_handler(TSCont cont, TSEvent event + } + + if (TS_HTTP_STATUS_OK == status) { +- TSDebug(PLUGIN_NAME, "Succesfully signed the AWS S3 URL"); ++ TSDebug(PLUGIN_NAME, "Successfully signed the AWS S3 URL"); + } else { + TSDebug(PLUGIN_NAME, "Failed to sign the AWS S3 URL, status = %d", status); + TSHttpTxnStatusSet(txnp, status); +--- a/src/traffic_cache_tool/CacheTool.cc ++++ b/src/traffic_cache_tool/CacheTool.cc +@@ -867,7 +867,7 @@ Span::updateHeader() + zret.push(0, errno, "Failed to update span - ", strerror(errno)); + } + } else { +- std::cout << "Writing not enabled, no updates perfomed" << std::endl; ++ std::cout << "Writing not enabled, no updates performed" << std::endl; + } + return zret; + } +--- a/src/traffic_crashlog/traffic_crashlog.cc ++++ b/src/traffic_crashlog/traffic_crashlog.cc +@@ -198,7 +198,7 @@ main(int /* argc ATS_UNUSED */, const ch + mgmterr = TSInit(nullptr, (TSInitOptionT)(TS_MGMT_OPT_NO_EVENTS | TS_MGMT_OPT_NO_SOCK_TESTS)); + if (mgmterr != TS_ERR_OKAY) { + char *msg = TSGetErrorMessage(mgmterr); +- Warning("failed to intialize management API: %s", msg); ++ Warning("failed to initialize management API: %s", msg); + TSfree(msg); + } + +--- a/src/traffic_logstats/logstats.cc ++++ b/src/traffic_logstats/logstats.cc +@@ -1828,7 +1828,7 @@ process_file(int in_fd, off_t offset, un + unsigned second_read_size = sizeof(LogBufferHeader) - first_read_size; + nread = read(in_fd, &buffer[first_read_size], second_read_size); + if (!nread || EOF == nread) { +- Debug("logstats", "Second read of header failed (attemped %d bytes at offset %d, got nothing), errno=%d.", second_read_size, ++ Debug("logstats", "Second read of header failed (attempted %d bytes at offset %d, got nothing), errno=%d.", second_read_size, + first_read_size, errno); + return 1; + } +--- a/src/traffic_manager/traffic_manager.cc ++++ b/src/traffic_manager/traffic_manager.cc +@@ -142,7 +142,7 @@ rotateLogs() + if (kill(tspid, SIGUSR2) != 0) { + mgmt_log("Could not send SIGUSR2 to TS: %s", strerror(errno)); + } else { +- mgmt_log("Succesfully sent SIGUSR2 to TS!"); ++ mgmt_log("Successfully sent SIGUSR2 to TS!"); + } + } + } +--- a/src/traffic_server/CoreUtils.h ++++ b/src/traffic_server/CoreUtils.h +@@ -44,7 +44,7 @@ + #define PC_REGNUM 12 /* Contains program counter EIP */ + #define FP_REGNUM 5 /* Virtual frame pointer EBP */ + #define NO_OF_ARGS \ +- 10 /* The argument depth upto which we would be looking into \ ++ 10 /* The argument depth up to which we would be looking into \ + the stack */ + + // contains local and in registers, frame pointer, and stack base +@@ -63,7 +63,7 @@ struct core_stack_state { + #include + + #define NO_OF_ARGS \ +- 10 /* The argument depth upto which we would be looking into \ ++ 10 /* The argument depth up to which we would be looking into \ + the stack */ + + // contains local and in registers, frame pointer, and stack base +--- a/src/traffic_server/InkAPITest.cc ++++ b/src/traffic_server/InkAPITest.cc +@@ -4225,7 +4225,7 @@ REGRESSION_TEST(SDK_API_TSHttpHdr)(Regre + SDK_RPRINT(test, "TSHttpHdrUrlSet&Get", "TestCase1", TC_FAIL, "TSHttpHdrUrlSet returns TS_ERROR"); + } else { + if (TSHttpHdrUrlGet(bufp1, hdr_loc1, &url_loc_Get) != TS_SUCCESS) { +- SDK_RPRINT(test, "TSHttpHdrUrlSet&Get", "TestCase1", TC_FAIL, "TSHttpHdrUrlGet retuns TS_ERROR"); ++ SDK_RPRINT(test, "TSHttpHdrUrlSet&Get", "TestCase1", TC_FAIL, "TSHttpHdrUrlGet returns TS_ERROR"); + } else { + if (url_loc == url_loc_Get) { + SDK_RPRINT(test, "TSHttpHdrUrlSet&Get", "TestCase1", TC_PASS, "ok"); +--- a/src/tscore/ArgParser.cc ++++ b/src/tscore/ArgParser.cc +@@ -154,7 +154,7 @@ ArgParser::parse(const char **argv) + }; + // if there is anything left, then output usage + if (!args.empty()) { +- std::string msg = "Unkown command, option or args:"; ++ std::string msg = "Unknown command, option or args:"; + for (auto it : args) { + msg = msg + " '" + it + "'"; + } +--- a/iocore/net/Socks.cc ++++ b/iocore/net/Socks.cc +@@ -694,7 +694,7 @@ socks5PasswdAuthHandler(int event, unsig + // NEC thinks it is 5 RFC seems to indicate 1. + switch (p[1]) { + case 0: +- Debug("Socks", "Username/Passwd succeded"); ++ Debug("Socks", "Username/Passwd succeeded"); + *h_ptr = nullptr; + break; + +--- a/iocore/net/UnixUDPNet.cc ++++ b/iocore/net/UnixUDPNet.cc +@@ -628,7 +628,7 @@ UDPNetProcessor::CreateUDPSocket(int *re + } + + if ((res = safe_getsockname(fd, &local_addr.sa, &local_addr_len)) < 0) { +- Debug("udpnet", "CreateUdpsocket: getsockname didnt' work"); ++ Debug("udpnet", "CreateUdpsocket: getsockname didn't work"); + goto HardError; + } + } +--- a/mgmt/Rollback.cc ++++ b/mgmt/Rollback.cc +@@ -133,7 +133,7 @@ Rollback::Rollback(const char *fileName_ + mgmt_log("[RollBack::Rollback] Automatic Rollback to prior version failed for %s : %s\n", fileName, strerror(errno)); + needZeroLength = true; + } else { +- mgmt_log("[RollBack::Rollback] Automatic Rollback to version succeded for %s\n", fileName, strerror(errno)); ++ mgmt_log("[RollBack::Rollback] Automatic Rollback to version succeeded for %s\n", fileName, strerror(errno)); + needZeroLength = false; + highestSeen--; + // Since we've made the highestVersion active +--- a/proxy/logging/LogBuffer.cc ++++ b/proxy/logging/LogBuffer.cc +@@ -270,7 +270,7 @@ LogBuffer::checkout_write(size_t *write_ + } + + if (switch_state(old_s, new_s)) { +- // we succeded in setting the new state ++ // we succeeded in setting the new state + break; + } + } +--- a/proxy/logging/LogObject.cc ++++ b/proxy/logging/LogObject.cc +@@ -409,7 +409,7 @@ LogObject::_checkout_write(size_t *write + + switch (result_code) { + case LogBuffer::LB_OK: +- // checkout succeded ++ // checkout succeeded + retry = false; + break; + +--- a/doc/admin-guide/plugins/header_rewrite.en.rst ++++ b/doc/admin-guide/plugins/header_rewrite.en.rst +@@ -330,7 +330,7 @@ The data that can be checked is :: + %{INBOUND:REMOTE-PORT} The client port for the connection. + %{INBOUND:TLS} The TLS protocol if the connection is over TLS, otherwise the empty string. + %{INBOUND:H2} The string "h2" if the connection is HTTP/2, otherwise the empty string. +- %{INBOUND:IPV4} The string "ipv4" if the connection is IPv4, otherwise the emtpy string. ++ %{INBOUND:IPV4} The string "ipv4" if the connection is IPv4, otherwise the empty string. + %{INBOUND:IPV6} The string "ipv6" if the connection is IPv6, otherwise the empty string. + %{INBOUND:IP-FAMILY} The IP family, either "ipv4" or "ipv6". + %{INBOUND:STACK} The full protocol stack separated by ','. +@@ -827,7 +827,7 @@ Variable Description + % The TLS protocol for the inbound connection if it is over TLS, otherwise the + empty string. + % The string "h2" if the inbound connection is HTTP/2, otherwise the empty string. +-% The string "ipv4" if the inbound connection is IPv4, otherwise the emtpy string. ++% The string "ipv4" if the inbound connection is IPv4, otherwise the empty string. + % The string "ipv6" if the inbound connection is IPv6, otherwise the empty string. + % The IP family of the inbound connection (either "ipv4" or "ipv6"). + % The full protocol stack of the inbound connection separated by ','. +--- a/iocore/dns/SplitDNS.cc ++++ b/iocore/dns/SplitDNS.cc +@@ -341,7 +341,7 @@ SplitDNSRecord::ProcessDNSHosts(char *va + if (tmp - current > (MAXDNAME - 1)) { + return "DNS server name (ip) is too long"; + } else if (tmp - current == 0) { +- return "server string is emtpy"; ++ return "server string is empty"; + } + *tmp = 0; + } +--- a/lib/records/RecHttp.cc ++++ b/lib/records/RecHttp.cc +@@ -410,7 +410,7 @@ HttpProxyPort::processOptions(const char + if (in_ip_set_p && m_family != m_inbound_ip.family()) { + std::string_view iname{ats_ip_family_name(m_inbound_ip.family())}; + std::string_view fname{ats_ip_family_name(m_family)}; +- Warning("Invalid port descriptor '%s' - the inbound adddress family [%.*s] is not the same type as the explicit family value " ++ Warning("Invalid port descriptor '%s' - the inbound address family [%.*s] is not the same type as the explicit family value " + "[%.*s]", + opts, static_cast(iname.size()), iname.data(), static_cast(fname.size()), fname.data()); + zret = false; +--- a/proxy/ParentSelection.cc ++++ b/proxy/ParentSelection.cc +@@ -491,7 +491,7 @@ ParentRecord::ProcessParents(char *val, + errPtr = "Parent hostname is too long"; + goto MERROR; + } else if (tmp - current == 0) { +- errPtr = "Parent string is emtpy"; ++ errPtr = "Parent string is empty"; + goto MERROR; + } + // Update the pRecords +--- a/src/tscore/HostLookup.cc ++++ b/src/tscore/HostLookup.cc +@@ -50,7 +50,7 @@ domaincmp(const char *hostname, const ch + const char *host_cur = hostname + strlen(hostname); + const char *domain_cur = domain + strlen(domain); + +- // Check to see if were passed emtpy stings for either ++ // Check to see if were passed empty stings for either + // argument. Empty strings do not match anything + // + if (domain_cur == domain || host_cur == hostname) { +@@ -380,7 +380,7 @@ charIndex::Insert(const char *match_data + + // Check to see if are at the level we supposed be at + if (*(match_data + 1) == '\0') { +- // The slot should always be emtpy, no duplicate ++ // The slot should always be empty, no duplicate + // keys are allowed + ink_assert(cur->branch_array[index] == nullptr); + cur->branch_array[index] = toInsert; +--- a/build/pkg.m4 ++++ b/build/pkg.m4 +@@ -53,7 +53,7 @@ fi[]dnl + # to PKG_CHECK_MODULES(), but does not set variables or print errors. + # + # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +-# only at the first occurence in configure.ac, so if the first place ++# only at the first occurrence in configure.ac, so if the first place + # it's called might be skipped (such as if it is within an "if", you + # have to call PKG_CHECK_EXISTS manually + # -------------------------------------------------------------- +--- a/doc/admin-guide/files/logging.yaml.en.rst ++++ b/doc/admin-guide/files/logging.yaml.en.rst +@@ -258,7 +258,7 @@ inconsequentially variable parameters th + reporting from log analyzers. + + Multiple query parameters may be listed, separated by spaces, though only the +-first occurence of each will be wiped from the query string if any individual ++first occurrence of each will be wiped from the query string if any individual + parameter appears more than once in the URL. + + .. _admin-custom-logs-logs: +--- a/include/tscpp/util/TextView.h ++++ b/include/tscpp/util/TextView.h +@@ -264,7 +264,7 @@ public: + self_type prefix(size_t n) const; + /// Convenience overload to avoid ambiguity for literal numbers. + self_type prefix(int n) const; +- /** Get the prefix delimited by the first occurence of the character @a c. ++ /** Get the prefix delimited by the first occurrence of the character @a c. + + If @a c is not found the entire view is returned. + The delimiter character is not included in the returned view. +@@ -272,7 +272,7 @@ public: + @return A view of the prefix. + */ + self_type prefix(char c) const; +- /** Get the prefix delimited by the first occurence of a character in @a delimiters. ++ /** Get the prefix delimited by the first occurrence of a character in @a delimiters. + + If no such character is found the entire view is returned. + The delimiter character is not included in the returned view. +--- a/iocore/cache/CacheVol.cc ++++ b/iocore/cache/CacheVol.cc +@@ -400,7 +400,7 @@ CacheVC::scanOpenWrite(int /* event ATS_ + // get volume lock + if (writer_lock_retry > SCAN_WRITER_LOCK_MAX_RETRY) { + int r = _action.continuation->handleEvent(CACHE_EVENT_SCAN_OPERATION_BLOCKED, nullptr); +- Debug("cache_scan", "still havent got the writer lock, asking user.."); ++ Debug("cache_scan", "still haven't got the writer lock, asking user.."); + switch (r) { + case CACHE_SCAN_RESULT_RETRY: + writer_lock_retry = 0; +--- a/iocore/eventsystem/I_IOBuffer.h ++++ b/iocore/eventsystem/I_IOBuffer.h +@@ -697,7 +697,7 @@ public: + /** + Perform a memchr() across the list of IOBufferBlocks. Returns the + offset from the current start point of the reader to the first +- occurence of character 'c' in the buffer. ++ occurrence of character 'c' in the buffer. + + @param c character to look for. + @param len number of characters to check. If len exceeds the number +--- a/iocore/net/OCSPStapling.cc ++++ b/iocore/net/OCSPStapling.cc +@@ -89,7 +89,7 @@ stapling_get_issuer(SSL_CTX *ssl_ctx, X5 + + #ifdef SSL_CTX_select_current_cert + if (!SSL_CTX_select_current_cert(ssl_ctx, x)) { +- Warning("OCSP: could not select current certifcate chain %p", x); ++ Warning("OCSP: could not select current certificate chain %p", x); + } + #endif + +--- a/mgmt/Alarms.cc ++++ b/mgmt/Alarms.cc +@@ -297,7 +297,7 @@ Alarms::signalAlarm(alarm_t a, const cha + (*(func))(a, ip, desc); + } + +- /* Priority 2 alarms get signalled if they are the first unsolved occurence. */ ++ /* Priority 2 alarms get signalled if they are the first unsolved occurrence. */ + if (priority == 2 && !ip) { + execAlarmBin(desc); + } +--- a/proxy/http/HttpSessionManager.cc ++++ b/proxy/http/HttpSessionManager.cc +@@ -204,7 +204,7 @@ ServerSessionPool::eventHandler(int even + if (connection_count_below_min) { + Debug("http_ss", + "[%" PRId64 "] [session_bucket] session received io notice [%s], " +- "reseting timeout to maintain minimum number of connections", ++ "resetting timeout to maintain minimum number of connections", + s->con_id, HttpDebugNames::get_event_name(event)); + s->get_netvc()->set_inactivity_timeout(s->get_netvc()->get_inactivity_timeout()); + s->get_netvc()->set_active_timeout(s->get_netvc()->get_active_timeout()); +--- a/plugins/lua/ts_lua_transform.c ++++ b/plugins/lua/ts_lua_transform.c +@@ -89,7 +89,7 @@ ts_lua_transform_handler(TSCont contp, t + empty_input = 0; + if (!TSVIOBufferGet(input_vio)) { + if (transform_ctx->output.vio) { +- TSDebug(TS_LUA_DEBUG_TAG, "[%s] reenabling ouput VIO after input VIO does not exist", __FUNCTION__); ++ TSDebug(TS_LUA_DEBUG_TAG, "[%s] reenabling output VIO after input VIO does not exist", __FUNCTION__); + TSVIONBytesSet(transform_ctx->output.vio, transform_ctx->total); + TSVIOReenable(transform_ctx->output.vio); + return 0; +--- a/proxy/hdrs/HdrHeap.cc ++++ b/proxy/hdrs/HdrHeap.cc +@@ -950,7 +950,7 @@ HdrHeap::unmarshal(int buf_length, int o + // Nothing to do + break; + default: +- fprintf(stderr, "WARNING: Unmarshal failed due to unknow obj type %d after %d bytes", (int)obj->m_type, ++ fprintf(stderr, "WARNING: Unmarshal failed due to unknown obj type %d after %d bytes", (int)obj->m_type, + (int)(obj_data - (char *)this)); + dump_heap(unmarshal_size); + return -1; diff --git a/patches/0013-fix-perl-interpreter-path.patch b/patches/0013-fix-perl-interpreter-path.patch new file mode 100644 index 00000000..4866f34b --- /dev/null +++ b/patches/0013-fix-perl-interpreter-path.patch @@ -0,0 +1,14 @@ +Description: Fix Perl interpreter path +Author: Jean Baptiste Favre +Reviewed-by: Jean Baptiste Favre +Last-Update: 2019-01-03 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/tools/tspush ++++ b/tools/tspush +@@ -1,4 +1,4 @@ +-#!/usr/bin/env perl ++#!/usr/bin/perl + + # + # Licensed to the Apache Software Foundation (ASF) under one diff --git a/patches/0014-use_system_yaml-cpp.patch b/patches/0014-use_system_yaml-cpp.patch new file mode 100644 index 00000000..058c052c --- /dev/null +++ b/patches/0014-use_system_yaml-cpp.patch @@ -0,0 +1,42 @@ +Description: Update compilation chain after embedded libyamlcpp removal +Author: Jean Baptiste Favre +Origin: other +Reviewed-by: Jean Baptiste Favre +Last-Update: 2019-01-30 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -41,7 +41,6 @@ include_directories(lib + proxy/shared + lib/records + include/wccp +- lib/yamlcpp/include + iocore/eventsystem + iocore/net + iocore/dns +--- a/configure.ac ++++ b/configure.ac +@@ -2041,7 +2041,6 @@ AC_CONFIG_FILES([ + include/tscore/ink_config.h + lib/tsconfig/Makefile + src/wccp/Makefile +- lib/yamlcpp/Makefile + mgmt/Makefile + mgmt/api/Makefile + mgmt/api/include/Makefile +--- a/lib/Makefile.am ++++ b/lib/Makefile.am +@@ -25,12 +25,6 @@ endif + # to prevent Clang Analyzer warning + LOCAL = + +-if BUILD_YAML_CPP +-LOCAL += yamlcpp +-endif +- + all-local: $(LOCAL) +- $(MAKE) -C yamlcpp + + clean-local: +- $(MAKE) -C yamlcpp clean diff --git a/patches/0015-8.0.4-CVE-backport.patch b/patches/0015-8.0.4-CVE-backport.patch new file mode 100644 index 00000000..e5e02de5 --- /dev/null +++ b/patches/0015-8.0.4-CVE-backport.patch @@ -0,0 +1,905 @@ +Description: HTTP/2 rate limiting + Fix for CVE-2019-9512, CVE-2019-9514, CVE-2019-9515, CVE-2019-10079 +Author: Bryan Call +Origin: backport, https://github.com/apache/trafficserver/pull/5822 +Reviewed-by: Jean Baptiste Favre +Last-Update: 2019-08-26 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/doc/admin-guide/files/records.config.en.rst ++++ b/doc/admin-guide/files/records.config.en.rst +@@ -3564,6 +3564,34 @@ HTTP/2 Configuration + HTTP/2 connection to avoid duplicate pushes on the same connection. If the + maximum number is reached, new entries are not remembered. + ++.. ts:cv:: CONFIG proxy.config.http2.max_settings_frames_per_minute INT 14 ++ :reloadable: ++ ++ Specifies how many SETTINGS frames |TS| receives for a minute at maximum. ++ Clients exceeded this limit will be immediately disconnected with an error ++ code of ENHANCE_YOUR_CALM. ++ ++.. ts:cv:: CONFIG proxy.config.http2.max_ping_frames_per_minute INT 60 ++ :reloadable: ++ ++ Specifies how many number of PING frames |TS| receives for a minute at maximum. ++ Clients exceeded this limit will be immediately disconnected with an error ++ code of ENHANCE_YOUR_CALM. ++ ++.. ts:cv:: CONFIG proxy.config.http2.max_priority_frames_per_minute INT 120 ++ :reloadable: ++ ++ Specifies how many number of PRIORITY frames |TS| receives for a minute at maximum. ++ Clients exceeded this limit will be immediately disconnected with an error ++ code of ENHANCE_YOUR_CALM. ++ ++.. ts:cv:: CONFIG proxy.config.http2.min_avg_window_update FLOAT 2560.0 ++ :reloadable: ++ ++ Specifies the minimum average window increment |TS| allows. The average will be calculated based on the last 5 WINDOW_UPDATE frames. ++ Clients that send smaller window increments lower than this limit will be immediately disconnected with an error ++ code of ENHANCE_YOUR_CALM. ++ + Plug-in Configuration + ===================== + +--- a/iocore/eventsystem/I_VIO.h ++++ b/iocore/eventsystem/I_VIO.h +@@ -139,6 +139,9 @@ public: + */ + inkcoreapi void reenable_re(); + ++ void disable(); ++ bool is_disabled(); ++ + VIO(int aop); + VIO(); + +@@ -218,6 +221,9 @@ public: + + */ + Ptr mutex; ++ ++private: ++ bool _disabled = false; + }; + + #include "I_VConnection.h" +--- a/iocore/eventsystem/P_VIO.h ++++ b/iocore/eventsystem/P_VIO.h +@@ -104,6 +104,7 @@ VIO::set_continuation(Continuation *acon + TS_INLINE void + VIO::reenable() + { ++ this->_disabled = false; + if (vc_server) { + vc_server->reenable(this); + } +@@ -117,7 +118,20 @@ VIO::reenable() + TS_INLINE void + VIO::reenable_re() + { ++ this->_disabled = false; + if (vc_server) { + vc_server->reenable_re(this); + } + } ++ ++TS_INLINE void ++VIO::disable() ++{ ++ this->_disabled = true; ++} ++ ++TS_INLINE bool ++VIO::is_disabled() ++{ ++ return this->_disabled; ++} +--- a/iocore/net/SSLNetVConnection.cc ++++ b/iocore/net/SSLNetVConnection.cc +@@ -526,7 +526,7 @@ SSLNetVConnection::net_read_io(NetHandle + // If it is not enabled, lower its priority. This allows + // a fast connection to speed match a slower connection by + // shifting down in priority even if it could read. +- if (!s->enabled || s->vio.op != VIO::READ) { ++ if (!s->enabled || s->vio.op != VIO::READ || s->vio.is_disabled()) { + read_disable(nh, this); + return; + } +@@ -632,7 +632,7 @@ SSLNetVConnection::net_read_io(NetHandle + } + + // If there is nothing to do or no space available, disable connection +- if (ntodo <= 0 || !buf.writer()->write_avail()) { ++ if (ntodo <= 0 || !buf.writer()->write_avail() || s->vio.is_disabled()) { + read_disable(nh, this); + return; + } +@@ -1577,6 +1577,7 @@ SSLNetVConnection::reenable(NetHandler * + } + Debug("ssl", "iterate from reenable curHook=%p %d", curHook, sslHandshakeHookState); + } ++ + this->readReschedule(nh); + } + +--- a/iocore/net/UnixNetVConnection.cc ++++ b/iocore/net/UnixNetVConnection.cc +@@ -201,7 +201,7 @@ read_from_net(NetHandler *nh, UnixNetVCo + return; + } + // if it is not enabled. +- if (!s->enabled || s->vio.op != VIO::READ) { ++ if (!s->enabled || s->vio.op != VIO::READ || s->vio.is_disabled()) { + read_disable(nh, vc); + return; + } +--- a/mgmt/RecordsConfig.cc ++++ b/mgmt/RecordsConfig.cc +@@ -1330,6 +1330,14 @@ static const RecordElement RecordsConfig + , + {RECT_LOCAL, "proxy.local.log.collation_mode", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-4]", RECA_NULL} + , ++ {RECT_CONFIG, "proxy.config.http2.max_settings_frames_per_minute", RECD_INT, "14", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} ++ , ++ {RECT_CONFIG, "proxy.config.http2.max_ping_frames_per_minute", RECD_INT, "60", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} ++ , ++ {RECT_CONFIG, "proxy.config.http2.max_priority_frames_per_minute", RECD_INT, "120", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} ++ , ++ {RECT_CONFIG, "proxy.config.http2.min_avg_window_update", RECD_FLOAT, "2560.0", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL} ++ , + + //# Librecords based stats system (new as of v2.1.3) + {RECT_CONFIG, "proxy.config.stat_api.max_stats_allowed", RECD_INT, "256", RECU_RESTART_TS, RR_NULL, RECC_INT, "[256-1000]", RECA_NULL} +--- a/proxy/http2/HTTP2.cc ++++ b/proxy/http2/HTTP2.cc +@@ -717,21 +717,28 @@ http2_decode_header_blocks(HTTPHdr *hdr, + } + + // Initialize this subsystem with librecords configs (for now) +-uint32_t Http2::max_concurrent_streams_in = 100; +-uint32_t Http2::min_concurrent_streams_in = 10; +-uint32_t Http2::max_active_streams_in = 0; +-bool Http2::throttling = false; +-uint32_t Http2::stream_priority_enabled = 0; +-uint32_t Http2::initial_window_size = 1048576; +-uint32_t Http2::max_frame_size = 16384; +-uint32_t Http2::header_table_size = 4096; +-uint32_t Http2::max_header_list_size = 4294967295; +-uint32_t Http2::max_request_header_size = 131072; +-uint32_t Http2::accept_no_activity_timeout = 120; +-uint32_t Http2::no_activity_timeout_in = 120; +-uint32_t Http2::active_timeout_in = 0; +-uint32_t Http2::push_diary_size = 256; +-uint32_t Http2::zombie_timeout_in = 0; ++uint32_t Http2::max_concurrent_streams_in = 100; ++uint32_t Http2::min_concurrent_streams_in = 10; ++uint32_t Http2::max_active_streams_in = 0; ++bool Http2::throttling = false; ++uint32_t Http2::stream_priority_enabled = 0; ++uint32_t Http2::initial_window_size = 1048576; ++uint32_t Http2::max_frame_size = 16384; ++uint32_t Http2::header_table_size = 4096; ++uint32_t Http2::max_header_list_size = 4294967295; ++uint32_t Http2::max_request_header_size = 131072; ++uint32_t Http2::accept_no_activity_timeout = 120; ++uint32_t Http2::no_activity_timeout_in = 120; ++uint32_t Http2::active_timeout_in = 0; ++uint32_t Http2::push_diary_size = 256; ++uint32_t Http2::zombie_timeout_in = 0; ++float Http2::stream_error_rate_threshold = 0.1; ++uint32_t Http2::max_settings_per_frame = 7; ++uint32_t Http2::max_settings_per_minute = 14; ++uint32_t Http2::max_settings_frames_per_minute = 14; ++uint32_t Http2::max_ping_frames_per_minute = 60; ++uint32_t Http2::max_priority_frames_per_minute = 120; ++float Http2::min_avg_window_update = 2560.0; + + void + Http2::init() +--- a/proxy/http2/HTTP2.h ++++ b/proxy/http2/HTTP2.h +@@ -378,6 +378,13 @@ public: + static uint32_t active_timeout_in; + static uint32_t push_diary_size; + static uint32_t zombie_timeout_in; ++ static float stream_error_rate_threshold; ++ static uint32_t max_settings_per_frame; ++ static uint32_t max_settings_per_minute; ++ static uint32_t max_settings_frames_per_minute; ++ static uint32_t max_ping_frames_per_minute; ++ static uint32_t max_priority_frames_per_minute; ++ static float min_avg_window_update; + + static void init(); + }; +--- a/proxy/http2/Http2ClientSession.cc ++++ b/proxy/http2/Http2ClientSession.cc +@@ -74,6 +74,11 @@ Http2ClientSession::destroy() + void + Http2ClientSession::free() + { ++ if (this->_reenable_event) { ++ this->_reenable_event->cancel(); ++ this->_reenable_event = nullptr; ++ } ++ + if (h2_pushed_urls) { + this->h2_pushed_urls = ink_hash_table_destroy(this->h2_pushed_urls); + } +@@ -326,6 +331,13 @@ Http2ClientSession::main_event_handler(i + break; + } + ++ case HTTP2_SESSION_EVENT_REENABLE: ++ // VIO will be reenableed in this handler ++ retval = (this->*session_handler)(VC_EVENT_READ_READY, static_cast(e->cookie)); ++ // Clear the event after calling session_handler to not reschedule REENABLE in it ++ this->_reenable_event = nullptr; ++ break; ++ + case VC_EVENT_ACTIVE_TIMEOUT: + case VC_EVENT_INACTIVITY_TIMEOUT: + case VC_EVENT_ERROR: +@@ -478,7 +490,16 @@ Http2ClientSession::state_complete_frame + STATE_ENTER(&Http2ClientSession::state_complete_frame_read, event); + ink_assert(event == VC_EVENT_READ_COMPLETE || event == VC_EVENT_READ_READY); + if (this->sm_reader->read_avail() < this->current_hdr.length) { +- vio->reenable(); ++ if (this->_should_do_something_else()) { ++ if (this->_reenable_event == nullptr) { ++ vio->disable(); ++ this->_reenable_event = mutex->thread_holding->schedule_in(this, HRTIME_MSECONDS(1), HTTP2_SESSION_EVENT_REENABLE, vio); ++ } else { ++ vio->reenable(); ++ } ++ } else { ++ vio->reenable(); ++ } + return 0; + } + Http2SsnDebug("completed frame read, %" PRId64 " bytes available", this->sm_reader->read_avail()); +@@ -495,6 +516,7 @@ Http2ClientSession::do_complete_frame_re + Http2Frame frame(this->current_hdr, this->sm_reader); + send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_RECV, &frame); + this->sm_reader->consume(this->current_hdr.length); ++ ++(this->_n_frame_read); + + // Set the event handler if there is no more data to process a new frame + HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_start_frame_read); +@@ -510,9 +532,19 @@ Http2ClientSession::state_process_frame_ + } + + while (this->sm_reader->read_avail() >= (int64_t)HTTP2_FRAME_HEADER_LEN) { ++ ++ Http2ErrorCode err = Http2ErrorCode::HTTP2_ERROR_NO_ERROR; ++ if (this->connection_state.get_stream_error_rate() > std::min(1.0, Http2::stream_error_rate_threshold * 2.0)) { ++ ip_port_text_buffer ipb; ++ const char *client_ip = ats_ip_ntop(get_client_addr(), ipb, sizeof(ipb)); ++ Error("HTTP/2 session error client_ip=%s session_id=%" PRId64 ++ " closing a connection, because its stream error rate (%f) is too high", ++ client_ip, connection_id(), this->connection_state.get_stream_error_rate()); ++ err = Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM; ++ } ++ + // Return if there was an error +- Http2ErrorCode err; +- if (do_start_frame_read(err) < 0) { ++ if (err > Http2ErrorCode::HTTP2_ERROR_NO_ERROR || do_start_frame_read(err) < 0) { + // send an error if specified. Otherwise, just go away + if (err > Http2ErrorCode::HTTP2_ERROR_NO_ERROR) { + SCOPED_MUTEX_LOCK(lock, this->connection_state.mutex, this_ethread()); +@@ -531,6 +563,14 @@ Http2ClientSession::state_process_frame_ + break; + } + do_complete_frame_read(); ++ ++ if (this->_should_do_something_else()) { ++ if (this->_reenable_event == nullptr) { ++ vio->disable(); ++ this->_reenable_event = mutex->thread_holding->schedule_in(this, HRTIME_MSECONDS(1), HTTP2_SESSION_EVENT_REENABLE, vio); ++ return 0; ++ } ++ } + } + + // If the client hasn't shut us down, reenable +@@ -539,3 +579,10 @@ Http2ClientSession::state_process_frame_ + } + return 0; + } ++ ++bool ++Http2ClientSession::_should_do_something_else() ++{ ++ // Do something else every 128 incoming frames ++ return (this->_n_frame_read & 0x7F) == 0; ++} +--- a/proxy/http2/Http2ClientSession.h ++++ b/proxy/http2/Http2ClientSession.h +@@ -42,6 +42,7 @@ + #define HTTP2_SESSION_EVENT_XMIT (HTTP2_SESSION_EVENTS_START + 4) + #define HTTP2_SESSION_EVENT_SHUTDOWN_INIT (HTTP2_SESSION_EVENTS_START + 5) + #define HTTP2_SESSION_EVENT_SHUTDOWN_CONT (HTTP2_SESSION_EVENTS_START + 6) ++#define HTTP2_SESSION_EVENT_REENABLE (HTTP2_SESSION_EVENTS_START + 7) + + size_t const HTTP2_HEADER_BUFFER_SIZE_INDEX = CLIENT_CONNECTION_FIRST_READ_BUFFER_SIZE_INDEX; + +@@ -335,6 +336,8 @@ private: + // if there are multiple frames ready on the wire + int state_process_frame_read(int event, VIO *vio, bool inside_frame); + ++ bool _should_do_something_else(); ++ + int64_t total_write_len = 0; + SessionHandler session_handler = nullptr; + NetVConnection *client_vc = nullptr; +@@ -358,6 +361,9 @@ private: + + InkHashTable *h2_pushed_urls = nullptr; + uint32_t h2_pushed_urls_size = 0; ++ ++ Event *_reenable_event = nullptr; ++ int _n_frame_read = 0; + }; + + extern ClassAllocator http2ClientSessionAllocator; +--- a/proxy/http2/Http2ConnectionState.cc ++++ b/proxy/http2/Http2ConnectionState.cc +@@ -27,6 +27,7 @@ + #include "Http2Stream.h" + #include "Http2DebugNames.h" + #include ++#include + + #define Http2ConDebug(ua_session, fmt, ...) \ + SsnDebug(ua_session, "http2_con", "[%" PRId64 "] " fmt, ua_session->connection_id(), ##__VA_ARGS__); +@@ -137,18 +138,18 @@ rcv_data_frame(Http2ConnectionState &cst + } + + // Check whether Window Size is acceptable +- if (cstate.server_rwnd < payload_length) { ++ if (cstate.server_rwnd() < payload_length) { + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_FLOW_CONTROL_ERROR, + "recv data cstate.server_rwnd < payload_length"); + } +- if (stream->server_rwnd < payload_length) { ++ if (stream->server_rwnd() < payload_length) { + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_FLOW_CONTROL_ERROR, + "recv data stream->server_rwnd < payload_length"); + } + + // Update Window size +- cstate.server_rwnd -= payload_length; +- stream->server_rwnd -= payload_length; ++ cstate.decrement_server_rwnd(payload_length); ++ stream->decrement_server_rwnd(payload_length); + + const uint32_t unpadded_length = payload_length - pad_length; + // If we call write() multiple times, we must keep the same reader, so we can +@@ -174,15 +175,15 @@ rcv_data_frame(Http2ConnectionState &cst + uint32_t initial_rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE); + uint32_t min_rwnd = std::min(initial_rwnd, cstate.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE)); + // Connection level WINDOW UPDATE +- if (cstate.server_rwnd <= min_rwnd) { +- Http2WindowSize diff_size = initial_rwnd - cstate.server_rwnd; +- cstate.server_rwnd += diff_size; ++ if (cstate.server_rwnd() <= min_rwnd) { ++ Http2WindowSize diff_size = initial_rwnd - cstate.server_rwnd(); ++ cstate.increment_server_rwnd(diff_size); + cstate.send_window_update_frame(0, diff_size); + } + // Stream level WINDOW UPDATE +- if (stream->server_rwnd <= min_rwnd) { +- Http2WindowSize diff_size = initial_rwnd - stream->server_rwnd; +- stream->server_rwnd += diff_size; ++ if (stream->server_rwnd() <= min_rwnd) { ++ Http2WindowSize diff_size = initial_rwnd - stream->server_rwnd(); ++ stream->increment_server_rwnd(diff_size); + cstate.send_window_update_frame(stream->get_id(), diff_size); + } + +@@ -406,6 +407,17 @@ rcv_priority_frame(Http2ConnectionState + "PRIORITY frame depends on itself"); + } + ++ // Update PRIORITY frame count per minute ++ cstate.increment_received_priority_frame_count(); ++ // Close this conection if its priority frame count received exceeds a limit ++ if (cstate.get_received_priority_frame_count() > Http2::max_priority_frames_per_minute) { ++ Http2StreamDebug(cstate.ua_session, stream_id, ++ "Observed too frequent priority changes: %u priority changes within a last minute", ++ cstate.get_received_priority_frame_count()); ++ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM, ++ "recv priority too frequent priority changes"); ++ } ++ + if (!Http2::stream_priority_enabled) { + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE); + } +@@ -511,6 +523,16 @@ rcv_settings_frame(Http2ConnectionState + Warning("Setting frame for zombied sessoin %" PRId64, cstate.ua_session->connection_id()); + } + ++ // Update SETTIGNS frame count per minute ++ cstate.increment_received_settings_frame_count(); ++ // Close this conection if its SETTINGS frame count exceeds a limit ++ if (cstate.get_received_settings_frame_count() > Http2::max_settings_frames_per_minute) { ++ Http2StreamDebug(cstate.ua_session, stream_id, "Observed too frequent SETTINGS frames: %u frames within a last minute", ++ cstate.get_received_settings_frame_count()); ++ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM, ++ "recv settings too frequent SETTINGS frames"); ++ } ++ + // [RFC 7540] 6.5. The stream identifier for a SETTINGS frame MUST be zero. + // If an endpoint receives a SETTINGS frame whose stream identifier field is + // anything other than 0x0, the endpoint MUST respond with a connection +@@ -614,6 +636,16 @@ rcv_ping_frame(Http2ConnectionState &cst + "ping bad length"); + } + ++ // Update PING frame count per minute ++ cstate.increment_received_ping_frame_count(); ++ // Close this conection if its ping count received exceeds a limit ++ if (cstate.get_received_ping_frame_count() > Http2::max_ping_frames_per_minute) { ++ Http2StreamDebug(cstate.ua_session, stream_id, "Observed too frequent PING frames: %u PING frames within a last minute", ++ cstate.get_received_ping_frame_count()); ++ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM, ++ "recv ping too frequent PING frame"); ++ } ++ + // An endpoint MUST NOT respond to PING frames containing this flag. + if (frame.header().flags & HTTP2_FLAGS_PING_ACK) { + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE); +@@ -695,7 +727,7 @@ rcv_window_update_frame(Http2ConnectionS + if (stream_id == 0) { + // Connection level window update + Http2StreamDebug(cstate.ua_session, stream_id, "Received WINDOW_UPDATE frame - updated to: %zd delta: %u", +- (cstate.client_rwnd + size), size); ++ (cstate.client_rwnd() + size), size); + + // A sender MUST NOT allow a flow-control window to exceed 2^31-1 + // octets. If a sender receives a WINDOW_UPDATE that causes a flow- +@@ -704,12 +736,16 @@ rcv_window_update_frame(Http2ConnectionS + // sends a RST_STREAM with an error code of FLOW_CONTROL_ERROR; for the + // connection, a GOAWAY frame with an error code of FLOW_CONTROL_ERROR + // is sent. +- if (size > HTTP2_MAX_WINDOW_SIZE - cstate.client_rwnd) { ++ if (size > HTTP2_MAX_WINDOW_SIZE - cstate.client_rwnd()) { + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_FLOW_CONTROL_ERROR, + "window update too big"); + } + +- cstate.client_rwnd += size; ++ auto error = cstate.increment_client_rwnd(size); ++ if (error != Http2ErrorCode::HTTP2_ERROR_NO_ERROR) { ++ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, error); ++ } ++ + cstate.restart_streams(); + } else { + // Stream level window update +@@ -725,7 +761,7 @@ rcv_window_update_frame(Http2ConnectionS + } + + Http2StreamDebug(cstate.ua_session, stream_id, "Received WINDOW_UPDATE frame - updated to: %zd delta: %u", +- (stream->client_rwnd + size), size); ++ (stream->client_rwnd() + size), size); + + // A sender MUST NOT allow a flow-control window to exceed 2^31-1 + // octets. If a sender receives a WINDOW_UPDATE that causes a flow- +@@ -734,14 +770,17 @@ rcv_window_update_frame(Http2ConnectionS + // sends a RST_STREAM with an error code of FLOW_CONTROL_ERROR; for the + // connection, a GOAWAY frame with an error code of FLOW_CONTROL_ERROR + // is sent. +- if (size > HTTP2_MAX_WINDOW_SIZE - stream->client_rwnd) { ++ if (size > HTTP2_MAX_WINDOW_SIZE - stream->client_rwnd()) { + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_FLOW_CONTROL_ERROR, + "window update too big 2"); + } + +- stream->client_rwnd += size; +- ssize_t wnd = std::min(cstate.client_rwnd, stream->client_rwnd); ++ auto error = stream->increment_client_rwnd(size); ++ if (error != Http2ErrorCode::HTTP2_ERROR_NO_ERROR) { ++ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, error); ++ } + ++ ssize_t wnd = std::min(cstate.client_rwnd(), stream->client_rwnd()); + if (!stream->is_closed() && stream->get_state() == Http2StreamState::HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE && wnd > 0) { + SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); + stream->restart_sending(); +@@ -1137,7 +1176,7 @@ Http2ConnectionState::restart_streams() + while (s != end) { + Http2Stream *next = static_cast(s->link.next ? s->link.next : stream_list.head); + if (!s->is_closed() && s->get_state() == Http2StreamState::HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE && +- std::min(this->client_rwnd, s->client_rwnd) > 0) { ++ std::min(this->client_rwnd(), s->client_rwnd()) > 0) { + SCOPED_MUTEX_LOCK(lock, s->mutex, this_ethread()); + s->restart_sending(); + } +@@ -1145,7 +1184,7 @@ Http2ConnectionState::restart_streams() + s = next; + } + if (!s->is_closed() && s->get_state() == Http2StreamState::HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE && +- std::min(this->client_rwnd, s->client_rwnd) > 0) { ++ std::min(this->client_rwnd(), s->client_rwnd()) > 0) { + SCOPED_MUTEX_LOCK(lock, s->mutex, this_ethread()); + s->restart_sending(); + } +@@ -1284,7 +1323,7 @@ Http2ConnectionState::update_initial_rwn + // Update stream level window sizes + for (Http2Stream *s = stream_list.head; s; s = static_cast(s->link.next)) { + SCOPED_MUTEX_LOCK(lock, s->mutex, this_ethread()); +- s->client_rwnd = new_size - (client_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE) - s->client_rwnd); ++ s->update_initial_rwnd(new_size - (client_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE) - s->client_rwnd())); + } + } + +@@ -1313,7 +1352,7 @@ Http2ConnectionState::send_data_frames_d + Http2DependencyTree::Node *node = dependency_tree->top(); + + // No node to send or no connection level window left +- if (node == nullptr || client_rwnd <= 0) { ++ if (node == nullptr || _client_rwnd <= 0) { + return; + } + +@@ -1355,7 +1394,7 @@ Http2ConnectionState::send_data_frames_d + Http2SendDataFrameResult + Http2ConnectionState::send_a_data_frame(Http2Stream *stream, size_t &payload_length) + { +- const ssize_t window_size = std::min(this->client_rwnd, stream->client_rwnd); ++ const ssize_t window_size = std::min(this->client_rwnd(), stream->client_rwnd()); + const size_t buf_len = BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_DATA]); + const size_t write_available_size = std::min(buf_len, static_cast(window_size)); + size_t read_available_size = 0; +@@ -1401,12 +1440,12 @@ Http2ConnectionState::send_a_data_frame( + } + + // Update window size +- this->client_rwnd -= payload_length; +- stream->client_rwnd -= payload_length; ++ this->decrement_client_rwnd(payload_length); ++ stream->decrement_client_rwnd(payload_length); + + // Create frame + Http2StreamDebug(ua_session, stream->get_id(), "Send a DATA frame - client window con: %5zd stream: %5zd payload: %5zd", +- client_rwnd, stream->client_rwnd, payload_length); ++ _client_rwnd, stream->client_rwnd(), payload_length); + + Http2Frame data(HTTP2_FRAME_TYPE_DATA, stream->get_id(), flags); + data.alloc(buffer_size_index[HTTP2_FRAME_TYPE_DATA]); +@@ -1817,6 +1856,72 @@ Http2ConnectionState::send_window_update + this->ua_session->handleEvent(HTTP2_SESSION_EVENT_XMIT, &window_update); + } + ++void ++Http2ConnectionState::increment_received_settings_frame_count() ++{ ++ ink_hrtime hrtime_sec = Thread::get_hrtime() / HRTIME_SECOND; ++ uint8_t counter_index = ((hrtime_sec % 60) >= 30); ++ ++ if ((hrtime_sec - 60) > this->settings_frame_count_last_update) { ++ this->settings_frame_count[0] = 0; ++ this->settings_frame_count[1] = 0; ++ } else if (counter_index != ((this->settings_frame_count_last_update % 60) >= 30)) { ++ this->settings_frame_count[counter_index] = 0; ++ } ++ ++this->settings_frame_count[counter_index]; ++ this->settings_frame_count_last_update = hrtime_sec; ++} ++ ++uint32_t ++Http2ConnectionState::get_received_settings_frame_count() ++{ ++ return this->settings_frame_count[0] + this->settings_frame_count[1]; ++} ++ ++void ++Http2ConnectionState::increment_received_ping_frame_count() ++{ ++ ink_hrtime hrtime_sec = Thread::get_hrtime() / HRTIME_SECOND; ++ uint8_t counter_index = ((hrtime_sec % 60) >= 30); ++ ++ if ((hrtime_sec - 60) > this->ping_frame_count_last_update) { ++ this->ping_frame_count[0] = 0; ++ this->ping_frame_count[1] = 0; ++ } else if (counter_index != ((this->ping_frame_count_last_update % 60) >= 30)) { ++ this->ping_frame_count[counter_index] = 0; ++ } ++ ++this->ping_frame_count[counter_index]; ++ this->ping_frame_count_last_update = hrtime_sec; ++} ++ ++uint32_t ++Http2ConnectionState::get_received_ping_frame_count() ++{ ++ return this->ping_frame_count[0] + this->ping_frame_count[1]; ++} ++ ++void ++Http2ConnectionState::increment_received_priority_frame_count() ++{ ++ ink_hrtime hrtime_sec = Thread::get_hrtime() / HRTIME_SECOND; ++ uint8_t counter_index = ((hrtime_sec % 60) >= 30); ++ ++ if ((hrtime_sec - 60) > this->priority_frame_count_last_update) { ++ this->priority_frame_count[0] = 0; ++ this->priority_frame_count[1] = 0; ++ } else if (counter_index != ((this->priority_frame_count_last_update % 60) >= 30)) { ++ this->priority_frame_count[counter_index] = 0; ++ } ++ ++this->priority_frame_count[counter_index]; ++ this->priority_frame_count_last_update = hrtime_sec; ++} ++ ++uint32_t ++Http2ConnectionState::get_received_priority_frame_count() ++{ ++ return this->priority_frame_count[0] + this->priority_frame_count[1]; ++} ++ + // Return min_concurrent_streams_in when current client streams number is larger than max_active_streams_in. + // Main purpose of this is preventing DDoS Attacks. + unsigned +@@ -1849,3 +1954,52 @@ Http2ConnectionState::_adjust_concurrent + + return Http2::max_concurrent_streams_in; + } ++ ++ssize_t ++Http2ConnectionState::client_rwnd() const ++{ ++ return this->_client_rwnd; ++} ++ ++Http2ErrorCode ++Http2ConnectionState::increment_client_rwnd(size_t amount) ++{ ++ this->_client_rwnd += amount; ++ ++ this->_recent_rwnd_increment[this->_recent_rwnd_increment_index] = amount; ++ ++this->_recent_rwnd_increment_index; ++ this->_recent_rwnd_increment_index %= this->_recent_rwnd_increment.size(); ++ double sum = std::accumulate(this->_recent_rwnd_increment.begin(), this->_recent_rwnd_increment.end(), 0.0); ++ double avg = sum / this->_recent_rwnd_increment.size(); ++ if (avg < Http2::min_avg_window_update) { ++ return Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM; ++ } ++ return Http2ErrorCode::HTTP2_ERROR_NO_ERROR; ++} ++ ++Http2ErrorCode ++Http2ConnectionState::decrement_client_rwnd(size_t amount) ++{ ++ this->_client_rwnd -= amount; ++ return Http2ErrorCode::HTTP2_ERROR_NO_ERROR; ++} ++ ++ssize_t ++Http2ConnectionState::server_rwnd() const ++{ ++ return this->_server_rwnd; ++} ++ ++Http2ErrorCode ++Http2ConnectionState::increment_server_rwnd(size_t amount) ++{ ++ this->_server_rwnd += amount; ++ return Http2ErrorCode::HTTP2_ERROR_NO_ERROR; ++} ++ ++Http2ErrorCode ++Http2ConnectionState::decrement_server_rwnd(size_t amount) ++{ ++ this->_server_rwnd -= amount; ++ return Http2ErrorCode::HTTP2_ERROR_NO_ERROR; ++} +--- a/proxy/http2/Http2ConnectionState.h ++++ b/proxy/http2/Http2ConnectionState.h +@@ -220,9 +220,16 @@ public: + return client_streams_in_count; + } + +- // Connection level window size +- ssize_t client_rwnd = HTTP2_INITIAL_WINDOW_SIZE; +- ssize_t server_rwnd = Http2::initial_window_size; ++ double ++ get_stream_error_rate() const ++ { ++ int total = get_stream_requests(); ++ if (total > 0) { ++ return (double)stream_error_count / (double)total; ++ } else { ++ return 0; ++ } ++ } + + // HTTP/2 frame sender + void schedule_stream(Http2Stream *stream); +@@ -292,6 +299,20 @@ public: + } + } + ++ void increment_received_settings_frame_count(); ++ uint32_t get_received_settings_frame_count(); ++ void increment_received_ping_frame_count(); ++ uint32_t get_received_ping_frame_count(); ++ void increment_received_priority_frame_count(); ++ uint32_t get_received_priority_frame_count(); ++ ++ ssize_t client_rwnd() const; ++ Http2ErrorCode increment_client_rwnd(size_t amount); ++ Http2ErrorCode decrement_client_rwnd(size_t amount); ++ ssize_t server_rwnd() const; ++ Http2ErrorCode increment_server_rwnd(size_t amount); ++ Http2ErrorCode decrement_server_rwnd(size_t amount); ++ + private: + unsigned _adjust_concurrent_stream(); + +@@ -330,4 +351,24 @@ private: + Event *shutdown_cont_event = nullptr; + Event *fini_event = nullptr; + Event *zombie_event = nullptr; ++ ++ // Counter for stream errors ATS sent ++ uint32_t stream_error_count; ++ ++ // Connection level window size ++ ssize_t _client_rwnd = HTTP2_INITIAL_WINDOW_SIZE; ++ ssize_t _server_rwnd = Http2::initial_window_size; ++ ++ std::vector _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX}; ++ int _recent_rwnd_increment_index = 0; ++ ++ // Counters for frames received within last 60 seconds ++ // Each item in an array holds a count for 30 seconds. ++ uint16_t settings_frame_count[2] = {0}; ++ ink_hrtime settings_frame_count_last_update = 0; ++ uint16_t ping_frame_count[2] = {0}; ++ ink_hrtime ping_frame_count_last_update = 0; ++ uint16_t priority_frame_count[2] = {0}; ++ ink_hrtime priority_frame_count_last_update = 0; ++ + }; +--- a/proxy/http2/Http2Stream.cc ++++ b/proxy/http2/Http2Stream.cc +@@ -26,6 +26,8 @@ + #include "Http2ClientSession.h" + #include "../http/HttpSM.h" + ++#include ++ + #define Http2StreamDebug(fmt, ...) \ + SsnDebug(parent, "http2_stream", "[%" PRId64 "] [%u] " fmt, parent->connection_id(), this->get_id(), ##__VA_ARGS__); + +@@ -812,6 +814,63 @@ Http2Stream::response_process_data(bool + } + } + ++ssize_t ++Http2Stream::client_rwnd() const ++{ ++ return this->_client_rwnd; ++} ++ ++Http2ErrorCode ++Http2Stream::increment_client_rwnd(size_t amount) ++{ ++ this->_client_rwnd += amount; ++ ++ this->_recent_rwnd_increment[this->_recent_rwnd_increment_index] = amount; ++ ++this->_recent_rwnd_increment_index; ++ this->_recent_rwnd_increment_index %= this->_recent_rwnd_increment.size(); ++ double sum = std::accumulate(this->_recent_rwnd_increment.begin(), this->_recent_rwnd_increment.end(), 0.0); ++ double avg = sum / this->_recent_rwnd_increment.size(); ++ if (avg < Http2::min_avg_window_update) { ++ return Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM; ++ } ++ return Http2ErrorCode::HTTP2_ERROR_NO_ERROR; ++} ++ ++Http2ErrorCode ++Http2Stream::decrement_client_rwnd(size_t amount) ++{ ++ this->_client_rwnd -= amount; ++ if (this->_client_rwnd < 0) { ++ return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR; ++ } else { ++ return Http2ErrorCode::HTTP2_ERROR_NO_ERROR; ++ } ++} ++ ++ssize_t ++Http2Stream::server_rwnd() const ++{ ++ return this->_server_rwnd; ++} ++ ++Http2ErrorCode ++Http2Stream::increment_server_rwnd(size_t amount) ++{ ++ this->_server_rwnd += amount; ++ return Http2ErrorCode::HTTP2_ERROR_NO_ERROR; ++} ++ ++Http2ErrorCode ++Http2Stream::decrement_server_rwnd(size_t amount) ++{ ++ this->_server_rwnd -= amount; ++ if (this->_server_rwnd < 0) { ++ return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR; ++ } else { ++ return Http2ErrorCode::HTTP2_ERROR_NO_ERROR; ++ } ++} ++ + bool + Http2Stream::response_is_data_available() const + { +--- a/proxy/http2/Http2Stream.h ++++ b/proxy/http2/Http2Stream.h +@@ -38,7 +38,7 @@ class Http2Stream : public ProxyClientTr + { + public: + typedef ProxyClientTransaction super; ///< Parent type. +- Http2Stream(Http2StreamId sid = 0, ssize_t initial_rwnd = Http2::initial_window_size) : client_rwnd(initial_rwnd), _id(sid) ++ Http2Stream(Http2StreamId sid = 0, ssize_t initial_rwnd = Http2::initial_window_size) : _id(sid), _client_rwnd(initial_rwnd) + { + SET_HANDLER(&Http2Stream::main_event_handler); + } +@@ -46,10 +46,10 @@ public: + void + init(Http2StreamId sid, ssize_t initial_rwnd) + { +- _id = sid; +- _start_time = Thread::get_hrtime(); +- _thread = this_ethread(); +- this->client_rwnd = initial_rwnd; ++ _id = sid; ++ _start_time = Thread::get_hrtime(); ++ _thread = this_ethread(); ++ this->_client_rwnd = initial_rwnd; + HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_STREAM_COUNT, _thread); + HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_CLIENT_STREAM_COUNT, _thread); + sm_reader = request_reader = request_buffer.alloc_reader(); +@@ -117,7 +117,7 @@ public: + void + update_initial_rwnd(Http2WindowSize new_size) + { +- client_rwnd = new_size; ++ this->_client_rwnd = new_size; + } + + bool +@@ -171,8 +171,12 @@ public: + void push_promise(URL &url, const MIMEField *accept_encoding); + + // Stream level window size +- ssize_t client_rwnd; +- ssize_t server_rwnd = Http2::initial_window_size; ++ ssize_t client_rwnd() const; ++ Http2ErrorCode increment_client_rwnd(size_t amount); ++ Http2ErrorCode decrement_client_rwnd(size_t amount); ++ ssize_t server_rwnd() const; ++ Http2ErrorCode increment_server_rwnd(size_t amount); ++ Http2ErrorCode decrement_server_rwnd(size_t amount); + + uint8_t *header_blocks = nullptr; + uint32_t header_blocks_length = 0; // total length of header blocks (not include +@@ -288,6 +292,12 @@ private: + uint64_t data_length = 0; + uint64_t bytes_sent = 0; + ++ ssize_t _client_rwnd; ++ ssize_t _server_rwnd = Http2::initial_window_size; ++ ++ std::vector _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX}; ++ int _recent_rwnd_increment_index = 0; ++ + ChunkedHandler chunked_handler; + Event *cross_thread_event = nullptr; + Event *buffer_full_write_event = nullptr; diff --git a/patches/0015-8.0.5-CVE-backport.patch b/patches/0015-8.0.5-CVE-backport.patch new file mode 100644 index 00000000..b252d5bc --- /dev/null +++ b/patches/0015-8.0.5-CVE-backport.patch @@ -0,0 +1,25 @@ +Description: HTTP/2 fix with realloc (CVE-2019-9518) +Author: Bryan Call +Origin: backport, https://github.com/apache/trafficserver/pull/5850 +Reviewed-by: Jean Baptiste Favre +Last-Update: 2019-08-26 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/proxy/http2/Http2ConnectionState.cc ++++ b/proxy/http2/Http2ConnectionState.cc +@@ -304,10 +304,12 @@ rcv_headers_frame(Http2ConnectionState & + } + } + +- stream->header_blocks = static_cast(ats_malloc(header_block_fragment_length)); +- frame.reader()->memcpy(stream->header_blocks, header_block_fragment_length, header_block_fragment_offset); ++ if (header_block_fragment_length > 0) { ++ stream->header_blocks = static_cast(ats_malloc(header_block_fragment_length)); ++ frame.reader()->memcpy(stream->header_blocks, header_block_fragment_length, header_block_fragment_offset); + +- stream->header_blocks_length = header_block_fragment_length; ++ stream->header_blocks_length = header_block_fragment_length; ++ } + + if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS) { + // NOTE: If there are END_HEADERS flag, decode stored Header Blocks. diff --git a/patches/0016-CVE-2019-17559.patch b/patches/0016-CVE-2019-17559.patch new file mode 100644 index 00000000..81040df5 --- /dev/null +++ b/patches/0016-CVE-2019-17559.patch @@ -0,0 +1,213 @@ +Description: Fix for CVE-2019-17559 +Author: Bryan Call +Origin: backport +Applied-Upstream: https://github.com/apache/trafficserver/pull/6389 +Last-Update: 2020-04-16 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- trafficserver.orig/proxy/hdrs/HdrTest.cc 2020-04-16 19:03:21.522369124 +0200 ++++ trafficserver/proxy/hdrs/HdrTest.cc 2020-04-16 19:03:21.522369124 +0200 +@@ -343,44 +343,100 @@ + // Start with an easy one... + "http://trafficserver.apache.org/index.html", + +- // "cheese://bogosity", This fails, but it's not clear it should work... ++ "cheese://bogosity", + +- "some.place", "some.place/", "http://some.place", "http://some.place/", "http://some.place/path", +- "http://some.place/path;params", "http://some.place/path;params?query", "http://some.place/path;params?query#fragment", +- "http://some.place/path?query#fragment", "http://some.place/path#fragment", +- +- "some.place:80", "some.place:80/", "http://some.place:80", "http://some.place:80/", +- +- "foo@some.place:80", "foo@some.place:80/", "http://foo@some.place:80", "http://foo@some.place:80/", +- +- "foo:bar@some.place:80", "foo:bar@some.place:80/", "http://foo:bar@some.place:80", "http://foo:bar@some.place:80/", ++ "some.place", ++ "some.place/", ++ "http://some.place", ++ "http://some.place/", ++ "http://some.place/path", ++ "http://some.place/path;params", ++ "http://some.place/path;params?query", ++ "http://some.place/path;params?query#fragment", ++ "http://some.place/path?query#fragment", ++ "http://some.place/path#fragment", ++ ++ "some.place:80", ++ "some.place:80/", ++ "http://some.place:80", ++ "http://some.place:80/", ++ ++ "foo@some.place:80", ++ "foo@some.place:80/", ++ "http://foo@some.place:80", ++ "http://foo@some.place:80/", ++ ++ "foo:bar@some.place:80", ++ "foo:bar@some.place:80/", ++ "http://foo:bar@some.place:80", ++ "http://foo:bar@some.place:80/", + + // Some address stuff +- "http://172.16.28.101", "http://172.16.28.101:8080", "http://[::]", "http://[::1]", "http://[fc01:172:16:28::101]", +- "http://[fc01:172:16:28::101]:80", "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]", +- "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080", "http://172.16.28.101/some/path", "http://172.16.28.101:8080/some/path", +- "http://[::1]/some/path", "http://[fc01:172:16:28::101]/some/path", "http://[fc01:172:16:28::101]:80/some/path", +- "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]/some/path", "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/some/path", +- "http://172.16.28.101/", "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/", ++ "http://172.16.28.101", ++ "http://172.16.28.101:8080", ++ "http://[::]", ++ "http://[::1]", ++ "http://[fc01:172:16:28::101]", ++ "http://[fc01:172:16:28::101]:80", ++ "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]", ++ "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080", ++ "http://172.16.28.101/some/path", ++ "http://172.16.28.101:8080/some/path", ++ "http://[::1]/some/path", ++ "http://[fc01:172:16:28::101]/some/path", ++ "http://[fc01:172:16:28::101]:80/some/path", ++ "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]/some/path", ++ "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/some/path", ++ "http://172.16.28.101/", ++ "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/", ++ ++ // "foo:@some.place", TODO - foo:@some.place is change to foo@some.place in the test ++ "foo:bar@some.place", ++ "foo:bar@some.place/", ++ "http://foo:bar@some.place", ++ "http://foo:bar@some.place/", ++ "http://foo:bar@[::1]:8080/", ++ "http://foo@[::1]", ++ ++ "mms://sm02.tsqa.example.com/0102rally.asf", ++ "pnm://foo:bar@some.place:80/path;params?query#fragment", ++ "rtsp://foo:bar@some.place:80/path;params?query#fragment", ++ "rtspu://foo:bar@some.place:80/path;params?query#fragment", ++ "/finance/external/cbsm/*http://cbs.marketwatch.com/archive/19990713/news/current/net.htx?source=blq/yhoo&dist=yhoo", ++ "http://a.b.com/xx.jpg?newpath=http://bob.dave.com", + +- "foo:bar@some.place", "foo:bar@some.place/", "http://foo:bar@some.place", "http://foo:bar@some.place/", +- "http://foo:bar@[::1]:8080/", "http://foo@[::1]", ++ "ht-tp://a.b.com", ++ "ht+tp://a.b.com", ++ "ht.tp://a.b.com", + +- "mms://sm02.tsqa.example.com/0102rally.asf", "pnm://foo:bar@some.place:80/path;params?query#fragment", +- "rtsp://foo:bar@some.place:80/path;params?query#fragment", "rtspu://foo:bar@some.place:80/path;params?query#fragment", +- "/finance/external/cbsm/*http://cbs.marketwatch.com/archive/19990713/news/current/net.htx?source=blq/yhoo&dist=yhoo", +- "http://a.b.com/xx.jpg?newpath=http://bob.dave.com"}; ++ "h1ttp://a.b.com", ++ "http1://a.b.com", ++ }; + + static const char *bad[] = { + "http://[1:2:3:4:5:6:7:8:9]", + "http://1:2:3:4:5:6:7:8:A:B", + "http://bob.com[::1]", + "http://[::1].com", ++ + "http://foo:bar:baz@bob.com/", + "http://foo:bar:baz@[::1]:8080/", ++ + "http://]", + "http://:", ++ + "http:/", ++ "http:/foo.bar.com/", ++ "~http://invalid.char.in.scheme/foo", ++ "http~://invalid.char.in.scheme/foo", ++ "ht~tp://invalid.char.in.scheme/foo", ++ "1http://first.char.not.alpha", ++ "some.domain.com/http://invalid.domain/foo", ++ ":", ++ "://", ++ ++ // maybe this should be a valid URL ++ "a.b.com/xx.jpg?newpath=http://bob.dave.com", + }; + + int err, failed; +@@ -400,6 +456,7 @@ + url.create(nullptr); + err = url.parse(&start, end); + if (err < 0) { ++ printf("Failed to parse url '%s'\n", start); + failed = 1; + break; + } +@@ -448,6 +505,8 @@ + failed = 1; + printf("Successfully parsed invalid url '%s'", x); + break; ++ } else { ++ printf(" bad URL - PARSE FAILED: '%s'\n", bad[i]); + } + } + +--- trafficserver.orig/proxy/hdrs/URL.cc 2020-04-16 19:03:21.522369124 +0200 ++++ trafficserver/proxy/hdrs/URL.cc 2020-04-16 19:03:21.522369124 +0200 +@@ -1120,34 +1120,41 @@ + const char *scheme_end = nullptr; + int scheme_wks_idx; + ++ // Skip over spaces + while (' ' == *cur && ++cur < end) { +- ; + } ++ + if (cur < end) { + scheme_start = scheme_end = cur; +- // special case 'http:' for performance +- if ((end - cur >= 5) && (((cur[0] ^ 'h') | (cur[1] ^ 't') | (cur[2] ^ 't') | (cur[3] ^ 'p') | (cur[4] ^ ':')) == 0)) { +- scheme_end = cur + 4; // point to colon +- url_scheme_set(heap, url, scheme_start, URL_WKSIDX_HTTP, 4, copy_strings_p); +- } else if ('/' != *cur) { +- // For forward transparent mode, the URL for the method can just be a path, +- // so don't scan that for a scheme, as we could find a false positive if there +- // is a URL in the parameters (which is legal). ++ ++ // If the URL is more complex then a path, parse to see if there is a scheme ++ if ('/' != *cur) { ++ // Search for a : it could be part of a scheme or a username:password + while (':' != *cur && ++cur < end) { +- ; + } +- if (cur < end) { // found a colon +- scheme_wks_idx = hdrtoken_tokenize(scheme_start, cur - scheme_start, &scheme_wks); + +- /* Distinguish between a scheme only and a username by looking past the colon. If it is missing +- or it's a slash, presume scheme. Otherwise it's a username with a password. +- */ +- if ((scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME) || // known scheme +- (cur >= end - 1 || cur[1] == '/')) // no more data or slash past colon +- { +- scheme_end = cur; +- url_scheme_set(heap, url, scheme_start, scheme_wks_idx, scheme_end - scheme_start, copy_strings_p); ++ // If there is a :// then there is a scheme ++ if (cur + 2 < end && cur[1] == '/' && cur[2] == '/') { // found "://" ++ scheme_end = cur; ++ scheme_wks_idx = hdrtoken_tokenize(scheme_start, scheme_end - scheme_start, &scheme_wks); ++ ++ if (!(scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME)) { ++ // Unknown scheme, validate the scheme ++ ++ // RFC 3986 Section 3.1 ++ // These are the valid characters in a scheme: ++ // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ++ // return an error if there is another character in the scheme ++ if (!ParseRules::is_alpha(*scheme_start)) { ++ return PARSE_RESULT_ERROR; ++ } ++ for (cur = scheme_start + 1; cur < scheme_end; ++cur) { ++ if (!(ParseRules::is_alnum(*cur) != 0 || *cur == '+' || *cur == '-' || *cur == '.')) { ++ return PARSE_RESULT_ERROR; ++ } ++ } + } ++ url_scheme_set(heap, url, scheme_start, scheme_wks_idx, scheme_end - scheme_start, copy_strings_p); + } + } + *start = scheme_end; diff --git a/patches/0016-CVE-2019-17565.patch b/patches/0016-CVE-2019-17565.patch new file mode 100644 index 00000000..ca4ce750 --- /dev/null +++ b/patches/0016-CVE-2019-17565.patch @@ -0,0 +1,262 @@ +Description: Fix for CVE-2019-17565 +Author: Bryan Call +Origin: backport +Applied-Upstream: https://github.com/apache/trafficserver/pull/6398 +Last-Update: 2020-04-16 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- trafficserver.orig/iocore/net/UnixNetVConnection.cc 2020-04-16 19:12:51.968334236 +0200 ++++ trafficserver/iocore/net/UnixNetVConnection.cc 2020-04-16 19:12:51.964334223 +0200 +@@ -933,7 +933,7 @@ + try_to_write = 0; + + while (niov < NET_MAX_IOV) { +- int64_t wavail = towrite - total_written; ++ int64_t wavail = towrite - total_written - try_to_write; + int64_t len = tmp_reader->block_read_avail(); + + // Check if we have done this block. +--- trafficserver.orig/proxy/http/HttpTransact.cc 2020-04-16 19:12:51.968334236 +0200 ++++ trafficserver/proxy/http/HttpTransact.cc 2020-04-16 19:12:51.964334223 +0200 +@@ -6785,9 +6785,8 @@ + + // check that the client is HTTP 1.1 and the conf allows chunking or the client + // protocol unchunks before returning to the user agent (i.e. is http/2) +- if (s->client_info.http_version == HTTPVersion(1, 1) && +- (s->txn_conf->chunking_enabled == 1 || +- (s->state_machine->plugin_tag && (!strncmp(s->state_machine->plugin_tag, "http/2", 6)))) && ++ if (s->client_info.http_version == HTTPVersion(1, 1) && s->txn_conf->chunking_enabled == 1 && ++ s->state_machine->ua_txn->is_chunked_encoding_supported() && + // if we're not sending a body, don't set a chunked header regardless of server response + !is_response_body_precluded(s->hdr_info.client_response.status_get(), s->method) && + // we do not need chunked encoding for internal error messages +--- trafficserver.orig/proxy/http/HttpTunnel.cc 2020-04-16 19:12:51.968334236 +0200 ++++ trafficserver/proxy/http/HttpTunnel.cc 2020-04-16 19:12:51.968334236 +0200 +@@ -871,10 +871,9 @@ + consumer_n = (producer_n = INT64_MAX); + } + +- // Do the IO on the consumers first so +- // data doesn't disappear out from +- // under the tunnel +- for (c = p->consumer_list.head; c;) { ++ // At least set up the consumer readers first so the data ++ // doesn't disappear out from under the tunnel ++ for (c = p->consumer_list.head; c; c = c->link.next) { + // Create a reader for each consumer. The reader allows + // us to implement skip bytes + if (c->vc_type == HT_CACHE_WRITE) { +@@ -905,45 +904,6 @@ + ink_assert(c->skip_bytes <= c->buffer_reader->read_avail()); + c->buffer_reader->consume(c->skip_bytes); + } +- int64_t c_write = consumer_n; +- +- // INKqa05109 - if we don't know the length leave it at +- // INT64_MAX or else the cache may bounce the write +- // because it thinks the document is too big. INT64_MAX +- // is a special case for the max document size code +- // in the cache +- if (c_write != INT64_MAX) { +- c_write -= c->skip_bytes; +- } +- // Fix for problems with not chunked content being chunked and +- // not sending the entire data. The content length grows when +- // it is being chunked. +- if (p->do_chunking == true) { +- c_write = INT64_MAX; +- } +- +- if (c_write == 0) { +- // Nothing to do, call back the cleanup handlers +- c->write_vio = nullptr; +- consumer_handler(VC_EVENT_WRITE_COMPLETE, c); +- } else { +- // In the client half close case, all the data that will be sent +- // from the client is already in the buffer. Go ahead and set +- // the amount to read since we know it. We will forward the FIN +- // to the server on VC_EVENT_WRITE_COMPLETE. +- if (p->vc_type == HT_HTTP_CLIENT) { +- ProxyClientTransaction *ua_vc = static_cast(p->vc); +- if (ua_vc->get_half_close_flag()) { +- c_write = c->buffer_reader->read_avail(); +- p->alive = false; +- p->handler_state = HTTP_SM_POST_SUCCESS; +- } +- } +- c->write_vio = c->vc->do_io_write(this, c_write, c->buffer_reader); +- ink_assert(c_write > 0); +- } +- +- c = c->link.next; + } + + // YTS Team, yamsat Plugin +@@ -1003,7 +963,56 @@ + producer_n = 0; + } + } ++ for (c = p->consumer_list.head; c; c = c->link.next) { ++ int64_t c_write = consumer_n; + ++ if (!p->alive) { ++ // Adjust the amount of chunked data to write if the only data was in the initial read ++ // The amount to write in some cases is dependent on the type of the consumer, so this ++ // value must be computed for each consumer ++ c_write = final_consumer_bytes_to_write(p, c); ++ } else { ++ // INKqa05109 - if we don't know the length leave it at ++ // INT64_MAX or else the cache may bounce the write ++ // because it thinks the document is too big. INT64_MAX ++ // is a special case for the max document size code ++ // in the cache ++ if (c_write != INT64_MAX) { ++ c_write -= c->skip_bytes; ++ } ++ // Fix for problems with not chunked content being chunked and ++ // not sending the entire data. The content length grows when ++ // it is being chunked. ++ if (p->do_chunking == true) { ++ c_write = INT64_MAX; ++ } ++ } ++ ++ if (c_write == 0) { ++ // Nothing to do, call back the cleanup handlers ++ c->write_vio = nullptr; ++ consumer_handler(VC_EVENT_WRITE_COMPLETE, c); ++ } else { ++ // In the client half close case, all the data that will be sent ++ // from the client is already in the buffer. Go ahead and set ++ // the amount to read since we know it. We will forward the FIN ++ // to the server on VC_EVENT_WRITE_COMPLETE. ++ if (p->vc_type == HT_HTTP_CLIENT) { ++ ProxyClientTransaction *ua_vc = static_cast(p->vc); ++ if (ua_vc->get_half_close_flag()) { ++ int tmp = c->buffer_reader->read_avail(); ++ if (tmp < c_write) { ++ c_write = tmp; ++ } ++ p->alive = false; ++ p->handler_state = HTTP_SM_POST_SUCCESS; ++ } ++ } ++ // Start the writes now that we know we will consume all the initial data ++ c->write_vio = c->vc->do_io_write(this, c_write, c->buffer_reader); ++ ink_assert(c_write > 0); ++ } ++ } + if (p->alive) { + ink_assert(producer_n >= 0); + +@@ -1184,8 +1193,8 @@ + } + } // end of added logic for partial copy of POST + +- Debug("http_redirect", "[HttpTunnel::producer_handler] enable_redirection: [%d %d %d] event: %d", p->alive == true, +- sm->enable_redirection, (p->self_consumer && p->self_consumer->alive == true), event); ++ Debug("http_redirect", "[HttpTunnel::producer_handler] enable_redirection: [%d %d %d] event: %d, state: %d", p->alive == true, ++ sm->enable_redirection, (p->self_consumer && p->self_consumer->alive == true), event, p->chunked_handler.state); + + switch (event) { + case VC_EVENT_READ_READY: +@@ -1470,21 +1479,18 @@ + } + } + +-// void HttpTunnel::chain_finish_internal(HttpTunnelProducer* p) + // +-// Internal function for finishing all consumers. Takes +-// chain argument about where to finish just immediate +-// consumer or all those downstream ++// Determine the number of bytes a consumer should read from a producer + // +-void +-HttpTunnel::finish_all_internal(HttpTunnelProducer *p, bool chain) ++int64_t ++HttpTunnel::final_consumer_bytes_to_write(HttpTunnelProducer *p, HttpTunnelConsumer *c) + { +- ink_assert(p->alive == false); +- HttpTunnelConsumer *c = p->consumer_list.head; +- int64_t total_bytes = 0; +- TunnelChunkingAction_t action = p->chunking_action; +- +- while (c) { ++ int64_t total_bytes = 0; ++ int64_t consumer_n = 0; ++ if (p->alive) { ++ consumer_n = INT64_MAX; ++ } else { ++ TunnelChunkingAction_t action = p->chunking_action; + if (c->alive) { + if (c->vc_type == HT_CACHE_WRITE) { + switch (action) { +@@ -1503,12 +1509,48 @@ + total_bytes = p->chunked_handler.skip_bytes + p->chunked_handler.chunked_size; + } else if (action == TCA_DECHUNK_CONTENT) { + total_bytes = p->chunked_handler.skip_bytes + p->chunked_handler.dechunked_size; ++ } else if (action == TCA_PASSTHRU_CHUNKED_CONTENT) { ++ total_bytes = p->bytes_read + p->init_bytes_done; + } else { + total_bytes = p->bytes_read + p->init_bytes_done; + } ++ consumer_n = total_bytes - c->skip_bytes; ++ } ++ } ++ return consumer_n; ++} ++ ++// ++// void HttpTunnel::finish_all_internal(HttpTunnelProducer* p) ++// ++// Internal function for finishing all consumers. Takes ++// chain argument about where to finish just immediate ++// consumer or all those downstream ++// ++void ++HttpTunnel::finish_all_internal(HttpTunnelProducer *p, bool chain) ++{ ++ ink_assert(p->alive == false); ++ HttpTunnelConsumer *c = p->consumer_list.head; ++ int64_t total_bytes = 0; ++ TunnelChunkingAction_t action = p->chunking_action; ++ ++ if (action == TCA_PASSTHRU_CHUNKED_CONTENT) { ++ // if the only chunked data was in the initial read, make sure we don't consume too much ++ if (p->bytes_read == 0) { ++ int num_read = p->buffer_start->read_avail() - p->chunked_handler.chunked_reader->read_avail(); ++ if (num_read < p->init_bytes_done) { ++ p->init_bytes_done = num_read; ++ } ++ } ++ } + ++ while (c) { ++ if (c->alive) { + if (c->write_vio) { +- c->write_vio->nbytes = total_bytes - c->skip_bytes; ++ // Adjust the number of bytes to write in the case of ++ // a completed unlimited producer ++ c->write_vio->nbytes = final_consumer_bytes_to_write(p, c); + ink_assert(c->write_vio->nbytes >= 0); + + if (c->write_vio->nbytes < 0) { +@@ -1525,7 +1567,7 @@ + // is nothing to do. Check to see if there is + // nothing to do and take the appripriate + // action +- if (c->write_vio && c->write_vio->nbytes == c->write_vio->ndone) { ++ if (c->write_vio && c->alive && c->write_vio->nbytes == c->write_vio->ndone) { + consumer_handler(VC_EVENT_WRITE_COMPLETE, c); + } + } +--- trafficserver.orig/proxy/http/HttpTunnel.h 2020-04-16 19:12:51.968334236 +0200 ++++ trafficserver/proxy/http/HttpTunnel.h 2020-04-16 19:12:51.968334236 +0200 +@@ -312,6 +312,7 @@ + void chain_abort_all(HttpTunnelProducer *p); + void abort_cache_write_finish_others(HttpTunnelProducer *p); + void append_message_to_producer_buffer(HttpTunnelProducer *p, const char *msg, int64_t msg_len); ++ int64_t final_consumer_bytes_to_write(HttpTunnelProducer *p, HttpTunnelConsumer *c); + + /** Mark a producer and consumer as the same underlying object. + diff --git a/patches/0016-CVE-2020-1944.patch b/patches/0016-CVE-2020-1944.patch new file mode 100644 index 00000000..19d93e7c --- /dev/null +++ b/patches/0016-CVE-2020-1944.patch @@ -0,0 +1,48 @@ +Description: Fix for CVE-2020-1944 +Author: Bryan Call +Origin: backport +Applied-Upstream: https://github.com/apache/trafficserver/pull/6390 +Last-Update: 2020-04-16 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- trafficserver.orig/proxy/hdrs/HTTP.cc 2020-04-16 19:20:19.621738649 +0200 ++++ trafficserver/proxy/hdrs/HTTP.cc 2020-04-16 19:23:55.166369632 +0200 +@@ -1125,19 +1125,18 @@ + + end = real_end; + parser->m_parsing_http = false; +- +- ParseResult ret = mime_parser_parse(&parser->m_mime_parser, heap, hh->m_fields_impl, start, end, must_copy_strings, eof); +- // If we're done with the main parse do some validation +- if (ret == PARSE_RESULT_DONE) { +- ret = validate_hdr_host(hh); // check HOST header +- } +- if (ret == PARSE_RESULT_DONE) { +- ret = validate_hdr_content_length(heap, hh); +- } +- return ret; + } + +- return mime_parser_parse(&parser->m_mime_parser, heap, hh->m_fields_impl, start, end, must_copy_strings, eof); ++ ParseResult ret = ++ mime_parser_parse(&parser->m_mime_parser, heap, hh->m_fields_impl, start, end, must_copy_strings, eof); ++ // If we're done with the main parse do some validation ++ if (ret == PARSE_RESULT_DONE) { ++ ret = validate_hdr_host(hh); // check HOST header ++ } ++ if (ret == PARSE_RESULT_DONE) { ++ ret = validate_hdr_content_length(heap, hh); ++ } ++ return ret; + } + + ParseResult +@@ -1189,7 +1188,7 @@ + if (mime_hdr_field_find(hh->m_fields_impl, MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING) != nullptr) { + // Delete all Content-Length headers + Debug("http", "Transfer-Encoding header and Content-Length headers the request, removing all Content-Length headers"); +- mime_hdr_field_delete(heap, hh->m_fields_impl, content_length_field); ++ mime_hdr_field_delete(heap, hh->m_fields_impl, content_length_field, true); + return PARSE_RESULT_DONE; + } + diff --git a/patches/0016-CVE-2020-9481.patch b/patches/0016-CVE-2020-9481.patch new file mode 100644 index 00000000..f865a712 --- /dev/null +++ b/patches/0016-CVE-2020-9481.patch @@ -0,0 +1,318 @@ +Index: trafficserver/doc/admin-guide/files/records.config.en.rst +=================================================================== +--- trafficserver.orig/doc/admin-guide/files/records.config.en.rst 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/doc/admin-guide/files/records.config.en.rst 2020-04-16 21:35:49.123162682 +0200 +@@ -3493,7 +3493,7 @@ + :ts:cv:`proxy.config.http2.min_concurrent_streams_in`. + To disable, set to zero (``0``). + +-.. ts:cv:: CONFIG proxy.config.http2.initial_window_size_in INT 1048576 ++.. ts:cv:: CONFIG proxy.config.http2.initial_window_size_in INT 65535 + :reloadable: + + The initial window size for inbound connections. +Index: trafficserver/mgmt/RecordsConfig.cc +=================================================================== +--- trafficserver.orig/mgmt/RecordsConfig.cc 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/mgmt/RecordsConfig.cc 2020-04-16 21:35:49.123162682 +0200 +@@ -1304,7 +1304,7 @@ + , + {RECT_CONFIG, "proxy.config.http2.max_active_streams_in", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} + , +- {RECT_CONFIG, "proxy.config.http2.initial_window_size_in", RECD_INT, "1048576", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} ++ {RECT_CONFIG, "proxy.config.http2.initial_window_size_in", RECD_INT, "65535", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} + , + {RECT_CONFIG, "proxy.config.http2.max_frame_size", RECD_INT, "16384", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} + , +Index: trafficserver/proxy/http2/HTTP2.cc +=================================================================== +--- trafficserver.orig/proxy/http2/HTTP2.cc 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/HTTP2.cc 2020-04-16 21:35:49.123162682 +0200 +@@ -722,7 +722,7 @@ + uint32_t Http2::max_active_streams_in = 0; + bool Http2::throttling = false; + uint32_t Http2::stream_priority_enabled = 0; +-uint32_t Http2::initial_window_size = 1048576; ++uint32_t Http2::initial_window_size = 65535; + uint32_t Http2::max_frame_size = 16384; + uint32_t Http2::header_table_size = 4096; + uint32_t Http2::max_header_list_size = 4294967295; +Index: trafficserver/proxy/http2/Http2ClientSession.cc +=================================================================== +--- trafficserver.orig/proxy/http2/Http2ClientSession.cc 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2ClientSession.cc 2020-04-16 21:35:49.123162682 +0200 +@@ -348,11 +348,9 @@ + break; + + case VC_EVENT_WRITE_READY: +- retval = 0; +- break; +- + case VC_EVENT_WRITE_COMPLETE: +- // Seems as this is being closed already ++ this->connection_state.restart_streams(); ++ + retval = 0; + break; + +@@ -586,3 +584,9 @@ + // Do something else every 128 incoming frames + return (this->_n_frame_read & 0x7F) == 0; + } ++ ++int64_t ++Http2ClientSession::write_avail() ++{ ++ return this->write_buffer->write_avail(); ++} +Index: trafficserver/proxy/http2/Http2ClientSession.h +=================================================================== +--- trafficserver.orig/proxy/http2/Http2ClientSession.h 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2ClientSession.h 2020-04-16 21:35:49.123162682 +0200 +@@ -319,6 +319,8 @@ + return write_buffer->max_read_avail(); + } + ++ int64_t write_avail(); ++ + // noncopyable + Http2ClientSession(Http2ClientSession &) = delete; + Http2ClientSession &operator=(const Http2ClientSession &) = delete; +Index: trafficserver/proxy/http2/Http2ConnectionState.cc +=================================================================== +--- trafficserver.orig/proxy/http2/Http2ConnectionState.cc 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2ConnectionState.cc 2020-04-16 21:35:49.127162696 +0200 +@@ -151,6 +151,12 @@ + cstate.decrement_server_rwnd(payload_length); + stream->decrement_server_rwnd(payload_length); + ++ if (is_debug_tag_set("http2_con")) { ++ uint32_t rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE); ++ Http2StreamDebug(cstate.ua_session, stream->get_id(), "Received DATA frame: rwnd con=%zd/%" PRId32 " stream=%zd/%" PRId32, ++ cstate.server_rwnd(), rwnd, stream->server_rwnd(), rwnd); ++ } ++ + const uint32_t unpadded_length = payload_length - pad_length; + // If we call write() multiple times, we must keep the same reader, so we can + // update its offset via consume. Otherwise, we will read the same data on the +@@ -172,21 +178,6 @@ + } + myreader->writer()->dealloc_reader(myreader); + +- uint32_t initial_rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE); +- uint32_t min_rwnd = std::min(initial_rwnd, cstate.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE)); +- // Connection level WINDOW UPDATE +- if (cstate.server_rwnd() <= min_rwnd) { +- Http2WindowSize diff_size = initial_rwnd - cstate.server_rwnd(); +- cstate.increment_server_rwnd(diff_size); +- cstate.send_window_update_frame(0, diff_size); +- } +- // Stream level WINDOW UPDATE +- if (stream->server_rwnd() <= min_rwnd) { +- Http2WindowSize diff_size = initial_rwnd - stream->server_rwnd(); +- stream->increment_server_rwnd(diff_size); +- cstate.send_window_update_frame(stream->get_id(), diff_size); +- } +- + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE); + } + +@@ -1196,6 +1187,35 @@ + } + + void ++Http2ConnectionState::restart_receiving(Http2Stream *stream) ++{ ++ uint32_t initial_rwnd = this->server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE); ++ uint32_t min_rwnd = std::min(initial_rwnd, this->server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE)); ++ ++ // Connection level WINDOW UPDATE ++ if (this->server_rwnd() < min_rwnd) { ++ Http2WindowSize diff_size = initial_rwnd - this->server_rwnd(); ++ this->increment_server_rwnd(diff_size); ++ this->send_window_update_frame(0, diff_size); ++ } ++ ++ // Stream level WINDOW UPDATE ++ if (stream == nullptr || stream->server_rwnd() >= min_rwnd) { ++ return; ++ } ++ ++ // If read_vio is buffering data, do not fully update window ++ int64_t data_size = stream->read_vio_read_avail(); ++ if (data_size >= initial_rwnd) { ++ return; ++ } ++ ++ Http2WindowSize diff_size = initial_rwnd - std::max(static_cast(stream->server_rwnd()), data_size); ++ stream->increment_server_rwnd(diff_size); ++ this->send_window_update_frame(stream->get_id(), diff_size); ++} ++ ++void + Http2ConnectionState::cleanup_streams() + { + Http2Stream *s = stream_list.head; +@@ -1415,6 +1435,12 @@ + return Http2SendDataFrameResult::ERROR; + } + ++ SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread()); ++ if (this->ua_session->write_avail() == 0) { ++ Http2StreamDebug(this->ua_session, stream->get_id(), "Not write avail"); ++ return Http2SendDataFrameResult::NOT_WRITE_AVAIL; ++ } ++ + // Select appropriate payload length + if (read_available_size > 0) { + // We only need to check for window size when there is a payload +@@ -1458,7 +1484,6 @@ + current_reader->consume(payload_length); + + // xmit event +- SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread()); + this->ua_session->handleEvent(HTTP2_SESSION_EVENT_XMIT, &data); + + if (flags & HTTP2_FLAGS_DATA_END_STREAM) { +@@ -1845,7 +1870,7 @@ + void + Http2ConnectionState::send_window_update_frame(Http2StreamId id, uint32_t size) + { +- Http2StreamDebug(ua_session, id, "Send WINDOW_UPDATE frame"); ++ Http2StreamDebug(ua_session, id, "Send WINDOW_UPDATE frame: size=%" PRIu32, size); + + // Create WINDOW_UPDATE frame + Http2Frame window_update(HTTP2_FRAME_TYPE_WINDOW_UPDATE, id, 0x0); +Index: trafficserver/proxy/http2/Http2ConnectionState.h +=================================================================== +--- trafficserver.orig/proxy/http2/Http2ConnectionState.h 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2ConnectionState.h 2020-04-16 21:35:49.127162696 +0200 +@@ -34,6 +34,7 @@ + NO_ERROR = 0, + NO_WINDOW, + NO_PAYLOAD, ++ NOT_WRITE_AVAIL, + ERROR, + DONE, + }; +@@ -128,6 +129,8 @@ + void + init() + { ++ this->_server_rwnd = Http2::initial_window_size; ++ + local_hpack_handle = new HpackHandle(HTTP2_HEADER_TABLE_SIZE); + remote_hpack_handle = new HpackHandle(HTTP2_HEADER_TABLE_SIZE); + dependency_tree = new DependencyTree(Http2::max_concurrent_streams_in); +@@ -171,6 +174,7 @@ + void release_stream(Http2Stream *stream); + void cleanup_streams(); + ++ void restart_receiving(Http2Stream *stream); + void update_initial_rwnd(Http2WindowSize new_size); + + Http2StreamId +@@ -357,7 +361,7 @@ + + // Connection level window size + ssize_t _client_rwnd = HTTP2_INITIAL_WINDOW_SIZE; +- ssize_t _server_rwnd = Http2::initial_window_size; ++ ssize_t _server_rwnd = 0; + + std::vector _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX}; + int _recent_rwnd_increment_index = 0; +Index: trafficserver/proxy/http2/Http2Stream.cc +=================================================================== +--- trafficserver.orig/proxy/http2/Http2Stream.cc 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2Stream.cc 2020-04-16 21:35:49.127162696 +0200 +@@ -524,6 +524,19 @@ + void + Http2Stream::restart_sending() + { ++ if (!this->response_header_done) { ++ return; ++ } ++ ++ IOBufferReader *reader = this->response_get_data_reader(); ++ if (reader && !reader->is_read_avail_more_than(0)) { ++ return; ++ } ++ ++ if (this->write_vio.mutex && this->write_vio.ntodo() == 0) { ++ return; ++ } ++ + this->send_response_body(true); + } + +@@ -705,6 +718,12 @@ + SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); + update_write_request(vio->get_reader(), INT64_MAX, true); + } else if (vio->op == VIO::READ) { ++ Http2ClientSession *h2_proxy_ssn = static_cast(this->get_parent()); ++ { ++ SCOPED_MUTEX_LOCK(lock, h2_proxy_ssn->connection_state.mutex, this_ethread()); ++ h2_proxy_ssn->connection_state.restart_receiving(this); ++ } ++ + SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); + update_read_request(INT64_MAX, true); + } +@@ -964,3 +983,14 @@ + current_reader = nullptr; // State machine is on its own way down. + this->do_io_close(); + } ++ ++int64_t ++Http2Stream::read_vio_read_avail() ++{ ++ MIOBuffer *writer = this->read_vio.get_writer(); ++ if (writer) { ++ return writer->max_read_avail(); ++ } ++ ++ return 0; ++} +Index: trafficserver/proxy/http2/Http2Stream.h +=================================================================== +--- trafficserver.orig/proxy/http2/Http2Stream.h 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2Stream.h 2020-04-16 21:35:49.127162696 +0200 +@@ -38,10 +38,7 @@ + { + public: + typedef ProxyClientTransaction super; ///< Parent type. +- Http2Stream(Http2StreamId sid = 0, ssize_t initial_rwnd = Http2::initial_window_size) : _id(sid), _client_rwnd(initial_rwnd) +- { +- SET_HANDLER(&Http2Stream::main_event_handler); +- } ++ Http2Stream() { SET_HANDLER(&Http2Stream::main_event_handler); } + + void + init(Http2StreamId sid, ssize_t initial_rwnd) +@@ -50,6 +47,7 @@ + _start_time = Thread::get_hrtime(); + _thread = this_ethread(); + this->_client_rwnd = initial_rwnd; ++ this->_server_rwnd = Http2::initial_window_size; + HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_STREAM_COUNT, _thread); + HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_CLIENT_STREAM_COUNT, _thread); + sm_reader = request_reader = request_buffer.alloc_reader(); +@@ -177,6 +175,7 @@ + ssize_t server_rwnd() const; + Http2ErrorCode increment_server_rwnd(size_t amount); + Http2ErrorCode decrement_server_rwnd(size_t amount); ++ int64_t read_vio_read_avail(); + + uint8_t *header_blocks = nullptr; + uint32_t header_blocks_length = 0; // total length of header blocks (not include +@@ -292,8 +291,8 @@ + uint64_t data_length = 0; + uint64_t bytes_sent = 0; + +- ssize_t _client_rwnd; +- ssize_t _server_rwnd = Http2::initial_window_size; ++ ssize_t _client_rwnd = 0; ++ ssize_t _server_rwnd = 0; + + std::vector _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX}; + int _recent_rwnd_increment_index = 0; diff --git a/patches/0017-CVE-2020-9494.patch b/patches/0017-CVE-2020-9494.patch new file mode 100644 index 00000000..1fb0aa44 --- /dev/null +++ b/patches/0017-CVE-2020-9494.patch @@ -0,0 +1,131 @@ +Author: Bryan Call +Origin: backport +Applied-Upstream: https://github.com/apache/trafficserver/pull/6922 +Last-Update: 2020-06-25 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/proxy/http2/HPACK.cc ++++ b/proxy/http2/HPACK.cc +@@ -944,7 +944,11 @@ hpack_decode_header_block(HpackIndexingT + + field->name_get(&name_len); + field->value_get(&value_len); +- total_header_size += name_len + value_len; ++ ++ // [RFC 7540] 6.5.2. SETTINGS_MAX_HEADER_LIST_SIZE: ++ // The value is based on the uncompressed size of header fields, including the length of the name and value in octets plus an ++ // overhead of 32 octets for each header field. ++ total_header_size += name_len + value_len + ADDITIONAL_OCTETS; + + if (total_header_size > max_header_size) { + return HPACK_ERROR_SIZE_EXCEEDED_ERROR; +--- a/proxy/http2/Http2ConnectionState.cc ++++ b/proxy/http2/Http2ConnectionState.cc +@@ -227,13 +227,6 @@ rcv_headers_frame(Http2ConnectionState & + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE); + } + +- // keep track of how many bytes we get in the frame +- stream->request_header_length += payload_length; +- if (stream->request_header_length > Http2::max_request_header_size) { +- return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, +- "recv headers payload for headers greater than header length"); +- } +- + Http2HeadersParameter params; + uint32_t header_block_fragment_offset = 0; + uint32_t header_block_fragment_length = payload_length; +@@ -252,7 +245,8 @@ rcv_headers_frame(Http2ConnectionState & + "recv headers failed to parse"); + } + +- if (params.pad_length > payload_length) { ++ // Payload length can't be smaller than the pad length ++ if ((params.pad_length + HTTP2_HEADERS_PADLEN_LEN) > header_block_fragment_length) { + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, + "recv headers pad > payload length"); + } +@@ -268,7 +262,7 @@ rcv_headers_frame(Http2ConnectionState & + frame.reader()->memcpy(buf, HTTP2_PRIORITY_LEN, header_block_fragment_offset); + if (!http2_parse_priority_parameter(make_iovec(buf, HTTP2_PRIORITY_LEN), params.priority)) { + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, +- "recv headers prioirity parameters failed parse"); ++ "recv headers priority parameters failed parse"); + } + // Protocol error if the stream depends on itself + if (stream_id == params.priority.stream_dependency) { +@@ -276,6 +270,12 @@ rcv_headers_frame(Http2ConnectionState & + "recv headers self dependency"); + } + ++ // Payload length can't be smaller than the priority length ++ if (HTTP2_PRIORITY_LEN > header_block_fragment_length) { ++ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, ++ "recv priority length > payload length"); ++ } ++ + header_block_fragment_offset += HTTP2_PRIORITY_LEN; + header_block_fragment_length -= HTTP2_PRIORITY_LEN; + } +@@ -295,11 +295,20 @@ rcv_headers_frame(Http2ConnectionState & + } + } + ++ stream->header_blocks_length = header_block_fragment_length; ++ ++ // ATS advertises SETTINGS_MAX_HEADER_LIST_SIZE as a limit of total header blocks length. (Details in [RFC 7560] 10.5.1.) ++ // Make it double to relax the limit in cases of 1) HPACK is used naively, or 2) Huffman Encoding generates large header blocks. ++ // The total "decoded" header length is strictly checked by hpack_decode_header_block(). ++ if (stream->header_blocks_length > std::max(Http2::max_header_list_size, Http2::max_header_list_size * 2)) { ++ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM, ++ "header blocks too large"); ++ } ++ + if (header_block_fragment_length > 0) { + stream->header_blocks = static_cast(ats_malloc(header_block_fragment_length)); + frame.reader()->memcpy(stream->header_blocks, header_block_fragment_length, header_block_fragment_offset); + +- stream->header_blocks_length = header_block_fragment_length; + } + + if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS) { +@@ -830,16 +839,17 @@ rcv_continuation_frame(Http2ConnectionSt + } + } + +- // keep track of how many bytes we get in the frame +- stream->request_header_length += payload_length; +- if (stream->request_header_length > Http2::max_request_header_size) { +- return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, +- "continuation payload for headers exceeded"); +- } +- + uint32_t header_blocks_offset = stream->header_blocks_length; + stream->header_blocks_length += payload_length; + ++ // ATS advertises SETTINGS_MAX_HEADER_LIST_SIZE as a limit of total header blocks length. (Details in [RFC 7560] 10.5.1.) ++ // Make it double to relax the limit in cases of 1) HPACK is used naively, or 2) Huffman Encoding generates large header blocks. ++ // The total "decoded" header length is strictly checked by hpack_decode_header_block(). ++ if (stream->header_blocks_length > std::max(Http2::max_header_list_size, Http2::max_header_list_size * 2)) { ++ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM, ++ "header blocks too large"); ++ } ++ + stream->header_blocks = static_cast(ats_realloc(stream->header_blocks, stream->header_blocks_length)); + frame.reader()->memcpy(stream->header_blocks + header_blocks_offset, payload_length); + +--- a/proxy/http2/Http2Stream.h ++++ b/proxy/http2/Http2Stream.h +@@ -178,10 +178,8 @@ public: + int64_t read_vio_read_avail(); + + uint8_t *header_blocks = nullptr; +- uint32_t header_blocks_length = 0; // total length of header blocks (not include +- // Padding or other fields) +- uint32_t request_header_length = 0; // total length of payload (include Padding +- // and other fields) ++ uint32_t header_blocks_length = 0; // total length of header blocks (not include Padding or other fields) ++ + bool recv_end_stream = false; + bool send_end_stream = false; + diff --git a/patches/0018-CVE-2020-17508.patch b/patches/0018-CVE-2020-17508.patch new file mode 100644 index 00000000..921678f6 --- /dev/null +++ b/patches/0018-CVE-2020-17508.patch @@ -0,0 +1,529 @@ +Author: Brian Neradt +Origin: backport +Applied-upstream: https://github.com/apache/trafficserver/pull/7358 +Last-Update: 2020-12-06 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/doc/admin-guide/plugins/esi.en.rst ++++ b/doc/admin-guide/plugins/esi.en.rst +@@ -80,11 +80,11 @@ Enabling ESI + + 2. There are four options you can add to the above. + +-- "--private-response" will add private cache control and expires header to the processed ESI document. +-- "--packed-node-support" will enable the support for using packed node, which will improve the performance of parsing ++- ``--private-response`` will add private cache control and expires header to the processed ESI document. ++- ``--packed-node-support`` will enable the support for using packed node, which will improve the performance of parsing + cached ESI document. +-- "--disable-gzip-output" will disable gzipped output, which will NOT gzip the output anyway. +-- "--first-byte-flush" will enable the first byte flush feature, which will flush content to users as soon as the entire ++- ``--disable-gzip-output`` will disable gzipped output, which will NOT gzip the output anyway. ++- ``--first-byte-flush`` will enable the first byte flush feature, which will flush content to users as soon as the entire + ESI document is received and parsed without all ESI includes fetched (the flushing will stop at the ESI include markup + till that include is fetched). + +--- a/plugins/esi/esi.cc ++++ b/plugins/esi/esi.cc +@@ -776,7 +776,7 @@ transformData(TSCont contp) + out_data_len = 0; + out_data = ""; + } else { +- TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes", __FUNCTION__, out_data_len, ++ TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes via gzip", __FUNCTION__, out_data_len, + (int)cdata.size()); + out_data_len = cdata.size(); + out_data = cdata.data(); +@@ -841,12 +841,9 @@ transformData(TSCont contp) + if (!cont_data->esi_gzip->stream_encode(out_data, cdata)) { + TSError("[esi][%s] Error while gzipping content", __FUNCTION__); + } else { +- TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes", __FUNCTION__, (int)out_data.size(), +- (int)cdata.size()); ++ TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes via EsiGzip", __FUNCTION__, ++ (int)out_data.size(), (int)cdata.size()); + } +- } +- +- if (cont_data->gzip_output) { + if (TSIOBufferWrite(TSVIOBufferGet(cont_data->output_vio), cdata.data(), cdata.size()) == TS_ERROR) { + TSError("[esi][%s] Error while writing bytes to downstream VC", __FUNCTION__); + return 0; +--- a/plugins/esi/lib/EsiGzip.cc ++++ b/plugins/esi/lib/EsiGzip.cc +@@ -30,7 +30,7 @@ using std::string; + using namespace EsiLib; + + EsiGzip::EsiGzip(const char *debug_tag, ComponentBase::Debug debug_func, ComponentBase::Error error_func) +- : ComponentBase(debug_tag, debug_func, error_func), _downstream_length(0), _total_data_length(0) ++ : ComponentBase(debug_tag, debug_func, error_func), _downstream_length(0), _total_data_length(0), _crc(0) + { + // Zlib _zstrm varibles are initialized when they are required in runDeflateLoop + // coverity[uninit_member] +@@ -71,6 +71,7 @@ runDeflateLoop(z_stream &zstrm, int flus + bool + EsiGzip::stream_encode(const char *data, int data_len, std::string &cdata) + { ++ const auto initial_cdata_size = cdata.size(); + if (_downstream_length == 0) { + cdata.assign(GZIP_HEADER_SIZE, 0); // reserving space for the header + cdata[0] = MAGIC_BYTE_1; +@@ -111,10 +112,9 @@ EsiGzip::stream_encode(const char *data, + return false; + } + _crc = crc32(_crc, reinterpret_cast(data), data_len); +- _downstream_length += cdata.size(); + _total_data_length += data_len; + } +- ++ _downstream_length += cdata.size() - initial_cdata_size; + deflateEnd(&_zstrm); + + return true; +@@ -123,6 +123,17 @@ EsiGzip::stream_encode(const char *data, + bool + EsiGzip::stream_finish(std::string &cdata, int &downstream_length) + { ++ if (_downstream_length == 0) { ++ // We need to run encode first to get the gzip header inserted. ++ if (!stream_encode(nullptr, 0, cdata)) { ++ return false; ++ } ++ } ++ // Note that a call to stream_encode will update cdata to apply the gzip ++ // header and that call itself will update _downstream_length. Since we don't ++ // want to double count the gzip header bytes, we capture initial_cdata_size ++ // here after any possible call to stream_encode above. ++ const auto initial_cdata_size = cdata.size(); + char buf[BUF_SIZE]; + + _zstrm.zalloc = Z_NULL; +@@ -145,7 +156,7 @@ EsiGzip::stream_finish(std::string &cdat + } + append(cdata, static_cast(_crc)); + append(cdata, static_cast(_total_data_length)); +- _downstream_length += cdata.size(); ++ _downstream_length += cdata.size() - initial_cdata_size; + downstream_length = _downstream_length; + return true; + } +--- a/plugins/esi/lib/EsiGzip.h ++++ b/plugins/esi/lib/EsiGzip.h +@@ -26,6 +26,7 @@ + #include "ComponentBase.h" + #include + #include ++#include + + class EsiGzip : private EsiLib::ComponentBase + { +@@ -34,19 +35,41 @@ public: + + ~EsiGzip() override; + ++ /** Compress the provided content. ++ * ++ * @param[in] data The input data to compress. ++ * @param[in] data_len The length of the input data to compress. ++ * @param[in,out] The result of compressing the input data will be appended ++ * to cdata. ++ * ++ * @return True if the compression succeeded, false otherwise. ++ */ + bool stream_encode(const char *data, int data_len, std::string &cdata); + ++ /** A string_view overload of stream_encode. */ + inline bool +- stream_encode(std::string data, std::string &cdata) ++ stream_encode(std::string_view data, std::string &cdata) + { + return stream_encode(data.data(), data.size(), cdata); + } + ++ /** Finish the compression stream. ++ * ++ * @param[out] cdata The compressed data is appended to this. ++ * @param[out] downstream_length The total number of compressed stream bytes ++ * across all calls to stream_encode and stream_finish. ++ * ++ * @return True if the compression succeeded, false otherwise. ++ */ + bool stream_finish(std::string &cdata, int &downstream_length); + + private: +- // int runDeflateLoop(z_stream &zstrm, int flush, std::string &cdata); ++ /** The cumulative total number of bytes for the compressed stream. */ + int _downstream_length; ++ ++ /** The cumulative total number of uncompressed bytes that have been ++ * compressed. ++ */ + int _total_data_length; + z_stream _zstrm; + uLong _crc; +--- /dev/null ++++ b/tests/gold_tests/pluginTest/esi/esi.test.py +@@ -0,0 +1,310 @@ ++''' ++Test the ESI plugin. ++''' ++# Licensed to the Apache Software Foundation (ASF) under one ++# or more contributor license agreements. See the NOTICE file ++# distributed with this work for additional information ++# regarding copyright ownership. The ASF licenses this file ++# to you under the Apache License, Version 2.0 (the ++# "License"); you may not use this file except in compliance ++# with the License. You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++import os ++ ++Test.Summary = ''' ++Test the ESI plugin. ++''' ++ ++Test.SkipUnless( ++ Condition.PluginExists('esi.so'), ++) ++ ++ ++class EsiTest(): ++ """ ++ A class that encapsulates the configuration and execution of a set of EPI ++ test cases. ++ """ ++ ++ """ static: The same server Process is used across all tests. """ ++ _server = None ++ ++ """ static: A counter to keep the ATS process names unique across tests. """ ++ _ts_counter = 0 ++ ++ """ static: A counter to keep any output file names unique across tests. """ ++ _output_counter = 0 ++ ++ """ The ATS process for this set of test cases. """ ++ _ts = None ++ ++ def __init__(self, plugin_config): ++ """ ++ Args: ++ plugin_config (str): The config line to place in plugin.config for ++ the ATS process. ++ """ ++ if EsiTest._server is None: ++ EsiTest._server = EsiTest._create_server() ++ ++ self._ts = EsiTest._create_ats(self, plugin_config) ++ ++ @staticmethod ++ def _create_server(): ++ """ ++ Create and start a server process. ++ """ ++ # Configure our server. ++ server = Test.MakeOriginServer("server") ++ ++ # Generate the set of ESI responses derived right from our ESI docs. ++ # See: ++ # doc/admin-guide/plugins/esi.en.rst ++ request_header = { ++ "headers": ( ++ "GET /esi.php HTTP/1.1\r\n" ++ "Host: www.example.com\r\n" ++ "Content-Length: 0\r\n\r\n"), ++ "timestamp": "1469733493.993", ++ "body": "" ++ } ++ esi_body = r''' ++ ++ ++Hello, ++ ++ ++''' ++ response_header = { ++ "headers": ( ++ "HTTP/1.1 200 OK\r\n" ++ "Content-Type: text/html\r\n" ++ "X-Esi: 1\r\n" ++ "Connection: close\r\n" ++ "Content-Length: {}\r\n" ++ "Cache-Control: max-age=300\r\n" ++ "\r\n".format(len(esi_body))), ++ "timestamp": "1469733493.993", ++ "body": esi_body ++ } ++ server.addResponse("sessionfile.log", request_header, response_header) ++ request_header = { ++ "headers": ( ++ "GET /date.php HTTP/1.1\r\n" ++ "Host: www.example.com\r\n" ++ "Content-Length: 0\r\n\r\n"), ++ "timestamp": "1469733493.993", ++ "body": "" ++ } ++ date_body = r''' ++''' ++ response_header = { ++ "headers": ( ++ "HTTP/1.1 200 OK\r\n" ++ "Content-Type: text/html\r\n" ++ "Connection: close\r\n" ++ "Content-Length: {}\r\n" ++ "Cache-Control: max-age=300\r\n" ++ "\r\n".format(len(date_body))), ++ "timestamp": "1469733493.993", ++ "body": date_body ++ } ++ server.addResponse("sessionfile.log", request_header, response_header) ++ # Verify correct functionality with an empty body. ++ request_header = { ++ "headers": ( ++ "GET /expect_empty_body HTTP/1.1\r\n" ++ "Host: www.example.com\r\n" ++ "Content-Length: 0\r\n\r\n"), ++ "timestamp": "1469733493.993", ++ "body": "" ++ } ++ response_header = { ++ "headers": ( ++ "HTTP/1.1 200 OK\r\n" ++ "X-ESI: On\r\n" ++ "Content-Length: 0\r\n" ++ "Connection: close\r\n" ++ "Content-Type: text/html; charset=UTF-8\r\n" ++ "\r\n"), ++ "timestamp": "1469733493.993", ++ "body": "" ++ } ++ server.addResponse("sessionfile.log", request_header, response_header) ++ ++ # Create a run to start the server. ++ tr = Test.AddTestRun("Start the server.") ++ tr.Processes.Default.StartBefore(server) ++ tr.Processes.Default.Command = "echo starting the server" ++ tr.Processes.Default.ReturnCode = 0 ++ tr.StillRunningAfter = server ++ ++ return server ++ ++ @staticmethod ++ def _create_ats(self, plugin_config): ++ """ ++ Create and start an ATS process. ++ """ ++ EsiTest._ts_counter += 1 ++ ++ # Configure ATS with a vanilla ESI plugin configuration. ++ ts = Test.MakeATSProcess("ts{}".format(EsiTest._ts_counter)) ++ ts.Disk.records_config.update({ ++ 'proxy.config.diags.debug.enabled': 1, ++ 'proxy.config.diags.debug.tags': 'http|plugin_esi', ++ }) ++ ts.Disk.remap_config.AddLine( ++ 'map http://www.example.com/ http://127.0.0.1:{0}'.format(EsiTest._server.Variables.Port) ++ ) ++ ts.Disk.plugin_config.AddLine(plugin_config) ++ ++ # Create a run to start the ATS process. ++ tr = Test.AddTestRun("Start the ATS process.") ++ tr.Processes.Default.StartBefore(ts) ++ tr.Processes.Default.Command = "echo starting ATS" ++ tr.Processes.Default.ReturnCode = 0 ++ tr.StillRunningAfter = ts ++ return ts ++ ++ def run_cases_expecting_gzip(self): ++ # Test 1: Verify basic ESI functionality. ++ tr = Test.AddTestRun("First request for esi.php: not cached") ++ tr.Processes.Default.Command = \ ++ ('curl http://127.0.0.1:{0}/esi.php -H"Host: www.example.com" ' ++ '-H"Accept: */*" --verbose'.format( ++ self._ts.Variables.port)) ++ tr.Processes.Default.ReturnCode = 0 ++ tr.Processes.Default.Streams.stderr = "gold/esi_headers.gold" ++ tr.Processes.Default.Streams.stdout = "gold/esi_body.gold" ++ tr.StillRunningAfter = self._server ++ tr.StillRunningAfter = self._ts ++ ++ # Test 2: Repeat the above, should now be cached. ++ tr = Test.AddTestRun("Second request for esi.php: will be cached") ++ tr.Processes.Default.Command = \ ++ ('curl http://127.0.0.1:{0}/esi.php -H"Host: www.example.com" ' ++ '-H"Accept: */*" --verbose'.format( ++ self._ts.Variables.port)) ++ tr.Processes.Default.ReturnCode = 0 ++ tr.Processes.Default.Streams.stderr = "gold/esi_headers.gold" ++ tr.Processes.Default.Streams.stdout = "gold/esi_body.gold" ++ tr.StillRunningAfter = self._server ++ tr.StillRunningAfter = self._ts ++ ++ # Test 3: Verify the ESI plugin can gzip a response when the client accepts it. ++ tr = Test.AddTestRun("Verify the ESI plugin can gzip a response") ++ EsiTest._output_counter += 1 ++ unzipped_body_file = os.path.join( ++ tr.RunDirectory, ++ "non_empty_curl_output_{}".format(EsiTest._output_counter)) ++ gzipped_body_file = unzipped_body_file + ".gz" ++ tr.Processes.Default.Command = \ ++ ('curl http://127.0.0.1:{0}/esi.php -H"Host: www.example.com" ' ++ '-H "Accept-Encoding: gzip" -H"Accept: */*" --verbose --output {1}'.format( ++ self._ts.Variables.port, gzipped_body_file)) ++ tr.Processes.Default.ReturnCode = 0 ++ tr.Processes.Default.Ready = When.FileExists(gzipped_body_file) ++ tr.Processes.Default.Streams.stderr = "gold/esi_gzipped.gold" ++ tr.StillRunningAfter = self._server ++ tr.StillRunningAfter = self._ts ++ zipped_body_disk_file = tr.Disk.File(gzipped_body_file) ++ zipped_body_disk_file.Exists = True ++ ++ # Now, unzip the file and make sure its size is the expected body. ++ tr = Test.AddTestRun("Verify the file uzips to the expected body.") ++ tr.Processes.Default.Command = "gunzip {}".format(gzipped_body_file) ++ tr.Processes.Default.Ready = When.FileExists(unzipped_body_file) ++ tr.Processes.Default.ReturnCode = 0 ++ unzipped_body_disk_file = tr.Disk.File(unzipped_body_file) ++ unzipped_body_disk_file.Content = "gold/esi_body.gold" ++ ++ # Test 4: Verify correct handling of a gzipped empty response body. ++ tr = Test.AddTestRun("Verify we can handle an empty response.") ++ EsiTest._output_counter += 1 ++ empty_body_file = os.path.join( ++ tr.RunDirectory, ++ "empty_curl_output_{}".format(EsiTest._output_counter)) ++ gzipped_empty_body = empty_body_file + ".gz" ++ tr.Processes.Default.Command = \ ++ ('curl http://127.0.0.1:{0}/expect_empty_body -H"Host: www.example.com" ' ++ '-H"Accept-Encoding: gzip" -H"Accept: */*" --verbose --output {1}'.format( ++ self._ts.Variables.port, gzipped_empty_body)) ++ tr.Processes.Default.ReturnCode = 0 ++ tr.Processes.Default.Ready = When.FileExists(gzipped_empty_body) ++ tr.Processes.Default.Streams.stderr = "gold/empty_response_body.gold" ++ tr.StillRunningAfter = self._server ++ tr.StillRunningAfter = self._ts ++ # The gzipped output file should be greater than 0, even though 0 bytes are ++ # compressed. ++ gz_disk_file = tr.Disk.File(gzipped_empty_body) ++ gz_disk_file.Size = Testers.GreaterThan(0) ++ ++ # Now, unzip the file and make sure its size is the original 0 size body. ++ tr = Test.AddTestRun("Verify the file uzips to a zero sized file.") ++ tr.Processes.Default.Command = "gunzip {}".format(gzipped_empty_body) ++ tr.Processes.Default.Ready = When.FileExists(empty_body_file) ++ tr.Processes.Default.ReturnCode = 0 ++ unzipped_disk_file = tr.Disk.File(empty_body_file) ++ unzipped_disk_file.Size = 0 ++ ++ def run_cases_expecting_no_gzip(self): ++ # Test 1: Run an ESI test where the client does not accept gzip. ++ tr = Test.AddTestRun("First request for esi.php: gzip not accepted.") ++ tr.Processes.Default.Command = \ ++ ('curl http://127.0.0.1:{0}/esi.php -H"Host: www.example.com" ' ++ '-H"Accept: */*" --verbose'.format( ++ self._ts.Variables.port)) ++ tr.Processes.Default.ReturnCode = 0 ++ tr.Processes.Default.Streams.stderr = "gold/esi_headers.gold" ++ tr.Processes.Default.Streams.stdout = "gold/esi_body.gold" ++ tr.StillRunningAfter = self._server ++ tr.StillRunningAfter = self._ts ++ ++ # Test 2: Verify the ESI plugin does not gzip the response even if the ++ # client accepts the gzip encoding. ++ tr = Test.AddTestRun("Verify the ESI plugin refuses to gzip responses with -–disable-gzip-output") ++ tr.Processes.Default.Command = \ ++ ('curl http://127.0.0.1:{0}/esi.php -H"Host: www.example.com" ' ++ '-H "Accept-Encoding: gzip" -H"Accept: */*" --verbose'.format( ++ self._ts.Variables.port)) ++ tr.Processes.Default.ReturnCode = 0 ++ tr.Processes.Default.Streams.stderr = "gold/esi_headers.gold" ++ tr.Processes.Default.Streams.stdout = "gold/esi_body.gold" ++ tr.StillRunningAfter = self._server ++ tr.StillRunningAfter = self._ts ++ ++ ++# ++# Configure and run the test cases. ++# ++ ++# Run the tests with ESI configured with no parameters. ++vanilla_test = EsiTest(plugin_config='esi.so') ++vanilla_test.run_cases_expecting_gzip() ++ ++# For these test cases, the behavior should remain the same with ++# --first-byte-flush set. ++first_byte_flush_test = EsiTest(plugin_config='esi.so --first-byte-flush') ++first_byte_flush_test.run_cases_expecting_gzip() ++ ++# For these test cases, the behavior should remain the same with ++# --packed-node-support set. ++packed_node_support_test = EsiTest(plugin_config='esi.so --packed-node-support') ++packed_node_support_test.run_cases_expecting_gzip() ++ ++# Run a set of cases verifying that the plugin does not zip content if ++# --disable-gzip-output is set. ++gzip_disabled_test = EsiTest(plugin_config='esi.so --disable-gzip-output') ++gzip_disabled_test.run_cases_expecting_no_gzip() +--- /dev/null ++++ b/tests/gold_tests/pluginTest/esi/gold/empty_response_body.gold +@@ -0,0 +1,14 @@ ++`` ++> GET /expect_empty_body HTTP/1.1 ++> Host: www.example.com ++> User-Agent: `` ++> Accept-Encoding: gzip ++> Accept: */* ++`` ++< HTTP/1.1 200 OK ++< Content-Type: text/html; charset=UTF-8 ++`` ++< Content-Length: 20 ++`` ++< Content-Encoding: gzip ++`` +--- /dev/null ++++ b/tests/gold_tests/pluginTest/esi/gold/esi_body.gold +@@ -0,0 +1,12 @@ ++`` ++ ++ ++ ++Hello, ++ ++ ++ ++`` +--- /dev/null ++++ b/tests/gold_tests/pluginTest/esi/gold/esi_gzipped.gold +@@ -0,0 +1,10 @@ ++`` ++> GET /esi.php HTTP/1.1 ++`` ++> Accept-Encoding: gzip ++> Accept: */* ++`` ++< HTTP/1.1 200 OK ++`` ++< Content-Encoding: gzip ++`` +--- /dev/null ++++ b/tests/gold_tests/pluginTest/esi/gold/esi_headers.gold +@@ -0,0 +1,6 @@ ++`` ++> GET /esi.php HTTP/1.1 ++`` ++< HTTP/1.1 200 OK ++< Content-Type: text/html ++`` diff --git a/patches/0018-CVE-2020-17509.patch b/patches/0018-CVE-2020-17509.patch new file mode 100644 index 00000000..4556d60f --- /dev/null +++ b/patches/0018-CVE-2020-17509.patch @@ -0,0 +1,218 @@ +Author: Brian Neradt +Origin: backport +Applied-upstream: https://github.com/apache/trafficserver/pull/7359 +Last-Update: 2020-06-25 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/doc/admin-guide/files/records.config.en.rst ++++ b/doc/admin-guide/files/records.config.en.rst +@@ -1482,11 +1482,9 @@ Negative Response Caching + ====================== ===================================================== + ``204`` No Content + ``305`` Use Proxy +- ``400`` Bad Request + ``403`` Forbidden + ``404`` Not Found + ``414`` URI Too Long +- ``405`` Method Not Allowed + ``500`` Internal Server Error + ``501`` Not Implemented + ``502`` Bad Gateway +@@ -1504,7 +1502,7 @@ Negative Response Caching + How long (in seconds) Traffic Server keeps the negative responses valid in cache. This value only affects negative + responses that do NOT have explicit ``Expires:`` or ``Cache-Control:`` lifetimes set by the server. + +-.. ts:cv:: CONFIG proxy.config.http.negative_caching_list STRING 204 305 403 404 405 414 500 501 502 503 504 ++.. ts:cv:: CONFIG proxy.config.http.negative_caching_list STRING 204 305 403 404 414 500 501 502 503 504 + :reloadable: + + The HTTP status code for negative caching. Default values are mentioned above. The unwanted status codes can be +--- a/doc/admin-guide/performance/index.en.rst ++++ b/doc/admin-guide/performance/index.en.rst +@@ -493,7 +493,7 @@ Error responses from origins are coniste + If error responses are costly for your origin server to generate, you may elect + to have |TS| cache these responses for a period of time. The default behavior is + to consider all of these responses to be uncacheable, which will lead to every +-client request to result in an origin request. ++client request resulting in an origin request. + + This behavior is controlled by both enabling the feature via + :ts:cv:`proxy.config.http.negative_caching_enabled` and setting the cache time +@@ -502,7 +502,7 @@ status code for negative caching can be + + CONFIG proxy.config.http.negative_caching_enabled INT 1 + CONFIG proxy.config.http.negative_caching_lifetime INT 10 +- CONFIG proxy.config.http.negative_caching_list STRING 204 305 403 404 405 414 500 501 502 503 504 ++ CONFIG proxy.config.http.negative_caching_list STRING 204 305 403 404 414 500 501 502 503 504 + + SSL-Specific Options + ~~~~~~~~~~~~~~~~~~~~ +--- a/mgmt/RecordsConfig.cc ++++ b/mgmt/RecordsConfig.cc +@@ -499,7 +499,7 @@ static const RecordElement RecordsConfig + , + {RECT_CONFIG, "proxy.config.http.negative_caching_lifetime", RECD_INT, "1800", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL} + , +- {RECT_CONFIG, "proxy.config.http.negative_caching_list", RECD_STRING, "204 305 403 404 405 414 500 501 502 503 504", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL} ++ {RECT_CONFIG, "proxy.config.http.negative_caching_list", RECD_STRING, "204 305 403 404 414 500 501 502 503 504", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL} + , + + // ######################### +--- a/proxy/http/HttpConfig.cc ++++ b/proxy/http/HttpConfig.cc +@@ -910,7 +910,7 @@ set_negative_caching_list(const char *na + HttpStatusBitset set; + // values from proxy.config.http.negative_caching_list + if (0 == strcasecmp("proxy.config.http.negative_caching_list", name) && RECD_STRING == dtype && data.rec_string) { +- // parse the list of status code ++ // parse the list of status codes + ts::TextView status_list(data.rec_string, strlen(data.rec_string)); + auto is_sep{[](char c) { return isspace(c) || ',' == c || ';' == c; }}; + while (!status_list.ltrim_if(is_sep).empty()) { +--- a/proxy/http/HttpSM.cc ++++ b/proxy/http/HttpSM.cc +@@ -2922,7 +2922,7 @@ HttpSM::tunnel_handler_server(int event, + // the reason string being written to the client and a bad CL when reading from cache. + // I didn't find anywhere this appended reason is being used, so commenting it out. + /* +- if (t_state.negative_caching && p->bytes_read == 0) { ++ if (t_state.is_cacheable_and_negative_caching_is_enabled && p->bytes_read == 0) { + int reason_len; + const char *reason = t_state.hdr_info.server_response.reason_get(&reason_len); + if (reason == NULL) +@@ -2977,8 +2977,8 @@ HttpSM::tunnel_handler_server(int event, + } + + // turn off negative caching in case there are multiple server contacts +- if (t_state.negative_caching) { +- t_state.negative_caching = false; ++ if (t_state.is_cacheable_and_negative_caching_is_enabled) { ++ t_state.is_cacheable_and_negative_caching_is_enabled = false; + } + + // If we had a ground fill, check update our status +@@ -6531,7 +6531,8 @@ HttpSM::setup_server_transfer() + + nbytes = server_transfer_init(buf, hdr_size); + +- if (t_state.negative_caching && t_state.hdr_info.server_response.status_get() == HTTP_STATUS_NO_CONTENT) { ++ if (t_state.is_cacheable_and_negative_caching_is_enabled && ++ t_state.hdr_info.server_response.status_get() == HTTP_STATUS_NO_CONTENT) { + int s = sizeof("No Content") - 1; + buf->write("No Content", s); + nbytes += s; +--- a/proxy/http/HttpTransact.cc ++++ b/proxy/http/HttpTransact.cc +@@ -4150,7 +4150,7 @@ HttpTransact::handle_cache_operation_on_ + client_response_code = server_response_code; + base_response = &s->hdr_info.server_response; + +- s->negative_caching = is_negative_caching_appropriate(s) && cacheable; ++ s->is_cacheable_and_negative_caching_is_enabled = cacheable && s->txn_conf->negative_caching_enabled; + + // determine the correct cache action given the original cache action, + // cacheability of server response, and request method +@@ -4185,7 +4185,7 @@ HttpTransact::handle_cache_operation_on_ + } + + } else if (s->cache_info.action == CACHE_DO_WRITE) { +- if (!cacheable && !s->negative_caching) { ++ if (!cacheable) { + s->cache_info.action = CACHE_DO_NO_ACTION; + } else if (s->method == HTTP_WKSIDX_HEAD) { + s->cache_info.action = CACHE_DO_NO_ACTION; +@@ -4212,7 +4212,7 @@ HttpTransact::handle_cache_operation_on_ + // before issuing a 304 + if (s->cache_info.action == CACHE_DO_WRITE || s->cache_info.action == CACHE_DO_NO_ACTION || + s->cache_info.action == CACHE_DO_REPLACE) { +- if (s->negative_caching) { ++ if (s->is_cacheable_and_negative_caching_is_enabled) { + HTTPHdr *resp; + s->cache_info.object_store.create(); + s->cache_info.object_store.request_set(&s->hdr_info.client_request); +@@ -4248,8 +4248,8 @@ HttpTransact::handle_cache_operation_on_ + SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVER_REVALIDATED); + } + } +- } else if (s->negative_caching) { +- s->negative_caching = false; ++ } else if (s->is_cacheable_and_negative_caching_is_enabled) { ++ s->is_cacheable_and_negative_caching_is_enabled = false; + } + + break; +@@ -4659,7 +4659,7 @@ HttpTransact::set_headers_for_cache_writ + sites yields no insight. So the assert is removed and we keep the behavior that if the response + in @a cache_info is already set, we don't override it. + */ +- if (!s->negative_caching || !cache_info->response_get()->valid()) { ++ if (!s->is_cacheable_and_negative_caching_is_enabled || !cache_info->response_get()->valid()) { + cache_info->response_set(response); + } + +@@ -6111,24 +6111,24 @@ HttpTransact::is_response_cacheable(Stat + } + } + +- // default cacheability +- if (!s->txn_conf->negative_caching_enabled) { +- if ((response_code == HTTP_STATUS_OK) || (response_code == HTTP_STATUS_NOT_MODIFIED) || +- (response_code == HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION) || (response_code == HTTP_STATUS_MOVED_PERMANENTLY) || +- (response_code == HTTP_STATUS_MULTIPLE_CHOICES) || (response_code == HTTP_STATUS_GONE)) { +- TxnDebug("http_trans", "[is_response_cacheable] YES by default "); +- return true; +- } else { +- TxnDebug("http_trans", "[is_response_cacheable] NO by default"); +- return false; +- } ++ if ((response_code == HTTP_STATUS_OK) || (response_code == HTTP_STATUS_NOT_MODIFIED) || ++ (response_code == HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION) || (response_code == HTTP_STATUS_MOVED_PERMANENTLY) || ++ (response_code == HTTP_STATUS_MULTIPLE_CHOICES) || (response_code == HTTP_STATUS_GONE)) { ++ TxnDebug("http_trans", "[is_response_cacheable] YES response code seems fine"); ++ return true; + } ++ // Notice that the following are not overridable by negative caching. + if (response_code == HTTP_STATUS_SEE_OTHER || response_code == HTTP_STATUS_UNAUTHORIZED || + response_code == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) { + return false; + } +- // let is_negative_caching_approriate decide what to do +- return true; ++ // The response code does not look appropriate for caching. Check, however, ++ // whether the user has specified it should be cached via negative response ++ // caching configuration. ++ if (is_negative_caching_appropriate(s)) { ++ return true; ++ } ++ return false; + /* Since we weren't caching response obtained with + Authorization (the cache control stuff was commented out previously) + I've moved this check to is_request_cache_lookupable(). +--- a/proxy/http/HttpTransact.h ++++ b/proxy/http/HttpTransact.h +@@ -761,8 +761,23 @@ public: + bool client_connection_enabled = true; + bool acl_filtering_performed = false; + +- // for negative caching +- bool negative_caching = false; ++ /// True if negative caching is enabled and the response is cacheable. ++ /// ++ /// Note carefully that this being true does not necessarily imply that the ++ /// response code was negative. It means that (a) the response was ++ /// cacheable apart from response code considerations, and (b) concerning ++ /// the response code one of the following was true: ++ /// ++ /// * The response was a negative response code configured cacheable ++ /// by the user via negative response caching configuration, or ... ++ /// ++ /// * The response code was an otherwise cacheable positive repsonse ++ /// value (such as a 200 response, for example). ++ /// ++ /// TODO: We should consider refactoring this variable and its use. For now ++ /// I'm giving it an awkwardly long name to make sure the meaning of it is ++ /// clear in its various contexts. ++ bool is_cacheable_and_negative_caching_is_enabled = false; + // for authenticated content caching + CacheAuth_t www_auth_content = CACHE_AUTH_NONE; + diff --git a/patches/0019-CVE-2021-35474_32567_32566_32565_27577.patch b/patches/0019-CVE-2021-35474_32567_32566_32565_27577.patch new file mode 100644 index 00000000..0a0a28f8 --- /dev/null +++ b/patches/0019-CVE-2021-35474_32567_32566_32565_27577.patch @@ -0,0 +1,120 @@ +From b82a3d192f995fb9d78e1c44d51d9acca4783277 Mon Sep 17 00:00:00 2001 +From: Evan Zelkowitz +Date: Tue, 22 Jun 2021 14:32:55 -0700 +Subject: [PATCH] Fixes (#7971) + +* String the url fragment for outgoing requests (#7966) + +Co-authored-by: Susan Hinrichs +(cherry picked from commit 2b13eb33794574e62249997b4ba654d943a10f2d) + +* Ensure that the content-length value is only digits (#7964) + +Co-authored-by: Susan Hinrichs +(cherry picked from commit 668d0f8668fec1cd350b0ceba3f7f8e4020ae3ca) + +* Schedule H2 reenable event only if it's necessary + +Co-authored-by: Katsutoshi Ikenoya + +* Fix dynamic-stack-buffer-overflow of cachekey plugin (#7945) + +* Fix dynamic-stack-buffer-overflow of cachekey plugin + +* Check dst_size include null termination + +(cherry picked from commit 5a9339d7bc65e1c2d8d2a0fc80bb051daf3cdb0b) + +Co-authored-by: Bryan Call +Co-authored-by: Masakazu Kitajo +Co-authored-by: Katsutoshi Ikenoya +Co-authored-by: Masaori Koshiba +--- + plugins/cachekey/cachekey.cc | 2 +- + proxy/hdrs/HTTP.cc | 11 +++++++++++ + proxy/http/HttpTransact.cc | 5 ++++- + proxy/http2/Http2ClientSession.cc | 14 +++++++------- + proxy/logging/LogUtils.cc | 2 +- + 5 files changed, 24 insertions(+), 10 deletions(-) + +diff --git a/plugins/cachekey/cachekey.cc b/plugins/cachekey/cachekey.cc +index 5f128894bfa..44925b3db28 100644 +--- a/plugins/cachekey/cachekey.cc ++++ b/plugins/cachekey/cachekey.cc +@@ -41,7 +41,7 @@ appendEncoded(String &target, const char *s, size_t len) + return; + } + +- char tmp[len * 2]; ++ char tmp[len * 3 + 1]; + size_t written; + + /* The default table does not encode the comma, so we need to use our own table here. */ +diff --git a/proxy/hdrs/HTTP.cc b/proxy/hdrs/HTTP.cc +index 6a2ecc41d3a..48032dd9ddf 100644 +--- a/proxy/hdrs/HTTP.cc ++++ b/proxy/hdrs/HTTP.cc +@@ -1202,6 +1202,17 @@ validate_hdr_content_length(HdrHeap *heap, HTTPHdrImpl *hh) + int content_length_len = 0; + const char *content_length_val = content_length_field->value_get(&content_length_len); + ++ // RFC 7230 section 3.3.2 ++ // Content-Length = 1*DIGIT ++ // ++ // If the content-length value contains a non-numeric value, the header is invalid ++ for (int i = 0; i < content_length_len; i++) { ++ if (!isdigit(content_length_val[i])) { ++ Debug("http", "Content-Length value contains non-digit, returning parse error"); ++ return PARSE_RESULT_ERROR; ++ } ++ } ++ + while (content_length_field->has_dups()) { + int content_length_len_2 = 0; + const char *content_length_val_2 = content_length_field->m_next_dup->value_get(&content_length_len_2); +diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc +index c3d135b98e8..2de29a8d08d 100644 +--- a/proxy/http/HttpTransact.cc ++++ b/proxy/http/HttpTransact.cc +@@ -7619,9 +7619,12 @@ HttpTransact::build_request(State *s, HTTPHdr *base_request, HTTPHdr *outgoing_r + + // HttpTransactHeaders::convert_request(outgoing_version, outgoing_request); // commented out this idea + ++ URL *url = outgoing_request->url_get(); ++ // Remove fragment from upstream URL ++ url->fragment_set(NULL, 0); ++ + // Check whether a Host header field is missing from a 1.0 or 1.1 request. + if (outgoing_version != HTTPVersion(0, 9) && !outgoing_request->presence(MIME_PRESENCE_HOST)) { +- URL *url = outgoing_request->url_get(); + int host_len; + const char *host = url->host_get(&host_len); + +diff --git a/proxy/http2/Http2ClientSession.cc b/proxy/http2/Http2ClientSession.cc +index 6d7d3de7992..ee952b8a275 100644 +--- a/proxy/http2/Http2ClientSession.cc ++++ b/proxy/http2/Http2ClientSession.cc +@@ -653,8 +653,8 @@ Http2ClientSession::remember(const SourceLocation &location, int event, int reen + bool + Http2ClientSession::_should_do_something_else() + { +- // Do something else every 128 incoming frames +- return (this->_n_frame_read & 0x7F) == 0; ++ // Do something else every 128 incoming frames if connection state isn't closed ++ return (this->_n_frame_read & 0x7F) == 0 && !connection_state.is_state_closed(); + } + + int64_t +diff --git a/proxy/logging/LogUtils.cc b/proxy/logging/LogUtils.cc +index 94becf250ac..475bee87cad 100644 +--- a/proxy/logging/LogUtils.cc ++++ b/proxy/logging/LogUtils.cc +@@ -343,7 +343,7 @@ escapify_url_common(Arena *arena, char *url, size_t len_in, int *len_out, char * + // + size_t out_len = len_in + 2 * count; + +- if (dst && out_len > dst_size) { ++ if (dst && (out_len + 1) > dst_size) { + *len_out = 0; + return nullptr; + } diff --git a/patches/0020-CVE-2021-37147.patch b/patches/0020-CVE-2021-37147.patch new file mode 100644 index 00000000..089f6ae2 --- /dev/null +++ b/patches/0020-CVE-2021-37147.patch @@ -0,0 +1,49 @@ +commit 5cad961c87cb07fbb8fa6890685d9878a169378d +Author: Brian Neradt +Date: Wed Oct 27 11:29:43 2021 -0500 + + Fix output '\n' HTTP field line endings (#8460) + + This is another attempt to fix what was initially addressed in #8096 but + got backed out via #8305. That more extensive patch was considered too + invasive and potentially risky. This more targeted patch will fix + clients that only send the \n endings but it will force the \r\n line + ending on output. + + This was mostly in place except for header lines that get + m_n_v_raw_printable set, which seems to be most header lines. The + addition checks to see if the header line ends in \r\n. If it does not + the m_n_v_raw_printable flag gets cleared and the logic that explicitly + adds the line endings while be invoked on output. + +Index: trafficserver/proxy/hdrs/MIME.cc +=================================================================== +--- trafficserver.orig/proxy/hdrs/MIME.cc 2022-05-21 21:12:19.187391663 +0200 ++++ trafficserver/proxy/hdrs/MIME.cc 2022-05-21 21:12:19.187391663 +0200 +@@ -2638,8 +2638,17 @@ + + // find_value_last + field_value_last = line_e - 1; ++ int suffix_count = 0; + while ((field_value_last >= field_value_first) && ParseRules::is_wslfcr(*field_value_last)) { + --field_value_last; ++ ++suffix_count; ++ } ++ ++ // Make sure the field ends in CRLF. If not, we'll fix the field via the n_v_raw_printable ++ // flag. ++ bool raw_print_field = true; ++ if (suffix_count < 2 || *(line_e - 2) != '\r' || *(line_e - 1) != '\n') { ++ raw_print_field = false; + } + + field_name_length = (int)(field_name_last - field_name_first + 1); +@@ -2676,7 +2685,7 @@ + + MIMEField *field = mime_field_create(heap, mh); + mime_field_name_value_set(heap, mh, field, field_name_wks_idx, field_name_first, field_name_length, field_value_first, +- field_value_length, true, total_line_length, false); ++ field_value_length, raw_print_field, total_line_length, false); + mime_hdr_field_attach(mh, field, 1, nullptr); + } + } diff --git a/patches/0020-CVE-2021-37148.patch b/patches/0020-CVE-2021-37148.patch new file mode 100644 index 00000000..b2f2b8d8 --- /dev/null +++ b/patches/0020-CVE-2021-37148.patch @@ -0,0 +1,36 @@ +commit e2c9ac217f24dc3e91ff2c9f52b52093e8fb32d5 +Author: Brian Neradt +Date: Wed Oct 27 11:30:32 2021 -0500 + + 8.1.x: Reject Transfer-Encoding in pre-HTTP/1.1 requests (#8457) + + Per spec, Transfer-Encoding is only supported in HTTP/1.1. For earlier + versions, we must reject Transfer-Encoding rather than interpret it + since downstream proxies may ignore the chunk header and rely upon the + Content-Length, or interpret the body some other way. These differences + in interpretation may open up the door to compatibility issues. To + protect against this, we reply with a 4xx if the client uses + Transfer-Encoding with HTTP versions that do not support it. + +Index: trafficserver/proxy/http/HttpTransact.cc +=================================================================== +--- trafficserver.orig/proxy/http/HttpTransact.cc 2022-05-21 21:12:19.271391152 +0200 ++++ trafficserver/proxy/http/HttpTransact.cc 2022-05-21 21:12:19.271391152 +0200 +@@ -5122,6 +5122,17 @@ + return BAD_CONNECT_PORT; + } + ++ if (s->client_info.transfer_encoding == CHUNKED_ENCODING && incoming_hdr->version_get() < HTTPVersion(1, 1)) { ++ // Per spec, Transfer-Encoding is only supported in HTTP/1.1. For earlier ++ // versions, we must reject Transfer-Encoding rather than interpret it ++ // since downstream proxies may ignore the chunk header and rely upon the ++ // Content-Length, or interpret the body some other way. These ++ // differences in interpretation may open up the door to compatibility ++ // issues. To protect against this, we reply with a 4xx if the client ++ // uses Transfer-Encoding with HTTP versions that do not support it. ++ return UNACCEPTABLE_TE_REQUIRED; ++ } ++ + // Require Content-Length/Transfer-Encoding for POST/PUSH/PUT + if ((scheme == URL_WKSIDX_HTTP || scheme == URL_WKSIDX_HTTPS) && + (method == HTTP_WKSIDX_POST || method == HTTP_WKSIDX_PUSH || method == HTTP_WKSIDX_PUT) && diff --git a/patches/0020-CVE-2021-37149.patch b/patches/0020-CVE-2021-37149.patch new file mode 100644 index 00000000..090ba837 --- /dev/null +++ b/patches/0020-CVE-2021-37149.patch @@ -0,0 +1,234 @@ +commit 2addc8ca71449ceac0d5b80172460ee09c938f5e +Author: Brian Neradt +Date: Wed Oct 27 11:30:07 2021 -0500 + + Detect and handle chunk header size truncation (#8458) + + This detects if a chunk header size is too large and, if so, closes the + connection. + +Index: trafficserver/include/tscore/ink_memory.h +=================================================================== +--- trafficserver.orig/include/tscore/ink_memory.h 2022-05-21 21:12:19.371390543 +0200 ++++ trafficserver/include/tscore/ink_memory.h 2022-05-21 21:12:19.367390568 +0200 +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -204,6 +205,24 @@ + memset(static_cast(&t), 0, sizeof(t)); + } + ++/** Verify that we can safely shift value num_places places left. ++ * ++ * This checks that the shift will not cause the variable to overflow and that ++ * the value will not become negative. ++ * ++ * @param[in] value The value against which to check whether the shift is safe. ++ * ++ * @param[in] num_places The number of places to check that shifting left is safe. ++ * ++ */ ++template ++inline constexpr bool ++can_safely_shift_left(T value, int num_places) ++{ ++ constexpr auto max_value = std::numeric_limits::max(); ++ return value >= 0 && value <= (max_value >> num_places); ++} ++ + /** Scoped resources. + + An instance of this class is used to hold a contingent resource. When this object goes out of scope +Index: trafficserver/proxy/http/HttpTunnel.cc +=================================================================== +--- trafficserver.orig/proxy/http/HttpTunnel.cc 2022-05-21 21:12:19.371390543 +0200 ++++ trafficserver/proxy/http/HttpTunnel.cc 2022-05-21 21:12:19.371390543 +0200 +@@ -36,6 +36,7 @@ + #include "HttpSM.h" + #include "HttpDebugNames.h" + #include "tscore/ParseRules.h" ++#include "tscore/ink_memory.h" + + static const int min_block_transfer_bytes = 256; + static const char *const CHUNK_HEADER_FMT = "%" PRIx64 "\r\n"; +@@ -153,8 +154,16 @@ + if (state == CHUNK_READ_SIZE) { + // The http spec says the chunked size is always in hex + if (ParseRules::is_hex(*tmp)) { ++ // Make sure we will not overflow running_sum with our shift. ++ if (!can_safely_shift_left(running_sum, 4)) { ++ // We have no more space in our variable for the shift. ++ state = CHUNK_READ_ERROR; ++ done = true; ++ break; ++ } + num_digits++; +- running_sum *= 16; ++ // Shift over one hex value. ++ running_sum <<= 4; + + if (ParseRules::is_digit(*tmp)) { + running_sum += *tmp - '0'; +Index: trafficserver/src/tscore/Makefile.am +=================================================================== +--- trafficserver.orig/src/tscore/Makefile.am 2022-05-21 21:12:19.371390543 +0200 ++++ trafficserver/src/tscore/Makefile.am 2022-05-21 21:12:19.371390543 +0200 +@@ -258,6 +258,7 @@ + unit_tests/test_BufferWriter.cc \ + unit_tests/test_BufferWriterFormat.cc \ + unit_tests/test_ink_inet.cc \ ++ unit_tests/test_ink_memory.cc \ + unit_tests/test_IntrusivePtr.cc \ + unit_tests/test_IpMap.cc \ + unit_tests/test_layout.cc \ +Index: trafficserver/src/tscore/unit_tests/test_ink_memory.cc +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ trafficserver/src/tscore/unit_tests/test_ink_memory.cc 2022-05-21 21:12:19.371390543 +0200 +@@ -0,0 +1,141 @@ ++/** @file ++ ++ ink_memory unit tests. ++ ++ @section license License ++ ++ Licensed to the Apache Software Foundation (ASF) under one ++ or more contributor license agreements. See the NOTICE file ++ distributed with this work for additional information ++ regarding copyright ownership. The ASF licenses this file ++ to you under the Apache License, Version 2.0 (the ++ "License"); you may not use this file except in compliance ++ with the License. You may obtain a copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++ Unless required by applicable law or agreed to in writing, software ++ distributed under the License is distributed on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ See the License for the specific language governing permissions and ++ limitations under the License. ++*/ ++ ++#include ++#include ++#include "tscore/ink_memory.h" ++ ++constexpr void ++test_can_safely_shift_int8_t() ++{ ++ constexpr int8_t a = 0; ++ static_assert(can_safely_shift_left(a, 0) == true, "shifting 0 is safe"); ++ static_assert(can_safely_shift_left(a, 4) == true, "shifting 0 is safe"); ++ static_assert(can_safely_shift_left(a, 8) == true, "shifting 0 is safe"); ++ ++ constexpr int8_t b = 1; ++ static_assert(can_safely_shift_left(b, 0) == true, "shifting int8_t 1 0 places is safe"); ++ static_assert(can_safely_shift_left(b, 1) == true, "shifting int8_t 1 1 places is safe"); ++ static_assert(can_safely_shift_left(b, 6) == true, "shifting int8_t 1 6 places is safe"); ++ static_assert(can_safely_shift_left(b, 7) == false, "shifting int8_t 1 7 places becomes negative"); ++ static_assert(can_safely_shift_left(b, 8) == false, "shifting int8_t 1 8 places overflows"); ++ ++ constexpr int8_t c = 0xff; ++ static_assert(can_safely_shift_left(c, 0) == false, "int8_t 0xff is already negative"); ++ static_assert(can_safely_shift_left(c, 1) == false, "shifting int8_t 0xff 1 place overflows"); ++} ++ ++constexpr void ++test_can_safely_shift_uint8_t() ++{ ++ constexpr uint8_t a = 0; ++ static_assert(can_safely_shift_left(a, 0) == true, "shifting 0 is safe"); ++ static_assert(can_safely_shift_left(a, 4) == true, "shifting 0 is safe"); ++ static_assert(can_safely_shift_left(a, 8) == true, "shifting 0 is safe"); ++ ++ constexpr uint8_t b = 1; ++ static_assert(can_safely_shift_left(b, 0) == true, "shifting uint8_t 1 0 places is safe"); ++ static_assert(can_safely_shift_left(b, 1) == true, "shifting uint8_t 1 1 places is safe"); ++ static_assert(can_safely_shift_left(b, 6) == true, "shifting uint8_t 1 6 places is safe"); ++ static_assert(can_safely_shift_left(b, 7) == true, "shifting uint8_t 1 7 is safe"); ++ static_assert(can_safely_shift_left(b, 8) == false, "shifting uint8_t 1 8 places overflows"); ++ ++ constexpr uint8_t c = 0xff; ++ static_assert(can_safely_shift_left(c, 0) == true, "shifting int8_t 0xff 0 places is safe"); ++ static_assert(can_safely_shift_left(c, 1) == false, "shifting int8_t 0xff 1 place overflows"); ++} ++ ++constexpr void ++test_can_safely_shift_int32_t() ++{ ++ constexpr int32_t a = 0; ++ static_assert(can_safely_shift_left(a, 4) == true, "shifting 0 is safe"); ++ ++ constexpr int32_t b = 1; ++ static_assert(can_safely_shift_left(b, 4) == true, "shifting 1 is safe"); ++ ++ constexpr int32_t c = 0x00ff'ffff; ++ static_assert(can_safely_shift_left(c, 4) == true, "shifting 0x00ff'ffff is safe"); ++ ++ constexpr int32_t d = 0x07ff'ffff; ++ static_assert(can_safely_shift_left(d, 4) == true, "shifting 0x07ff'ffff is safe"); ++ ++ constexpr int32_t e = -1; ++ static_assert(can_safely_shift_left(e, 4) == false, "shifting -1 will result in truncation"); ++ ++ constexpr int32_t f = 0x0800'0000; ++ static_assert(can_safely_shift_left(f, 4) == false, "shifting 0x0801'0000 will become negative"); ++ ++ constexpr int32_t g = 0x0fff'ffff; ++ static_assert(can_safely_shift_left(g, 4) == false, "shifting 0x0fff'ffff will become negative"); ++ ++ constexpr int32_t h = 0x1000'0000; ++ static_assert(can_safely_shift_left(h, 4) == false, "shifting 0x1000'0000 will overflow"); ++ ++ constexpr int32_t i = 0xf000'0000; ++ static_assert(can_safely_shift_left(i, 4) == false, "shifting 0xf000'0000 will overflow"); ++ ++ constexpr int32_t j = 0xf800'0000; ++ static_assert(can_safely_shift_left(j, 4) == false, "shifting 0xf800'0000 will become negative"); ++} ++ ++constexpr void ++test_can_safely_shift_uint32_t() ++{ ++ constexpr uint32_t a = 0; ++ static_assert(can_safely_shift_left(a, 4) == true, "shifting 0 is safe"); ++ ++ constexpr uint32_t b = 1; ++ static_assert(can_safely_shift_left(b, 4) == true, "shifting 1 is safe"); ++ ++ constexpr uint32_t c = 0x00ff'ffff; ++ static_assert(can_safely_shift_left(c, 4) == true, "shifting 0x00ff'ffff is safe"); ++ ++ constexpr uint32_t d = 0x07ff'ffff; ++ static_assert(can_safely_shift_left(d, 4) == true, "shifting 0x07ff'ffff is safe"); ++ ++ constexpr uint32_t e = 0x0800'0000; ++ static_assert(can_safely_shift_left(e, 4) == true, "shifting unisgned 0x0800'0000 is safe"); ++ ++ constexpr uint32_t f = 0x0fff'ffff; ++ static_assert(can_safely_shift_left(f, 4) == true, "shifting unsigned 0x0fff'ffff is safe"); ++ ++ constexpr uint32_t g = 0x1000'0000; ++ static_assert(can_safely_shift_left(g, 4) == false, "shifting 0x1000'0000 will overflow"); ++ ++ constexpr uint32_t h = 0xf000'0000; ++ static_assert(can_safely_shift_left(h, 4) == false, "shifting 0xf000'0000 will overflow"); ++ ++ constexpr uint32_t i = 0xf800'0000; ++ static_assert(can_safely_shift_left(i, 4) == false, "shifting 0xf800'0000 will become negative"); ++} ++ ++TEST_CASE("can_safely_shift", "[libts][ink_inet][memory]") ++{ ++ // can_safely_shift_left is a constexpr function, therefore all these checks are ++ // done at compile time and REQUIRES calls are not necessary. ++ test_can_safely_shift_int8_t(); ++ test_can_safely_shift_uint8_t(); ++ test_can_safely_shift_int32_t(); ++ test_can_safely_shift_uint32_t(); ++} diff --git a/patches/0020-CVE-2021-38161.patch b/patches/0020-CVE-2021-38161.patch new file mode 100644 index 00000000..baae6196 --- /dev/null +++ b/patches/0020-CVE-2021-38161.patch @@ -0,0 +1,50 @@ +commit feefc5e4abc5011dfad5dcfef3f22998faf6e2d4 +Author: Alan M. Carroll +Date: Wed Oct 27 13:41:47 2021 -0500 + + Add some checking to validate the scheme matches the wire protocol. (#8464) + +Index: trafficserver/proxy/http/HttpSM.cc +=================================================================== +--- trafficserver.orig/proxy/http/HttpSM.cc 2022-05-21 21:46:27.407477781 +0200 ++++ trafficserver/proxy/http/HttpSM.cc 2022-05-21 21:48:18.978834781 +0200 +@@ -443,6 +443,9 @@ + // Collect log & stats information + client_tcp_reused = !(ua_txn->is_first_transaction()); + SSLNetVConnection *ssl_vc = dynamic_cast(netvc); ++ ++ is_internal = netvc->get_is_internal_request(); ++ + if (ssl_vc != nullptr) { + client_connection_is_ssl = true; + client_ssl_reused = ssl_vc->getSSLSessionCacheHit(); +@@ -724,6 +727,17 @@ + case PARSE_RESULT_DONE: + SMDebug("http", "[%" PRId64 "] done parsing client request header", sm_id); + ++ if (!is_internal) { ++ auto scheme = t_state.hdr_info.client_request.url_get()->scheme_get_wksidx(); ++ if ((client_connection_is_ssl && (scheme == URL_WKSIDX_HTTP || scheme == URL_WKSIDX_WS)) || ++ (!client_connection_is_ssl && (scheme == URL_WKSIDX_HTTPS || scheme == URL_WKSIDX_WSS))) { ++ SMDebug("http", "scheme [%s] vs. protocol [%s] mismatch", hdrtoken_index_to_wks(scheme), ++ client_connection_is_ssl ? "tls" : "plaintext"); ++ t_state.http_return_code = HTTP_STATUS_BAD_REQUEST; ++ call_transact_and_set_next_state(HttpTransact::BadRequest); ++ break; ++ } ++ } + ua_txn->set_session_active(); + + if (t_state.hdr_info.client_request.version_get() == HTTPVersion(1, 1) && +Index: trafficserver/proxy/http/HttpSM.h +=================================================================== +--- trafficserver.orig/proxy/http/HttpSM.h 2022-05-21 21:47:00.135289347 +0200 ++++ trafficserver/proxy/http/HttpSM.h 2022-05-21 21:47:14.131208716 +0200 +@@ -541,6 +541,7 @@ + int64_t pushed_response_body_bytes = 0; + bool client_tcp_reused = false; + // Info about client's SSL connection. ++ bool is_internal = false; + bool client_ssl_reused = false; + bool client_connection_is_ssl = false; + const char *client_protocol = "-"; diff --git a/patches/0021-CVE_2021_44040.patch b/patches/0021-CVE_2021_44040.patch new file mode 100644 index 00000000..57a33473 --- /dev/null +++ b/patches/0021-CVE_2021_44040.patch @@ -0,0 +1,513 @@ +Description: Improper Input Validation vulnerability in request line parsing +Author: +Origin: upstream +Applied-Upstream: 85c319a7f7c0537bee408ea25df6f1a5ed0a4071, c4e6661a5a205b1f60279f0e66aa496023185967, 8c6f2ed84ba0d8e6255baceb99ee891ebe1ce473 +Reviewed-by: Jean Baptiste Favre +Last-Update: 2022-05-21 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +Index: trafficserver/doc/admin-guide/files/records.config.en.rst +=================================================================== +--- trafficserver.orig/doc/admin-guide/files/records.config.en.rst 2022-05-21 21:12:19.571389326 +0200 ++++ trafficserver/doc/admin-guide/files/records.config.en.rst 2022-05-21 21:12:19.567389350 +0200 +@@ -1112,10 +1112,12 @@ + An arbitrary string value that, if set, will be used to replace any request + ``User-Agent`` header. + +-.. ts:cv:: CONFIG proxy.config.http.strict_uri_parsing INT 0 ++.. ts:cv:: CONFIG proxy.config.http.strict_uri_parsing INT 2 + +- Enables (``1``) or disables (``0``) Traffic Server to return a 400 Bad Request +- if client's request URI includes character which is not RFC 3986 compliant ++ Takes a value between 0 and 2. ``0`` disables strict_uri_parsing. Any character can appears ++ in the URI. ``1`` causes |TS| to return 400 Bad Request ++ if client's request URI includes character which is not RFC 3986 compliant. ``2`` directs |TS| ++ to reject the clients request if it contains whitespace or non-printable characters. + + .. ts:cv:: CONFIG proxy.config.http.errors.log_error_pages INT 1 + :reloadable: +Index: trafficserver/mgmt/RecordsConfig.cc +=================================================================== +--- trafficserver.orig/mgmt/RecordsConfig.cc 2022-05-21 21:12:19.571389326 +0200 ++++ trafficserver/mgmt/RecordsConfig.cc 2022-05-21 21:12:19.567389350 +0200 +@@ -354,7 +354,7 @@ + , + {RECT_CONFIG, "proxy.config.http.post.check.content_length.enabled", RECD_INT, "1", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL} + , +- {RECT_CONFIG, "proxy.config.http.strict_uri_parsing", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL} ++ {RECT_CONFIG, "proxy.config.http.strict_uri_parsing", RECD_INT, "2", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-2]", RECA_NULL} + , + // # Send http11 requests + // # +Index: trafficserver/proxy/hdrs/HTTP.cc +=================================================================== +--- trafficserver.orig/proxy/hdrs/HTTP.cc 2022-05-21 21:12:19.571389326 +0200 ++++ trafficserver/proxy/hdrs/HTTP.cc 2022-05-21 21:12:19.567389350 +0200 +@@ -885,7 +885,7 @@ + + ParseResult + http_parser_parse_req(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const char **start, const char *end, +- bool must_copy_strings, bool eof, bool strict_uri_parsing) ++ bool must_copy_strings, bool eof, int strict_uri_parsing) + { + if (parser->m_parsing_http) { + MIMEScanner *scanner = &parser->m_mime_parser.m_scanner; +Index: trafficserver/proxy/hdrs/HTTP.h +=================================================================== +--- trafficserver.orig/proxy/hdrs/HTTP.h 2022-05-21 21:12:19.571389326 +0200 ++++ trafficserver/proxy/hdrs/HTTP.h 2022-05-21 21:12:19.567389350 +0200 +@@ -445,7 +445,7 @@ + void http_parser_init(HTTPParser *parser); + void http_parser_clear(HTTPParser *parser); + ParseResult http_parser_parse_req(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const char **start, const char *end, +- bool must_copy_strings, bool eof, bool strict_uri_parsing); ++ bool must_copy_strings, bool eof, int strict_uri_parsing); + ParseResult validate_hdr_host(HTTPHdrImpl *hh); + ParseResult validate_hdr_content_length(HdrHeap *heap, HTTPHdrImpl *hh); + ParseResult http_parser_parse_resp(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const char **start, const char *end, +@@ -623,10 +623,10 @@ + const char *reason_get(int *length); + void reason_set(const char *value, int length); + +- ParseResult parse_req(HTTPParser *parser, const char **start, const char *end, bool eof, bool strict_uri_parsing = false); ++ ParseResult parse_req(HTTPParser *parser, const char **start, const char *end, bool eof, int strict_uri_parsing = 0); + ParseResult parse_resp(HTTPParser *parser, const char **start, const char *end, bool eof); + +- ParseResult parse_req(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof, bool strict_uri_parsing = false); ++ ParseResult parse_req(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof, int strict_uri_parsing = 0); + ParseResult parse_resp(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof); + + public: +@@ -1221,7 +1221,7 @@ + -------------------------------------------------------------------------*/ + + inline ParseResult +-HTTPHdr::parse_req(HTTPParser *parser, const char **start, const char *end, bool eof, bool strict_uri_parsing) ++HTTPHdr::parse_req(HTTPParser *parser, const char **start, const char *end, bool eof, int strict_uri_parsing) + { + ink_assert(valid()); + ink_assert(m_http->m_polarity == HTTP_TYPE_REQUEST); +Index: trafficserver/proxy/hdrs/HdrTSOnly.cc +=================================================================== +--- trafficserver.orig/proxy/hdrs/HdrTSOnly.cc 2022-05-21 21:12:19.571389326 +0200 ++++ trafficserver/proxy/hdrs/HdrTSOnly.cc 2022-05-21 21:12:19.567389350 +0200 +@@ -45,7 +45,7 @@ + -------------------------------------------------------------------------*/ + + ParseResult +-HTTPHdr::parse_req(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof, bool strict_uri_parsing) ++HTTPHdr::parse_req(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof, int strict_uri_parsing) + { + const char *start; + const char *tmp; +Index: trafficserver/proxy/hdrs/URL.cc +=================================================================== +--- trafficserver.orig/proxy/hdrs/URL.cc 2022-05-21 21:12:19.571389326 +0200 ++++ trafficserver/proxy/hdrs/URL.cc 2022-05-21 21:12:19.567389350 +0200 +@@ -1179,10 +1179,34 @@ + return true; + } + ++/** ++ * This method will return TRUE if the uri is mostly compliant with ++ * RFC 3986 and it will return FALSE if not. Specifically denying white ++ * space an unprintable characters ++ */ ++static bool ++url_is_mostly_compliant(const char *start, const char *end) ++{ ++ for (const char *i = start; i < end; ++i) { ++ if (isspace(*i)) { ++ Debug("http", "Whitespace character [0x%.2X] found in URL", (unsigned char)*i); ++ return false; ++ } ++ if (!isprint(*i)) { ++ Debug("http", "Non-printable character [0x%.2X] found in URL", (unsigned char)*i); ++ return false; ++ } ++ } ++ return true; ++} ++ + ParseResult +-url_parse(HdrHeap *heap, URLImpl *url, const char **start, const char *end, bool copy_strings_p, bool strict_uri_parsing) ++url_parse(HdrHeap *heap, URLImpl *url, const char **start, const char *end, bool copy_strings_p, int strict_uri_parsing) + { +- if (strict_uri_parsing && !url_is_strictly_compliant(*start, end)) { ++ if (strict_uri_parsing == 1 && !url_is_strictly_compliant(*start, end)) { ++ return PARSE_RESULT_ERROR; ++ } ++ if (strict_uri_parsing == 2 && !url_is_mostly_compliant(*start, end)) { + return PARSE_RESULT_ERROR; + } + +Index: trafficserver/proxy/hdrs/URL.h +=================================================================== +--- trafficserver.orig/proxy/hdrs/URL.h 2022-05-21 21:12:19.571389326 +0200 ++++ trafficserver/proxy/hdrs/URL.h 2022-05-21 21:12:19.567389350 +0200 +@@ -198,14 +198,13 @@ + void url_fragment_set(HdrHeap *heap, URLImpl *url, const char *value, int length, bool copy_string); + + ParseResult url_parse(HdrHeap *heap, URLImpl *url, const char **start, const char *end, bool copy_strings, +- bool strict_uri_parsing = false); ++ int strict_uri_parsing = false); + ParseResult url_parse_no_path_component_breakdown(HdrHeap *heap, URLImpl *url, const char **start, const char *end, + bool copy_strings); + ParseResult url_parse_internet(HdrHeap *heap, URLImpl *url, const char **start, const char *end, bool copy_strings); + ParseResult url_parse_http(HdrHeap *heap, URLImpl *url, const char **start, const char *end, bool copy_strings); + ParseResult url_parse_http_no_path_component_breakdown(HdrHeap *heap, URLImpl *url, const char **start, const char *end, + bool copy_strings); +- + char *url_unescapify(Arena *arena, const char *str, int length); + + void unescape_str(char *&buf, char *buf_e, const char *&str, const char *str_e, int &state); +Index: trafficserver/proxy/http/HttpConfig.cc +=================================================================== +--- trafficserver.orig/proxy/http/HttpConfig.cc 2022-05-21 21:12:19.571389326 +0200 ++++ trafficserver/proxy/http/HttpConfig.cc 2022-05-21 21:12:19.567389350 +0200 +@@ -1475,7 +1475,7 @@ + params->referer_filter_enabled = INT_TO_BOOL(m_master.referer_filter_enabled); + params->referer_format_redirect = INT_TO_BOOL(m_master.referer_format_redirect); + +- params->strict_uri_parsing = INT_TO_BOOL(m_master.strict_uri_parsing); ++ params->strict_uri_parsing = m_master.strict_uri_parsing; + + params->oride.down_server_timeout = m_master.oride.down_server_timeout; + params->oride.client_abort_threshold = m_master.oride.client_abort_threshold; +Index: trafficserver/proxy/http/HttpConfig.h +=================================================================== +--- trafficserver.orig/proxy/http/HttpConfig.h 2022-05-21 21:12:19.571389326 +0200 ++++ trafficserver/proxy/http/HttpConfig.h 2022-05-21 21:12:19.567389350 +0200 +@@ -847,7 +847,7 @@ + MgmtByte referer_filter_enabled = 0; + MgmtByte referer_format_redirect = 0; + +- MgmtByte strict_uri_parsing = 0; ++ MgmtByte strict_uri_parsing = 2; + + MgmtByte reverse_proxy_enabled = 0; + MgmtByte url_remap_required = 1; +Index: trafficserver/tests/gold_tests/headers/gold/bad_good_request.gold +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ trafficserver/tests/gold_tests/headers/gold/bad_good_request.gold 2022-05-21 21:12:19.567389350 +0200 +@@ -0,0 +1,9 @@ ++``HTTP/1.1 400 Invalid HTTP Request ++``Connection: close ++``Server: ATS/`` ++``Content-Length: 219 ++`` ++Bad Request ++``

Bad Request

++``Description: Could not process this request. ++`` +Index: trafficserver/tests/gold_tests/headers/gold/bad_good_request_header.gold +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ trafficserver/tests/gold_tests/headers/gold/bad_good_request_header.gold 2022-05-21 21:12:19.567389350 +0200 +@@ -0,0 +1,5 @@ ++``HTTP/1.1 400 Invalid HTTP Request ++``Connection: close ++``Server: ATS/`` ++``Content-Length: 219 ++`` +Index: trafficserver/tests/gold_tests/headers/gold/bad_good_request_http1.gold +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ trafficserver/tests/gold_tests/headers/gold/bad_good_request_http1.gold 2022-05-21 21:12:19.567389350 +0200 +@@ -0,0 +1,8 @@ ++``HTTP/1.0 400 Invalid HTTP Request ++``Server: ATS/`` ++``Content-Length: 219 ++`` ++Bad Request ++``

Bad Request

++``Description: Could not process this request. ++`` +Index: trafficserver/tests/gold_tests/headers/gold/bad_method.gold +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ trafficserver/tests/gold_tests/headers/gold/bad_method.gold 2022-05-21 21:12:19.567389350 +0200 +@@ -0,0 +1,24 @@ ++HTTP/1.1 501 Unsupported method ('gET') ++Content-Type: text/html;charset=utf-8 ++Content-Length: 496 ++Date: `` ++Age: 0 ++Connection: keep-alive ++Server: ATS/`` ++ ++ ++ ++ ++ ++ Error response ++ ++ ++

Error response

++

Error code: 501

++

Message: Unsupported method ('gET').

++

Error code explanation: HTTPStatus.NOT_IMPLEMENTED - Server does not support this operation.

++ ++ ++HTTP/1.1 200 OK ++`` +Index: trafficserver/tests/gold_tests/headers/gold/bad_protocol_number.gold +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ trafficserver/tests/gold_tests/headers/gold/bad_protocol_number.gold 2022-05-21 21:12:19.567389350 +0200 +@@ -0,0 +1,22 @@ ++HTTP/1.1 505 Unsupported HTTP Version ++Date: `` ++Server: ATS/`` ++Cache-Control: no-store ++Content-Type: text/html ++Content-Language: en ++Content-Length: 219 ++ ++ ++ ++Bad Request ++ ++ ++ ++

Bad Request

++
++ ++ ++Description: Could not process this request. ++ ++
++ +Index: trafficserver/tests/gold_tests/headers/gold/bad_te_value.gold +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ trafficserver/tests/gold_tests/headers/gold/bad_te_value.gold 2022-05-21 21:12:19.567389350 +0200 +@@ -0,0 +1,25 @@ ++HTTP/1.1 501 Field not implemented ++Date: `` ++Connection: keep-alive ++Server: ATS/`` ++Cache-Control: no-store ++Content-Type: text/html ++Content-Language: en ++Content-Length: 289 ++ ++ ++ ++Transcoding Not Available ++ ++ ++ ++

Transcoding Not Available

++
++ ++ ++ ++ Description: Unable to provide the document in the ++format requested by your browser. ++ ++
++ +Index: trafficserver/tests/gold_tests/headers/good_request_after_bad.test.py +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ trafficserver/tests/gold_tests/headers/good_request_after_bad.test.py 2022-05-21 21:12:19.567389350 +0200 +@@ -0,0 +1,196 @@ ++''' ++Verify that request following a ill-formed request is not processed ++''' ++# Licensed to the Apache Software Foundation (ASF) under one ++# or more contributor license agreements. See the NOTICE file ++# distributed with this work for additional information ++# regarding copyright ownership. The ASF licenses this file ++# to you under the Apache License, Version 2.0 (the ++# "License"); you may not use this file except in compliance ++# with the License. You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++import os ++ ++Test.Summary = ''' ++Verify that request following a ill-formed request is not processed ++''' ++Test.ContinueOnFail = True ++ts = Test.MakeATSProcess("ts") ++Test.ContinueOnFail = True ++ts.Disk.records_config.update({'proxy.config.diags.debug.tags': 'http', ++ 'proxy.config.diags.debug.enabled': 0, ++ 'proxy.config.http.strict_uri_parsing': 1 ++ }) ++ ++ts2 = Test.MakeATSProcess("ts2") ++ ++ts2.Disk.records_config.update({'proxy.config.diags.debug.tags': 'http', ++ 'proxy.config.diags.debug.enabled': 0, ++ 'proxy.config.http.strict_uri_parsing': 2 ++ }) ++ ++ ++server = Test.MakeOriginServer("server") ++request_header = {"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""} ++response_header = { ++ "headers": "HTTP/1.1 200 OK\r\nConnection: close\r\nLast-Modified: Tue, 08 May 2018 15:49:41 GMT\r\nCache-Control: max-age=1000\r\n\r\n", ++ "timestamp": "1469733493.993", ++ "body": "xxx"} ++server.addResponse("sessionlog.json", request_header, response_header) ++ ++ts.Disk.remap_config.AddLine( ++ 'map / http://127.0.0.1:{0}'.format(server.Variables.Port) ++) ++ts.Disk.remap_config.AddLine( ++ 'map /bob<> http://127.0.0.1:{0}'.format(server.Variables.Port) ++) ++ts2.Disk.remap_config.AddLine( ++ 'map / http://127.0.0.1:{0}'.format(server.Variables.Port) ++) ++ts2.Disk.remap_config.AddLine( ++ 'map /bob<> http://127.0.0.1:{0}'.format(server.Variables.Port) ++) ++ ++trace_out = Test.Disk.File("trace_curl.txt") ++ ++# Make a good request to get item in the cache for later tests ++tr = Test.AddTestRun("Good control") ++tr.Processes.Default.StartBefore(server) ++tr.Processes.Default.StartBefore(Test.Processes.ts) ++tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nHost: bob\r\n\r\n" | nc 127.0.0.1 {}'.format(ts.Variables.port) ++tr.Processes.Default.ReturnCode = 0 ++ ++tr = Test.AddTestRun("Good control") ++tr.Processes.Default.StartBefore(server) ++tr.Processes.Default.StartBefore(Test.Processes.ts2) ++tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nHost: bob\r\n\r\n" | nc 127.0.0.1 {}'.format(ts2.Variables.port) ++tr.Processes.Default.ReturnCode = 0 ++ ++tr = Test.AddTestRun("space after header name") ++tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nHost : bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++ ts.Variables.port) ++tr.Processes.Default.ReturnCode = 0 ++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request.gold' ++ ++# Commenting out a bunch of tests on master whose fixes are not in 8.1.x. ++#tr = Test.AddTestRun("Bad protocol number") ++#tr.Processes.Default.Command = 'printf "GET / HTTP/11.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++# ts.Variables.port) ++#tr.Processes.Default.ReturnCode = 0 ++#tr.Processes.Default.Streams.stdout = 'gold/bad_protocol_number.gold' ++# ++#tr = Test.AddTestRun("Unsupported Transfer Encoding value") ++#tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nhost: bob\r\ntransfer-encoding: random\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++# ts.Variables.port) ++#tr.Processes.Default.ReturnCode = 0 ++#tr.Processes.Default.Streams.stdout = 'gold/bad_te_value.gold' ++# ++#tr = Test.AddTestRun("Another unsupported Transfer Encoding value") ++#tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nhost: bob\r\ntransfer-encoding: \x08chunked\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++# ts.Variables.port) ++#tr.Processes.Default.ReturnCode = 0 ++#tr.Processes.Default.Streams.stdout = 'gold/bad_te_value.gold' ++# ++#tr = Test.AddTestRun("Extra characters in content-length") ++#tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nhost: bob\r\ncontent-length:+3\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++# ts.Variables.port) ++#tr.Processes.Default.ReturnCode = 0 ++#tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_header.gold' ++# ++#tr = Test.AddTestRun("Different extra characters in content-length") ++#tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nhost: bob\r\ncontent-length:\x0c3\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++# ts.Variables.port) ++#tr.Processes.Default.ReturnCode = 0 ++#tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_header.gold' ++# ++# ++## TRACE request with a body ++#tr = Test.AddTestRun("Trace request with a body") ++#tr.Processes.Default.Command = 'printf "TRACE /foo HTTP/1.1\r\nHost: bob\r\nContent-length:2\r\n\r\nokGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++# ts.Variables.port) ++#tr.Processes.Default.ReturnCode = 0 ++#tr.Processes.Default.Streams.stdout = 'gold/bad_good_request.gold' ++# ++#tr = Test.AddTestRun("Trace request with a chunked body") ++#tr.Processes.Default.Command = 'printf "TRACE /foo HTTP/1.1\r\nHost: bob\r\ntransfer-encoding: chunked\r\n\r\n2\r\nokGGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++# ts.Variables.port) ++#tr.Processes.Default.ReturnCode = 0 ++#tr.Processes.Default.Streams.stdout = 'gold/bad_good_request.gold' ++# ++#tr = Test.AddTestRun("Trace request with a chunked body via curl") ++#tr.Processes.Default.Command = 'curl -v --http1.1 --header "Transfer-Encoding: chunked" -d aaa -X TRACE -o trace_curl.txt -k http://127.0.0.1:{}/foo'.format( ++# ts.Variables.port) ++#tr.Processes.Default.ReturnCode = 0 ++#tr.Processes.Default.Streams.All = 'gold/bad_good_request_header.gold' ++#trace_out.Content = Testers.ContainsExpression("Bad Request", "ATS error msg") ++#trace_out.Content += Testers.ContainsExpression("Description: Could not process this request.", "ATS error msg") ++# ++#tr = Test.AddTestRun("Trace request via curl") ++#tr.Processes.Default.Command = 'curl -v --http1.1 -X TRACE -k http://127.0.0.1:{}/bar'.format(ts.Variables.port) ++#tr.Processes.Default.ReturnCode = 0 ++#tr.Processes.Default.Streams.All = Testers.ContainsExpression( ++# r"HTTP/1.1 501 Unsupported method \('TRACE'\)", ++# "microserver does not support TRACE") ++# ++## Methods are case sensitive. Verify that "gET" is not confused with "GET". ++#tr = Test.AddTestRun("mixed case method") ++#tr.Processes.Default.Command = 'printf "gET / HTTP/1.1\r\nHost:bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++# ts.Variables.port) ++#tr.Processes.Default.ReturnCode = 0 ++#tr.Processes.Default.Streams.stdout = 'gold/bad_method.gold' ++# ++## mangled termination ++#tr = Test.AddTestRun("mangled line termination") ++#tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nHost:bob\r\n \r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++# ts.Variables.port) ++#tr.Processes.Default.ReturnCode = 0 ++#tr.Processes.Default.Streams.stdout = 'gold/bad_good_request.gold' ++ ++tr = Test.AddTestRun("Catch bad URL characters") ++tr.Processes.Default.Command = 'printf "GET /bob<> HTTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++ ts.Variables.port) ++tr.Processes.Default.ReturnCode = 0 ++# Since the request line is messsed up ATS will reply with HTTP/1.0 ++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_http1.gold' ++ ++tr = Test.AddTestRun("Catch whitespace in URL") ++tr.Processes.Default.Command = 'printf "GET /bob foo HTTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++ ts.Variables.port) ++tr.Processes.Default.ReturnCode = 0 ++# Since the request line is messsed up ATS will reply with HTTP/1.0 ++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_http1.gold' ++ ++tr = Test.AddTestRun("Extra characters in protocol") ++tr.Processes.Default.Command = 'printf "GET / HTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++ ts.Variables.port) ++tr.Processes.Default.ReturnCode = 0 ++# Since the request line is messsed up ATS will reply with HTTP/1.0 ++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_http1.gold' ++ ++tr = Test.AddTestRun("Characters that are strict but not case 2 bad") ++tr.Processes.Default.Command = 'printf "GET /bob<> HTTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++ ts2.Variables.port) ++tr.Processes.Default.ReturnCode = 0 ++tr.Processes.Default.Streams.All = Testers.ContainsExpression("HTTP/1.1 200 OK", "Success") ++ ++tr = Test.AddTestRun("Catch whitespace in URL") ++tr.Processes.Default.Command = 'printf "GET /bob foo HTTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++ ts2.Variables.port) ++tr.Processes.Default.ReturnCode = 0 ++# Since the request line is messsed up ATS will reply with HTTP/1.0 ++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_http1.gold' ++ ++tr = Test.AddTestRun("Extra characters in protocol") ++tr.Processes.Default.Command = 'printf "GET / HTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++ ts2.Variables.port) ++tr.Processes.Default.ReturnCode = 0 ++# Since the request line is messsed up ATS will reply with HTTP/1.0 ++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_http1.gold' diff --git a/patches/0021-CVE_2021_44759.patch b/patches/0021-CVE_2021_44759.patch new file mode 100644 index 00000000..c7eed126 --- /dev/null +++ b/patches/0021-CVE_2021_44759.patch @@ -0,0 +1,79 @@ +Description: Bug fix in origin connection handling +Origin: upstream +Applied-Upstream: https://github.com/apache/trafficserver/commit/d3f36f79820ea10c26573c742b1bbc370c351716 +Reviewed-by: Jean Baptiste Favre +Last-Update: 2022-05-21 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +Index: trafficserver/iocore/net/SSLNetVConnection.cc +=================================================================== +--- trafficserver.orig/iocore/net/SSLNetVConnection.cc 2022-05-21 21:12:19.763388158 +0200 ++++ trafficserver/iocore/net/SSLNetVConnection.cc 2022-05-21 21:12:19.763388158 +0200 +@@ -1036,8 +1036,7 @@ + // Making the check here instead of later, so we only + // do this setting immediately after we create the SSL object + SNIConfig::scoped_config sniParam; +- int8_t clientVerify = 0; +- cchar *serverKey = this->options.sni_servername; ++ cchar *serverKey = this->options.sni_servername; + if (!serverKey) { + ats_ip_ntop(this->get_remote_addr(), buff, INET6_ADDRSTRLEN); + serverKey = buff; +@@ -1046,24 +1045,30 @@ + SSL_CTX *clientCTX = nullptr; + + if (nps) { +- clientCTX = nps->ctx; +- clientVerify = nps->verifyLevel; ++ clientCTX = nps->ctx; + } else { +- clientCTX = params->client_ctx; +- clientVerify = params->clientVerify; ++ clientCTX = params->client_ctx; + } ++ + if (!clientCTX) { + SSLErrorVC(this, "failed to create SSL client session"); + return EVENT_ERROR; + } + ++ if (nps && nps->verifyLevel != static_cast(YamlSNIConfig::Level::UNSET)) { ++ this->options.clientVerificationFlag = nps->verifyLevel; ++ } else { ++ // Keeping backwards compatibility on the proxy.config.ssl.client.verify.server setting ++ this->options.clientVerificationFlag = params->clientVerify ? (params->clientVerify == 1 ? 2 : 1) : 0; ++ } ++ + this->ssl = make_ssl_connection(clientCTX, this); + if (this->ssl == nullptr) { + SSLErrorVC(this, "failed to create SSL client session"); + return EVENT_ERROR; + } + int verify_op; +- if (clientVerify) { ++ if (this->options.clientVerificationFlag) { + verify_op = SSL_VERIFY_PEER; + SSL_set_verify(this->ssl, verify_op, verify_callback); + } else { +Index: trafficserver/iocore/net/YamlSNIConfig.h +=================================================================== +--- trafficserver.orig/iocore/net/YamlSNIConfig.h 2022-05-21 21:12:19.763388158 +0200 ++++ trafficserver/iocore/net/YamlSNIConfig.h 2022-05-21 21:12:19.763388158 +0200 +@@ -44,7 +44,7 @@ + client_cert + + }; +- enum class Level { NONE = 0, MODERATE, STRICT }; ++ enum class Level { NONE = 0, MODERATE, STRICT, UNSET }; + + YamlSNIConfig() {} + +@@ -53,7 +53,7 @@ + bool disable_h2 = false; + uint8_t verify_client_level = 0; + std::string tunnel_destination; +- uint8_t verify_origin_server = 0; ++ uint8_t verify_origin_server = static_cast(Level::UNSET); + std::string client_cert; + std::string ip_allow; + }; diff --git a/patches/CVE-2021-37150.patch b/patches/CVE-2021-37150.patch new file mode 100644 index 00000000..0d015fe0 --- /dev/null +++ b/patches/CVE-2021-37150.patch @@ -0,0 +1,118 @@ +From 4da63a69cbce10a6cd4d103de9f9b01d9c9be908 Mon Sep 17 00:00:00 2001 +From: Brian Neradt +Date: Mon, 8 Aug 2022 22:18:49 -0500 +Subject: [PATCH] Add back validatation that the scheme matches the wire + protocol (#9007) + +This adds back in the scheme and wire protocol check (see #8465) along +with a configuration to be able to disable the check if the verification +is not desired. +--- + doc/admin-guide/files/records.config.en.rst | 23 +++++++++++++++++++++ + mgmt/RecordsConfig.cc | 2 ++ + proxy/http/HttpConfig.cc | 4 +++- + proxy/http/HttpConfig.h | 1 + + proxy/http/HttpSM.cc | 14 +++++++++++++ + 5 files changed, 43 insertions(+), 1 deletion(-) + +--- a/doc/admin-guide/files/records.config.en.rst ++++ b/doc/admin-guide/files/records.config.en.rst +@@ -3413,6 +3413,29 @@ Client-Related Configuration + + Enables (``1``) or disables (``0``) TLSv1_1 in the ATS client context. If not specified, enabled by default + ++.. ts:cv:: CONFIG proxy.config.ssl.client.scheme_proto_mismatch_policy INT 2 ++ :overridable: ++ ++ This option controls how |TS| behaves when the client side connection ++ protocol and the client request's scheme do not match. For example, if ++ enforcement is enabled by setting this value to ``2`` and the client ++ connection is a cleartext HTTP connection but the scheme of the URL is ++ ``https://``, then |TS| will emit a warning and return an immediate 400 HTTP ++ response without proxying the request to the origin. ++ ++ The default value is ``2``, meaning that |TS| will enforce that the protocol ++ matches the scheme. ++ ++ ===== ====================================================================== ++ Value Description ++ ===== ====================================================================== ++ ``0`` Disable verification that the protocol and scheme match. ++ ``1`` Check that the protocol and scheme match, but only emit a warning if ++ they do not. ++ ``2`` Check that the protocol and scheme match and, if they do not, emit a ++ warning and return an immediate HTTP 400 response. ++ ===== ====================================================================== ++ + .. ts:cv:: CONFIG proxy.config.ssl.client.TLSv1_2 INT 1 + + Enables (``1``) or disables (``0``) TLSv1_2 in the ATS client context. If not specified, enabled by default +--- a/mgmt/RecordsConfig.cc ++++ b/mgmt/RecordsConfig.cc +@@ -1137,6 +1137,8 @@ static const RecordElement RecordsConfig + , + {RECT_CONFIG, "proxy.config.ssl.client.CA.cert.path", RECD_STRING, TS_BUILD_SYSCONFDIR, RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} + , ++ {RECT_CONFIG, "proxy.config.ssl.client.scheme_proto_mismatch_policy", RECD_INT, "2", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL} ++ , + {RECT_CONFIG, "proxy.config.ssl.session_cache", RECD_INT, "2", RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} + , + {RECT_CONFIG, "proxy.config.ssl.session_cache.size", RECD_INT, "102400", RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} +--- a/proxy/http/HttpConfig.cc ++++ b/proxy/http/HttpConfig.cc +@@ -1225,7 +1225,7 @@ HttpConfig::startup() + HttpEstablishStaticConfigByte(c.redirection_host_no_port, "proxy.config.http.redirect_host_no_port"); + HttpEstablishStaticConfigLongLong(c.oride.number_of_redirections, "proxy.config.http.number_of_redirections"); + HttpEstablishStaticConfigLongLong(c.post_copy_size, "proxy.config.http.post_copy_size"); +- ++ HttpEstablishStaticConfigByte(c.scheme_proto_mismatch_policy, "proxy.config.ssl.client.scheme_proto_mismatch_policy"); + http_config_cont->handleEvent(EVENT_NONE, nullptr); + + return; +@@ -1492,6 +1492,8 @@ HttpConfig::reconfigure() + params->oride.client_cert_filename = ats_strdup(m_master.oride.client_cert_filename); + params->oride.client_cert_filepath = ats_strdup(m_master.oride.client_cert_filepath); + ++ params->scheme_proto_mismatch_policy = m_master.scheme_proto_mismatch_policy; ++ + params->negative_caching_list = m_master.negative_caching_list; + + m_id = configProcessor.set(m_id, params); +--- a/proxy/http/HttpConfig.h ++++ b/proxy/http/HttpConfig.h +@@ -873,6 +873,7 @@ public: + + MgmtInt body_factory_response_max_size = 8192; + ++ MgmtByte scheme_proto_mismatch_policy = 2; + // noncopyable + ///////////////////////////////////// + // operator = and copy constructor // +--- a/proxy/http/HttpSM.cc ++++ b/proxy/http/HttpSM.cc +@@ -727,17 +727,20 @@ HttpSM::state_read_client_request_header + case PARSE_RESULT_DONE: + SMDebug("http", "[%" PRId64 "] done parsing client request header", sm_id); + +- if (!is_internal) { ++ if (!is_internal && t_state.http_config_param->scheme_proto_mismatch_policy != 0) { + auto scheme = t_state.hdr_info.client_request.url_get()->scheme_get_wksidx(); + if ((client_connection_is_ssl && (scheme == URL_WKSIDX_HTTP || scheme == URL_WKSIDX_WS)) || + (!client_connection_is_ssl && (scheme == URL_WKSIDX_HTTPS || scheme == URL_WKSIDX_WSS))) { +- SMDebug("http", "scheme [%s] vs. protocol [%s] mismatch", hdrtoken_index_to_wks(scheme), ++ Warning("scheme [%s] vs. protocol [%s] mismatch", hdrtoken_index_to_wks(scheme), + client_connection_is_ssl ? "tls" : "plaintext"); +- t_state.http_return_code = HTTP_STATUS_BAD_REQUEST; +- call_transact_and_set_next_state(HttpTransact::BadRequest); +- break; ++ if (t_state.http_config_param->scheme_proto_mismatch_policy == 2) { ++ t_state.http_return_code = HTTP_STATUS_BAD_REQUEST; ++ call_transact_and_set_next_state(HttpTransact::BadRequest); ++ break; ++ } + } + } ++ + ua_txn->set_session_active(); + + if (t_state.hdr_info.client_request.version_get() == HTTPVersion(1, 1) && diff --git a/patches/CVE-2022-25763.patch b/patches/CVE-2022-25763.patch new file mode 100644 index 00000000..4e6e75cd --- /dev/null +++ b/patches/CVE-2022-25763.patch @@ -0,0 +1,619 @@ +From 0ca9ef5abc8a535d05150ebc7c16bbfa4e982d16 Mon Sep 17 00:00:00 2001 +From: Masaori Koshiba +Date: Tue, 9 Aug 2022 12:19:13 +0900 +Subject: [PATCH] [8.1.x] Backport HTTP Validations (#9015) + +--- + .gitignore | 1 + + include/tscore/ParseRules.h | 14 +++- + proxy/hdrs/MIME.cc | 15 ++++ + proxy/hdrs/MIME.h | 12 +-- + proxy/hdrs/Makefile.am | 21 +++++ + proxy/hdrs/URL.cc | 42 +++++++--- + proxy/hdrs/URL.h | 2 + + proxy/hdrs/unit_tests/test_Hdrs.cc | 80 +++++++++++++++++++ + proxy/hdrs/unit_tests/test_URL.cc | 46 +++++++++++ + proxy/hdrs/unit_tests/unit_test_main.cc | 44 ++++++++++ + proxy/http/HttpTunnel.h | 12 +-- + proxy/http2/HTTP2.cc | 25 ++++-- + proxy/http2/Http2ConnectionState.cc | 14 +++- + proxy/http2/Http2Stream.cc | 6 +- + .../gold/invalid_character_in_te_value.gold | 23 ++++++ + .../headers/good_request_after_bad.test.py | 6 ++ + 16 files changed, 332 insertions(+), 31 deletions(-) + create mode 100644 proxy/hdrs/unit_tests/test_Hdrs.cc + create mode 100644 proxy/hdrs/unit_tests/test_URL.cc + create mode 100644 proxy/hdrs/unit_tests/unit_test_main.cc + create mode 100644 tests/gold_tests/headers/gold/invalid_character_in_te_value.gold + +--- a/include/tscore/ParseRules.h ++++ b/include/tscore/ParseRules.h +@@ -126,7 +126,7 @@ public: + static CTypeResult is_empty(char c); // wslfcr,# + static CTypeResult is_alnum(char c); // 0-9,A-Z,a-z + static CTypeResult is_space(char c); // ' ' HT,VT,NP,CR,LF +- static CTypeResult is_control(char c); // 0-31 127 ++ static CTypeResult is_control(char c); // 0x00-0x08, 0x0a-0x1f, 0x7f + static CTypeResult is_mime_sep(char c); // ()<>,;\"/[]?{} \t + static CTypeResult is_http_field_name(char c); // not : or mime_sep except for @ + static CTypeResult is_http_field_value(char c); // not CR, LF, comma, or " +@@ -665,14 +665,24 @@ ParseRules::is_space(char c) + #endif + } + ++/** ++ Return true if @c is a control char except HTAB(0x09) and SP(0x20). ++ If you need to check @c is HTAB or SP, use `ParseRules::is_ws`. ++ */ + inline CTypeResult + ParseRules::is_control(char c) + { + #ifndef COMPILE_PARSE_RULES + return (parseRulesCType[(unsigned char)c] & is_control_BIT); + #else +- if (((unsigned char)c) < 32 || ((unsigned char)c) == 127) ++ if (c == CHAR_HT || c == CHAR_SP) { ++ return false; ++ } ++ ++ if (((unsigned char)c) < 0x20 || c == 0x7f) { + return true; ++ } ++ + return false; + #endif + } +--- a/proxy/hdrs/MIME.cc ++++ b/proxy/hdrs/MIME.cc +@@ -2679,6 +2679,21 @@ mime_parser_parse(MIMEParser *parser, Hd + + int field_name_wks_idx = hdrtoken_tokenize(field_name_first, field_name_length); + ++ if (field_name_wks_idx < 0) { ++ for (int i = 0; i < field_name_length; ++i) { ++ if (ParseRules::is_control(field_name_first[i])) { ++ return PARSE_RESULT_ERROR; ++ } ++ } ++ } ++ ++ // RFC 9110 Section 5.5. Field Values ++ for (int i = 0; i < field_value_length; ++i) { ++ if (ParseRules::is_control(field_value_first[i])) { ++ return PARSE_RESULT_ERROR; ++ } ++ } ++ + /////////////////////////////////////////// + // build and insert the new field object // + /////////////////////////////////////////// +--- a/proxy/hdrs/MIME.h ++++ b/proxy/hdrs/MIME.h +@@ -164,7 +164,7 @@ struct MIMEField { + int value_get_comma_list(StrList *list) const; + + void name_set(HdrHeap *heap, MIMEHdrImpl *mh, const char *name, int length); +- bool name_is_valid() const; ++ bool name_is_valid(uint32_t invalid_char_bits = is_control_BIT) const; + + void value_set(HdrHeap *heap, MIMEHdrImpl *mh, const char *value, int length); + void value_set_int(HdrHeap *heap, MIMEHdrImpl *mh, int32_t value); +@@ -176,7 +176,7 @@ struct MIMEField { + // Other separators (e.g. ';' in Set-cookie/Cookie) are also possible + void value_append(HdrHeap *heap, MIMEHdrImpl *mh, const char *value, int length, bool prepend_comma = false, + const char separator = ','); +- bool value_is_valid() const; ++ bool value_is_valid(uint32_t invalid_char_bits = is_control_BIT) const; + int has_dups() const; + }; + +@@ -768,13 +768,13 @@ MIMEField::name_set(HdrHeap *heap, MIMEH + -------------------------------------------------------------------------*/ + + inline bool +-MIMEField::name_is_valid() const ++MIMEField::name_is_valid(uint32_t invalid_char_bits) const + { + const char *name; + int length; + + for (name = name_get(&length); length > 0; length--) { +- if (ParseRules::is_control(name[length - 1])) { ++ if (ParseRules::is_type(name[length - 1], invalid_char_bits)) { + return false; + } + } +@@ -875,13 +875,13 @@ MIMEField::value_append(HdrHeap *heap, M + -------------------------------------------------------------------------*/ + + inline bool +-MIMEField::value_is_valid() const ++MIMEField::value_is_valid(uint32_t invalid_char_bits) const + { + const char *value; + int length; + + for (value = value_get(&length); length > 0; length--) { +- if (ParseRules::is_control(value[length - 1])) { ++ if (ParseRules::is_type(value[length - 1], invalid_char_bits)) { + return false; + } + } +--- a/proxy/hdrs/Makefile.am ++++ b/proxy/hdrs/Makefile.am +@@ -62,9 +62,31 @@ load_http_hdr_LDADD = -L. -lhdrs \ + $(top_builddir)/src/tscpp/util/libtscpputil.la \ + @LIBTCL@ + +-check_PROGRAMS = test_mime ++check_PROGRAMS = test_proxy_hdrs \ ++ test_mime ++ ++TESTS = $(check_PROGRAMS) ++ ++test_proxy_hdrs_CPPFLAGS = $(AM_CPPFLAGS) \ ++ -I$(abs_top_srcdir)/tests/include ++ ++test_proxy_hdrs_SOURCES = \ ++ unit_tests/unit_test_main.cc \ ++ unit_tests/test_URL.cc \ ++ unit_tests/test_Hdrs.cc ++ ++test_proxy_hdrs_LDADD = \ ++ $(top_builddir)/src/tscore/libtscore.la \ ++ -L. -lhdrs \ ++ $(top_builddir)/src/tscore/libtscore.la \ ++ $(top_builddir)/src/tscpp/util/libtscpputil.la \ ++ $(top_builddir)/iocore/eventsystem/libinkevent.a \ ++ $(top_builddir)/lib/records/librecords_p.a \ ++ $(top_builddir)/mgmt/libmgmt_p.la \ ++ $(top_builddir)/proxy/shared/libUglyLogStubs.a \ ++ @HWLOC_LIBS@ \ ++ @LIBCAP@ + +-TESTS = test_mime + + test_mime_LDADD = -L. -lhdrs \ + $(top_builddir)/src/tscore/libtscore.la \ +--- a/proxy/hdrs/URL.cc ++++ b/proxy/hdrs/URL.cc +@@ -114,6 +114,36 @@ validate_host_name(std::string_view addr + return std::all_of(addr.begin(), addr.end(), &is_host_char); + } + ++/** ++ Checks if the (un-well-known) scheme is valid ++ ++ RFC 3986 Section 3.1 ++ These are the valid characters in a scheme: ++ scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ++ return an error if there is another character in the scheme ++*/ ++bool ++validate_scheme(std::string_view scheme) ++{ ++ if (scheme.empty()) { ++ return false; ++ } ++ ++ if (!ParseRules::is_alpha(scheme[0])) { ++ return false; ++ } ++ ++ for (size_t i = 0; i < scheme.size(); ++i) { ++ const char &c = scheme[i]; ++ ++ if (!(ParseRules::is_alnum(c) != 0 || c == '+' || c == '-' || c == '.')) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ + /*------------------------------------------------------------------------- + -------------------------------------------------------------------------*/ + +@@ -1140,19 +1170,9 @@ url_parse_scheme(HdrHeap *heap, URLImpl + + if (!(scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME)) { + // Unknown scheme, validate the scheme +- +- // RFC 3986 Section 3.1 +- // These are the valid characters in a scheme: +- // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) +- // return an error if there is another character in the scheme +- if (!ParseRules::is_alpha(*scheme_start)) { ++ if (!validate_scheme({scheme_start, static_cast(scheme_end - scheme_start)})) { + return PARSE_RESULT_ERROR; + } +- for (cur = scheme_start + 1; cur < scheme_end; ++cur) { +- if (!(ParseRules::is_alnum(*cur) != 0 || *cur == '+' || *cur == '-' || *cur == '.')) { +- return PARSE_RESULT_ERROR; +- } +- } + } + url_scheme_set(heap, url, scheme_start, scheme_wks_idx, scheme_end - scheme_start, copy_strings_p); + } +--- a/proxy/hdrs/URL.h ++++ b/proxy/hdrs/URL.h +@@ -156,6 +156,8 @@ void url_adjust(MarshalXlate *str_xlate, + + /* Public */ + bool validate_host_name(std::string_view addr); ++bool validate_scheme(std::string_view scheme); ++ + void url_init(); + + URLImpl *url_create(HdrHeap *heap); +--- /dev/null ++++ b/proxy/hdrs/unit_tests/test_Hdrs.cc +@@ -0,0 +1,80 @@ ++/** @file ++ ++ Catch-based unit tests for various header logic. ++ This replaces the old regression tests in HdrTest.cc. ++ ++ @section license License ++ ++ Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. ++ See the NOTICE file distributed with this work for additional information regarding copyright ++ ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the ++ "License"); you may not use this file except in compliance with the License. You may obtain a ++ copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++ Unless required by applicable law or agreed to in writing, software distributed under the License ++ is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express ++ or implied. See the License for the specific language governing permissions and limitations under ++ the License. ++ */ ++ ++#include ++ ++#include "catch.hpp" ++ ++#include "MIME.h" ++ ++TEST_CASE("HdrTest", "[proxy][hdrtest]") ++{ ++ mime_init(); ++ ++ SECTION("Field Char Check") ++ { ++ static struct { ++ std::string_view line; ++ ParseResult expected; ++ } test_cases[] = { ++ //// ++ // Field Name ++ {"Content-Length: 10\r\n", PARSE_RESULT_CONT}, ++ {"Content-Length\x0b: 10\r\n", PARSE_RESULT_ERROR}, ++ //// ++ // Field Value ++ // SP ++ {"Content-Length: 10\r\n", PARSE_RESULT_CONT}, ++ {"Foo: ab cd\r\n", PARSE_RESULT_CONT}, ++ // HTAB ++ {"Foo: ab\td/cd\r\n", PARSE_RESULT_CONT}, ++ // VCHAR ++ {"Foo: ab\x21/cd\r\n", PARSE_RESULT_CONT}, ++ {"Foo: ab\x7e/cd\r\n", PARSE_RESULT_CONT}, ++ // DEL ++ {"Foo: ab\x7f/cd\r\n", PARSE_RESULT_ERROR}, ++ // obs-text ++ {"Foo: ab\x80/cd\r\n", PARSE_RESULT_CONT}, ++ {"Foo: ab\xff/cd\r\n", PARSE_RESULT_CONT}, ++ // control char ++ {"Content-Length: 10\x0b\r\n", PARSE_RESULT_ERROR}, ++ {"Content-Length:\x0b 10\r\n", PARSE_RESULT_ERROR}, ++ {"Foo: ab\x1d/cd\r\n", PARSE_RESULT_ERROR}, ++ }; ++ ++ MIMEHdr hdr; ++ MIMEParser parser; ++ mime_parser_init(&parser); ++ ++ for (const auto &t : test_cases) { ++ mime_parser_clear(&parser); ++ ++ const char *start = t.line.data(); ++ const char *end = start + t.line.size(); ++ ++ int r = hdr.parse(&parser, &start, end, false, false); ++ if (r != t.expected) { ++ std::printf("Expected %s is %s, but not", t.line.data(), t.expected == PARSE_RESULT_ERROR ? "invalid" : "valid"); ++ CHECK(false); ++ } ++ } ++ } ++} +--- /dev/null ++++ b/proxy/hdrs/unit_tests/test_URL.cc +@@ -0,0 +1,46 @@ ++/** @file ++ ++ Catch-based unit tests for URL ++ ++ @section license License ++ ++ Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. ++ See the NOTICE file distributed with this work for additional information regarding copyright ++ ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the ++ "License"); you may not use this file except in compliance with the License. You may obtain a ++ copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++ Unless required by applicable law or agreed to in writing, software distributed under the License ++ is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express ++ or implied. See the License for the specific language governing permissions and limitations under ++ the License. ++ */ ++ ++#include ++ ++#include "catch.hpp" ++ ++#include "URL.h" ++ ++TEST_CASE("Validate Scheme", "[proxy][validscheme]") ++{ ++ static const struct { ++ std::string_view text; ++ bool valid; ++ } scheme_test_cases[] = {{"http", true}, {"https", true}, {"example", true}, {"example.", true}, ++ {"example++", true}, {"example--.", true}, {"++example", false}, {"--example", false}, ++ {".example", false}, {"example://", false}}; ++ ++ for (auto i : scheme_test_cases) { ++ // it's pretty hard to debug with ++ // CHECK(validate_scheme(i.text) == i.valid); ++ ++ std::string_view text = i.text; ++ if (validate_scheme(text) != i.valid) { ++ std::printf("Validation of scheme: \"%s\", expected %s, but not\n", text.data(), (i.valid ? "true" : "false")); ++ CHECK(false); ++ } ++ } ++} +--- /dev/null ++++ b/proxy/hdrs/unit_tests/unit_test_main.cc +@@ -0,0 +1,44 @@ ++/** @file ++ ++ This file used for catch based tests. It is the main() stub. ++ ++ @section license License ++ ++ Licensed to the Apache Software Foundation (ASF) under one ++ or more contributor license agreements. See the NOTICE file ++ distributed with this work for additional information ++ regarding copyright ownership. The ASF licenses this file ++ to you under the Apache License, Version 2.0 (the ++ "License"); you may not use this file except in compliance ++ with the License. You may obtain a copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++ Unless required by applicable law or agreed to in writing, software ++ distributed under the License is distributed on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ See the License for the specific language governing permissions and ++ limitations under the License. ++ */ ++ ++#include "HTTP.h" ++ ++#define CATCH_CONFIG_RUNNER ++#include "catch.hpp" ++ ++extern int cmd_disable_pfreelist; ++ ++int ++main(int argc, char *argv[]) ++{ ++ // No thread setup, forbid use of thread local allocators. ++ cmd_disable_pfreelist = true; ++ // Get all of the HTTP WKS items populated. ++ http_init(); ++ ++ int result = Catch::Session().run(argc, argv); ++ ++ // global clean-up... ++ ++ return result; ++} +--- a/proxy/http/HttpTunnel.h ++++ b/proxy/http/HttpTunnel.h +@@ -282,6 +282,7 @@ public: + } + bool is_tunnel_alive() const; + bool has_cache_writer() const; ++ bool has_consumer_besides_client() const; + + HttpTunnelProducer *add_producer(VConnection *vc, int64_t nbytes, IOBufferReader *reader_start, HttpProducerHandler sm_handler, + HttpTunnelType_t vc_type, const char *name); +@@ -515,6 +516,30 @@ HttpTunnel::has_cache_writer() const + } + + inline bool ++HttpTunnel::has_consumer_besides_client() const ++{ ++ bool res = false; // case of no consumers ++ ++ for (const auto &consumer : consumers) { ++ if (!consumer.alive) { ++ continue; ++ } ++ ++ switch (consumer.vc_type) { ++ case HT_HTTP_CLIENT: ++ continue; ++ case HT_HTTP_SERVER: ++ // ignore uploading data to servers ++ continue; ++ default: ++ return true; ++ } ++ } ++ ++ return res; ++} ++ ++inline bool + HttpTunnelConsumer::is_downstream_from(VConnection *vc) + { + HttpTunnelProducer *p = producer; +--- a/proxy/http2/HTTP2.cc ++++ b/proxy/http2/HTTP2.cc +@@ -420,19 +420,33 @@ http2_convert_header_from_2_to_1_1(HTTPH + int scheme_len, authority_len, path_len, method_len; + + // Get values of :scheme, :authority and :path to assemble requested URL +- if ((field = headers->field_find(HTTP2_VALUE_SCHEME, HTTP2_LEN_SCHEME)) != nullptr && field->value_is_valid()) { ++ if ((field = headers->field_find(HTTP2_VALUE_SCHEME, HTTP2_LEN_SCHEME)) != nullptr && ++ field->value_is_valid(is_control_BIT | is_ws_BIT)) { + scheme = field->value_get(&scheme_len); ++ ++ const char *scheme_wks; ++ ++ int scheme_wks_idx = hdrtoken_tokenize(scheme, scheme_len, &scheme_wks); ++ ++ if (!(scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME)) { ++ // unkown scheme, validate the scheme ++ if (!validate_scheme({scheme, static_cast(scheme_len)})) { ++ return PARSE_RESULT_ERROR; ++ } ++ } + } else { + return PARSE_RESULT_ERROR; + } + +- if ((field = headers->field_find(HTTP2_VALUE_AUTHORITY, HTTP2_LEN_AUTHORITY)) != nullptr && field->value_is_valid()) { ++ if ((field = headers->field_find(HTTP2_VALUE_AUTHORITY, HTTP2_LEN_AUTHORITY)) != nullptr && ++ field->value_is_valid(is_control_BIT | is_ws_BIT)) { + authority = field->value_get(&authority_len); + } else { + return PARSE_RESULT_ERROR; + } + +- if ((field = headers->field_find(HTTP2_VALUE_PATH, HTTP2_LEN_PATH)) != nullptr && field->value_is_valid()) { ++ if ((field = headers->field_find(HTTP2_VALUE_PATH, HTTP2_LEN_PATH)) != nullptr && ++ field->value_is_valid(is_control_BIT | is_ws_BIT)) { + path = field->value_get(&path_len); + } else { + return PARSE_RESULT_ERROR; +@@ -452,7 +466,8 @@ http2_convert_header_from_2_to_1_1(HTTPH + arena.str_free(url); + + // Get value of :method +- if ((field = headers->field_find(HTTP2_VALUE_METHOD, HTTP2_LEN_METHOD)) != nullptr && field->value_is_valid()) { ++ if ((field = headers->field_find(HTTP2_VALUE_METHOD, HTTP2_LEN_METHOD)) != nullptr && ++ field->value_is_valid(is_control_BIT | is_ws_BIT)) { + method = field->value_get(&method_len); + + int method_wks_idx = hdrtoken_tokenize(method, method_len); +@@ -494,7 +509,7 @@ http2_convert_header_from_2_to_1_1(HTTPH + // Check validity of all names and values + MIMEFieldIter iter; + for (const MIMEField *field = headers->iter_get_first(&iter); field != nullptr; field = headers->iter_get_next(&iter)) { +- if (!field->name_is_valid() || !field->value_is_valid()) { ++ if (!field->name_is_valid(is_control_BIT | is_ws_BIT) || !field->value_is_valid()) { + return PARSE_RESULT_ERROR; + } + } +--- a/proxy/http2/Http2ConnectionState.cc ++++ b/proxy/http2/Http2ConnectionState.cc +@@ -127,7 +127,7 @@ rcv_data_frame(Http2ConnectionState &cst + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE); + } + if (!stream->payload_length_is_valid()) { +- return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, ++ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, + "recv data bad payload length"); + } + } +@@ -347,6 +347,12 @@ rcv_headers_frame(Http2ConnectionState & + } + } + ++ // Check Content-Length & payload length when END_STREAM flag is true ++ if (stream->recv_end_stream && !stream->payload_length_is_valid()) { ++ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, ++ "recv data bad payload length"); ++ } ++ + // Set up the State Machine + if (!empty_request) { + SCOPED_MUTEX_LOCK(stream_lock, stream->mutex, this_ethread()); +@@ -878,6 +884,12 @@ rcv_continuation_frame(Http2ConnectionSt + } + } + ++ // Check Content-Length & payload length when END_STREAM flag is true ++ if (stream->recv_end_stream && !stream->payload_length_is_valid()) { ++ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR, ++ "recv data bad payload length"); ++ } ++ + // Set up the State Machine + SCOPED_MUTEX_LOCK(stream_lock, stream->mutex, this_ethread()); + stream->new_transaction(); +--- a/proxy/http2/Http2Stream.cc ++++ b/proxy/http2/Http2Stream.cc +@@ -146,7 +146,11 @@ void + Http2Stream::send_request(Http2ConnectionState &cstate) + { + // Convert header to HTTP/1.1 format +- http2_convert_header_from_2_to_1_1(&_req_header); ++ if (http2_convert_header_from_2_to_1_1(&_req_header) == PARSE_RESULT_ERROR) { ++ // There's no way to cause Bad Request directly at this time. ++ // Set an invalid method so it causes an error later. ++ _req_header.method_set("\xffVOID", 1); ++ } + + // Write header to a buffer. Borrowing logic from HttpSM::write_header_into_buffer. + // Seems like a function like this ought to be in HTTPHdr directly +--- /dev/null ++++ b/tests/gold_tests/headers/gold/invalid_character_in_te_value.gold +@@ -0,0 +1,23 @@ ++HTTP/1.1 400 Invalid HTTP Request ++Date:`` ++Connection: close ++Server:`` ++Cache-Control: no-store ++Content-Type: text/html ++Content-Language: en ++Content-Length:`` ++ ++ ++ ++Bad Request ++ ++ ++ ++

Bad Request

++
++ ++ ++Description: Could not process this request. ++ ++
++ +--- a/tests/gold_tests/headers/good_request_after_bad.test.py ++++ b/tests/gold_tests/headers/good_request_after_bad.test.py +@@ -80,6 +80,12 @@ tr.Processes.Default.Command = 'printf " + tr.Processes.Default.ReturnCode = 0 + tr.Processes.Default.Streams.stdout = 'gold/bad_good_request.gold' + ++tr = Test.AddTestRun("Another unsupported Transfer Encoding value") ++tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nhost: bob\r\ntransfer-encoding: \x08chunked\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( ++ ts.Variables.port) ++tr.Processes.Default.ReturnCode = 0 ++tr.Processes.Default.Streams.stdout = 'gold/invalid_character_in_te_value.gold' ++ + # Commenting out a bunch of tests on master whose fixes are not in 8.1.x. + #tr = Test.AddTestRun("Bad protocol number") + #tr.Processes.Default.Command = 'printf "GET / HTTP/11.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc 127.0.0.1 {}'.format( diff --git a/patches/series b/patches/series new file mode 100644 index 00000000..5d0f4236 --- /dev/null +++ b/patches/series @@ -0,0 +1,27 @@ +0001-Use-mcx16-on-x86-platforms-only.patch +0003-reproductible-build.patch +0006-fix-doc-build.patch +0008-fix-python-check-unused-dependencies.patch +0009-fix-mysql-8-build.patch +0011-fix-segfault.patch +0012-fix-spelling-checks.patch +0013-fix-perl-interpreter-path.patch +0014-use_system_yaml-cpp.patch +0015-8.0.4-CVE-backport.patch +0015-8.0.5-CVE-backport.patch +0016-CVE-2019-17559.patch +0016-CVE-2019-17565.patch +0016-CVE-2020-1944.patch +0016-CVE-2020-9481.patch +0017-CVE-2020-9494.patch +0018-CVE-2020-17508.patch +0018-CVE-2020-17509.patch +0019-CVE-2021-35474_32567_32566_32565_27577.patch +0020-CVE-2021-37147.patch +0020-CVE-2021-37148.patch +0020-CVE-2021-37149.patch +0020-CVE-2021-38161.patch +0021-CVE_2021_44040.patch +0021-CVE_2021_44759.patch +CVE-2022-25763.patch +CVE-2021-37150.patch diff --git a/rules b/rules new file mode 100755 index 00000000..127eaae4 --- /dev/null +++ b/rules @@ -0,0 +1,80 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_CFLAGS_MAINT_APPEND = -O3 +export DEB_CXXFLAGS_MAINT_APPEND = -O3 +# mips & mipsel need explicit libatomic link +ifeq ($(DEB_HOST_ARCH),$(filter $(DEB_HOST_ARCH),mips mipsel)) + export DEB_LDFLAGS_MAINT_APPEND += -latomic +endif + +# Get build architecture. ./configure is different on Linux +include /usr/share/dpkg/architecture.mk +# Get rid f dpkg-parsechangelog +include /usr/share/dpkg/pkg-info.mk + +configure_flags = \ + --enable-layout=Debian \ + --sysconfdir=/etc/trafficserver --libdir=/usr/lib/trafficserver \ + --libexecdir=/usr/lib/trafficserver/modules \ + --with-user=root --with-group=root --disable-silent-rules \ + --enable-experimental-plugins --enable-32bit-build \ + --enable-mime-sanity-check --enable-docs \ + --with-build-version=$(DEB_VERSION) \ + --with-yaml-cpp=/usr \ + $(shell dpkg-buildflags --export=configure) + +ifeq ($(DEB_HOST_ARCH_OS),linux) + configure_flags += --enable-wccp +endif + +%: + dh $@ --with autoreconf + +override_dh_auto_configure: + dh_auto_configure -- $(configure_flags) + +override_dh_auto_install: + dh_auto_install -- INSTALLDIRS=vendor + rm -f debian/tmp/usr/bin/trafficserver # We install our own + # Satisfy §10.2 (http://wiki.debian.org/ReleaseGoals/LAFileRemoval) + rm -f debian/tmp/usr/lib/trafficserver/lib*.la + rm -f debian/tmp/usr/lib/trafficserver/modules/*.la + rm -f debian/tmp/usr/lib/trafficserver/lib*.a +ifneq ($(DEB_HOST_ARCH_OS),linux) + # Remove Linux-specific plugin + sed -i '/\/healthchecks\.so$$/d' \ + debian/trafficserver.install +endif + +override_dh_install: + dh_install + dh_missing --list-missing + ./debian/change_config.pl debian/trafficserver/etc/trafficserver/records.config + # Lintian fixes + mkdir -p debian/trafficserver/usr/share/doc/trafficserver + cat CHANGELOG-* >> debian/trafficserver/usr/share/doc/trafficserver/changelog + #rm debian/trafficserver-dev/usr/share/doc/trafficserver-dev/examples/ssl-preaccept/ssl_preaccept.config + #(cd debian/trafficserver-dev/usr/share/doc/trafficserver-dev/examples/ssl-preaccept/; \ + #ln -s ssl_preaccept.config ssl_sni.config) + +override_dh_fixperms: + dh_fixperms -Xvar/lib/trafficserver \ + -Xvar/log/trafficserver \ + -Xvar/cache/trafficserver \ + -Xvar/run/trafficserver + # Fix lintian warning + chmod -x debian/trafficserver-dev/usr/share/doc/trafficserver-dev/examples/remap_header_add/build.txt + +override_dh_installexamples: + dh_installexamples -XMakefile -X.libs -X.dirstamp -X.deps -X.la -X.lo + +override_dh_makeshlibs: + dh_makeshlibs -Xdebian/tmp/usr/lib/trafficserver + +override_dh_auto_test: + -dh_auto_test diff --git a/source/format b/source/format new file mode 100644 index 00000000..163aaf8d --- /dev/null +++ b/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/source/options b/source/options new file mode 100644 index 00000000..9693b2ab --- /dev/null +++ b/source/options @@ -0,0 +1 @@ +--extend-diff-ignore='examples/|diags.log|lib/perl/' diff --git a/trafficserver-dev.examples b/trafficserver-dev.examples new file mode 100644 index 00000000..0bbe99ef --- /dev/null +++ b/trafficserver-dev.examples @@ -0,0 +1 @@ +example/* diff --git a/trafficserver-dev.install b/trafficserver-dev.install new file mode 100644 index 00000000..973eea9a --- /dev/null +++ b/trafficserver-dev.install @@ -0,0 +1,4 @@ +debian/tmp/usr/include/* +debian/tmp/usr/bin/tsxs +debian/tmp/usr/lib/trafficserver/lib*.so +debian/tmp/usr/lib/trafficserver/pkgconfig/trafficserver.pc diff --git a/trafficserver-dev.manpages b/trafficserver-dev.manpages new file mode 100644 index 00000000..c1687793 --- /dev/null +++ b/trafficserver-dev.manpages @@ -0,0 +1,2 @@ +debian/tmp/usr/share/man/man1/tsxs.1 +debian/tmp/usr/share/man/man3/* diff --git a/trafficserver-experimental-plugins.install b/trafficserver-experimental-plugins.install new file mode 100644 index 00000000..5d8c3707 --- /dev/null +++ b/trafficserver-experimental-plugins.install @@ -0,0 +1,36 @@ +usr/lib/trafficserver/modules/access_control.so +usr/lib/trafficserver/modules/acme.so +usr/lib/trafficserver/modules/balancer.so +usr/lib/trafficserver/modules/buffer_upload.so +usr/lib/trafficserver/modules/cache_key_genid.so +usr/lib/trafficserver/modules/cache_range_requests.so +usr/lib/trafficserver/modules/certifier.so +usr/lib/trafficserver/modules/collapsed_forwarding.so +usr/lib/trafficserver/modules/custom_redirect.so +usr/lib/trafficserver/modules/fq_pacing.so +usr/lib/trafficserver/modules/geoip_acl.so +usr/lib/trafficserver/modules/header_freq.so +usr/lib/trafficserver/modules/header_normalize.so +usr/lib/trafficserver/modules/hipes.so +usr/lib/trafficserver/modules/hook-trace.so +usr/lib/trafficserver/modules/inliner.so +usr/lib/trafficserver/modules/tsmemcache.so +usr/lib/trafficserver/modules/memcached_remap.so +usr/lib/trafficserver/modules/metalink.so +usr/lib/trafficserver/modules/money_trace.so +usr/lib/trafficserver/modules/mp4.so +usr/lib/trafficserver/modules/multiplexer.so +usr/lib/trafficserver/modules/mysql_remap.so +usr/lib/trafficserver/modules/prefetch.so +usr/lib/trafficserver/modules/remap_purge.so +usr/lib/trafficserver/modules/remap_stats.so +usr/lib/trafficserver/modules/server_push_preload.so +usr/lib/trafficserver/modules/ssl_cert_loader.so +usr/lib/trafficserver/modules/sslheaders.so +usr/lib/trafficserver/modules/stale_while_revalidate.so +usr/lib/trafficserver/modules/stream_editor.so +usr/lib/trafficserver/modules/system_stats.so +usr/lib/trafficserver/modules/tls_bridge.so +usr/lib/trafficserver/modules/traffic_dump.so +usr/lib/trafficserver/modules/uri_signing.so +usr/lib/trafficserver/modules/url_sig.so diff --git a/trafficserver-experimental-plugins.lintian-overrides b/trafficserver-experimental-plugins.lintian-overrides new file mode 100644 index 00000000..77c80dbf --- /dev/null +++ b/trafficserver-experimental-plugins.lintian-overrides @@ -0,0 +1,3 @@ +# changelog is already provided in trafficserver main package. +# no need to duplicate it in each package +no-upstream-changelog diff --git a/trafficserver.default b/trafficserver.default new file mode 100644 index 00000000..ca86af35 --- /dev/null +++ b/trafficserver.default @@ -0,0 +1,41 @@ +## Defaults for trafficserver initscript +## sourced by /etc/init.d/trafficserver +## installed at /etc/default/trafficserver by the maintainer scripts + +## +## This is a POSIX shell fragment +## + +## Variable: RUNDIR +## Default: /var/run/trafficserver +## Description: Set this to the directory where runtime data is stored. The +## default value should work fine for almost all users. +# RUNDIR=/var/run/trafficserver + + +## Configuration for `traffic_manager'. +## Meaning of variables is analogous to traffic_cop above, but for the +## `traffic_manager' binary. + +TM_START=yes +# TM_DAEMON_ARGS="" +# TM_PIDFILE=$RUNDIR/manager.lock + +## +## NOTICE: +## Typically you do not want to configure anything below. Note, generally Traffic +## Server is started through `traffic_cop' which is a watchdog to control any local +## Traffic Server instances. It starts both, traffic_manager and traffic_server, as +## does it monitor these processes. While it is generally not advised, you can +## choose to manage both processes yourself. In such cases do not set TC_START to +## "yes" and enable any service you want below. +## +## Choose either alternative, but do not mix up both. + +## Configuration for `traffic_server'. +## Meaning of variables is analogous to traffic_cop above, but for the +## `traffic_server' binary. + +# TS_START=no +# TS_DAEMON_ARGS="" +# TS_PIDFILE=$RUNDIR/server.lock diff --git a/trafficserver.dirs b/trafficserver.dirs new file mode 100644 index 00000000..91e6eaf4 --- /dev/null +++ b/trafficserver.dirs @@ -0,0 +1,2 @@ +/var/cache/trafficserver +/var/log/trafficserver diff --git a/trafficserver.example b/trafficserver.example new file mode 100644 index 00000000..b9d32e1e --- /dev/null +++ b/trafficserver.example @@ -0,0 +1,2 @@ +plugins/experimental/cacheurl/*.example +plugins/experimental/mysql_remap/sample.ini diff --git a/trafficserver.init b/trafficserver.init new file mode 100644 index 00000000..cc1179b0 --- /dev/null +++ b/trafficserver.init @@ -0,0 +1,257 @@ +#! /bin/sh + +### BEGIN INIT INFO +# Provides: trafficserver +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: init script for the Apache Traffic Server +# Description: Apache Traffic Server is fast, scalable and extensible +# HTTP/1.1 compliant caching proxy server. +### END INIT INFO + +# Author: Arno Töll +# +# This init script is derived from the source package's version shipped +# along the source tarball as rc/trafficserver. Therefore it is a derivative +# work and licensed as follows: +# +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="Apache Traffic Server" +NAME=trafficserver +SCRIPTNAME=/etc/init.d/$NAME + + +# Please do not touch TS_ROOT and TS_BASE. Traffic Server uses them +# They are used to determine location of ATS components on the file +# system. +# According to DPM § 9.9 a program must not depend on the existance of +# environment variables to work properly. Please report any errors if +# you experience such a problem, for me it seems to work just fine with- +# out +ESED=/usr/bin/sed +test -x $ESED || ESED=sed +TS_PREFIX="/usr" +TS_ROOT=${TS_ROOT:-$TS_PREFIX} + +# TS_BASE is offset inside the file system from where the layout starts +# For standard installations TS_BASE will be empty +eval TS_BASE="`echo $TS_ROOT | ${ESED} -e 's;/usr$;;'`" + +# Set some safe defaults. So not change values here, override them in +# in /etc/default/trafficserver instead. +# See there for a documentation as well + +RUNDIR=${RUNDIR:-$TS_BASE/var/run/trafficserver} + +TM_START=${TM_START:-no} +TM_NAME=${TM_NAME:-traffic_manager} +TM_DAEMON=${TM_DAEMON:-$TS_BASE/usr/bin/traffic_manager} +TM_DAEMON_ARGS="" +TM_PIDFILE=${TM_PIDFILE:-$RUNDIR/manager.lock} + +TS_START=${TS_START:-no} +TS_NAME=${TS_NAME:-traffic_server} +TS_DAEMON=${TS_DAEMON:-$TS_BASE/usr/bin/traffic_server} +TS_DAEMON_ARGS="" +TS_PIDFILE=${TS_PIDFILE:-$RUNDIR/server.lock} + +# Exit if the package is not installed +[ -x "$TM_DAEMON" ] || exit 0 + + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +. /lib/lsb/init-functions + +# Check permissions of /etc/trafficserver. +# Traffic Server needs write permissions, so warn the user if we suppose it +# wouldn't. +# The sysadmin is welcome to change the user ID that ATS uses. However to do that +# in a clean and supported way, the administrator should overwrite the `stat override' +# Debian installs by default in Trafficserver's postinst maintainer script. +# Print a warning only. +# Since this can't be safely determined by this script print a warning only, but +# don't fail. +CONF_DIR='/etc/trafficserver' +USER=$(dpkg-statoverride --list "$CONF_DIR" | awk '{print $1}') +OWNER=$(env stat -c '%U' "$CONF_DIR") +if [ -d "$CONF_DIR" ] && [ ! "x$OWNER" = "x$USER" ] ; then + log_warning_msg "Configuration directory '$CONF_DIR' is not owned by user '$USER'. " \ + "However Traffic Server needs write permissions to it." +fi + + +# Make sure $RUNDIR exists as the underlying file system +# may be volatile (see § 9.3.2 from DPM) +install -d -o trafficserver -g trafficserver -m 0755 "$RUNDIR" + + +# A helper function, its purpose is to start a daemon. +# Arguments are interpreted in order as follows: +# 1) The executable path +# 2) A string containing optional daemon arguments +# 3) A (valid) path containing the PID file for the daemon +# Returns: +# 0 if daemon has been started +# 1 if daemon was already running +# 2 if daemon could not be started +start_cmd() +{ + # Args + DAEMON=$1 + DAEMON_ARGS=$2 + PID=$3 + + #echo "\n\n" + #echo "d:" $DAEMON + #echo "da:" $DAEMON_ARGS + #echo "pid:" $PID + + start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --background --pidfile $PID --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + + return 0 +} + + +# A helper function, its purpose is to stop a daemon. +# Arguments are interpreted in order as follows: +# 1) The daemon name (i.e. the binary name) +# 2) The executable path +# 3) A (valid) path containing the PID file for the daemon +# Returns: +# 0 if daemon has been stopped +# 1 if daemon was already stopped +# 2 if daemon could not be stopped +# Another value if a failure occurred +stop_cmd() +{ + NAME=$1 + DAEMON=$2 + PID=$3 + + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PID --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + + # Many daemons don't delete their pidfiles when they exit. + rm -f $PID + return "$RETVAL" +} + +# The start function +# This function does everything required to bring up the service +# at boot time. +# It does not accept any arguments +do_start() { + if [ "x$TM_START" != "xno" ]; then + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $TM_NAME" + start_cmd "$TM_DAEMON" "$TM_DAEMON_ARGS" "$TM_PIDFILE" + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + fi + + if [ "x$TS_START" != "xno" ]; then + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $TS_NAME" + start_cmd "$TS_DAEMON" "$TS_DAEMON_ARGS" "$TS_PIDFILE" + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + fi +} + + +# The stop function +# This function does everything required to stop the service. +# It does not accept any arguments +do_stop() { + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $TM_NAME" + stop_cmd "$TM_NAME" "$TM_DAEMON" "$TM_PIDFILE" + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $TS_NAME" + stop_cmd "$TS_NAME" "$TS_DAEMON" "$TS_PIDFILE" + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac +} + +case "$1" in + start) + if [ "x$TM_START" = "xno" ] && [ "x$TS_START" = "xno" ]; then + [ "$VERBOSE" != no ] && log_warning_msg "Not starting $DESC" + else + do_start + fi + ;; + stop) + do_stop + ;; + status) + if [ "x$TM_START" != "xno" ] ; then + status_of_proc "$TM_DAEMON" "$TM_NAME" -p "$TM_PIDFILE" && exit 0 || exit $? + else + status_of_proc "$TS_DAEMON" "$TS_NAME" -p "$TS_PIDFILE" || exit $? + fi + ;; + restart|force-reload) + log_daemon_msg "Restarting $DESC" "$NAME\n" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/trafficserver.install b/trafficserver.install new file mode 100644 index 00000000..aa971f52 --- /dev/null +++ b/trafficserver.install @@ -0,0 +1,26 @@ +usr/bin/traffic_* +usr/bin/tspush +etc/trafficserver/* +usr/lib/trafficserver/lib*.so.* +usr/lib/trafficserver/modules/authproxy.so +usr/lib/trafficserver/modules/background_fetch.so +usr/lib/trafficserver/modules/cachekey.so +usr/lib/trafficserver/modules/cache_promote.so +usr/lib/trafficserver/modules/combo_handler.so +usr/lib/trafficserver/modules/compress.so +usr/lib/trafficserver/modules/conf_remap.so +usr/lib/trafficserver/modules/escalate.so +usr/lib/trafficserver/modules/esi.so +usr/lib/trafficserver/modules/generator.so +usr/lib/trafficserver/modules/header_rewrite.so +usr/lib/trafficserver/modules/healthchecks.so +usr/lib/trafficserver/modules/libloader.so +usr/lib/trafficserver/modules/regex_remap.so +usr/lib/trafficserver/modules/regex_revalidate.so +usr/lib/trafficserver/modules/s3_auth.so +usr/lib/trafficserver/modules/stats_over_http.so +usr/lib/trafficserver/modules/tcpinfo.so +usr/lib/trafficserver/modules/test_cppapi.so +usr/lib/trafficserver/modules/tslua.so +usr/lib/trafficserver/modules/xdebug.so +usr/lib/perl5/* usr/share/perl5/ diff --git a/trafficserver.maintscript b/trafficserver.maintscript new file mode 100644 index 00000000..51810a2e --- /dev/null +++ b/trafficserver.maintscript @@ -0,0 +1,9 @@ +rm_conffile /etc/trafficserver/vaddrs.config 8.0.0~ trafficserver +rm_conffile /etc/trafficserver/metrics.config 8.0.0~ trafficserver +rm_conffile /etc/trafficserver/logging.config 8.0.0~ trafficserver +rm_conffile /etc/trafficserver/log_hosts.config 8.0.0~ trafficserver +rm_conffile /etc/trafficserver/congestion.config 8.0.0~ trafficserver +rm_conffile /etc/trafficserver/cluster.config 8.0.0~ trafficserver +rm_conffile /etc/trafficserver/body_factory/default/congestion#retryAfter 8.0.0~ trafficserver +rm_conffile /etc/trafficserver/icp.config 8.0.0~ trafficserver +rm_conffile /etc/trafficserver/snapshosts 8.0.0~ trafficserver diff --git a/trafficserver.manpages b/trafficserver.manpages new file mode 100644 index 00000000..b2bfc3aa --- /dev/null +++ b/trafficserver.manpages @@ -0,0 +1,4 @@ +debian/tmp/usr/share/man/man1/traffic_* +debian/tmp/usr/share/man/man1/tspush.1 +debian/tmp/usr/share/man/man8/traffic_* +debian/tmp/usr/share/man/man5/* diff --git a/trafficserver.postinst b/trafficserver.postinst new file mode 100755 index 00000000..23764905 --- /dev/null +++ b/trafficserver.postinst @@ -0,0 +1,127 @@ +#! /bin/sh +# postinst script for trafficserver +# +# see: dh_installdeb(1) +# +# Copyright 2011 Arno Toell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +set -e + +USER='trafficserver' +GROUP='trafficserver' +USER_HOME='/var/run/trafficserver' +OWNER=$(env stat -c '%U' /etc/trafficserver) +OWNER_CACHE_DIR=$(env stat -c '%U' /var/cache/trafficserver) + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see /usr/share/doc/packaging-manual/ +# +# quoting from the policy: +# Any necessary prompting should almost always be confined to the +# post-installation script, and should be protected with a conditional +# so that unnecessary prompting doesn't happen if a package's +# installation fails and the `postinst' is called with `abort-upgrade', +# `abort-remove' or `abort-deconfigure'. + +case "$1" in +configure) + + if ! getent passwd -- "$USER" >/dev/null 2>&1 ; then + adduser --home "$USER_HOME" \ + --group \ + --system \ + --disabled-password \ + --no-create-home \ + --gecos "Debian Traffic Server user" \ + $USER + fi + + if [ -d /etc/trafficserver ] && [ "x$OWNER" = "xroot" ] ; then + # Ok, I admit I am lazy. I don't check every permission + # the user may have changed. If he didn't for /etc I can + # safely assume he neither has for other directories (I + # hope, since /etc requires write permissions by ATS). + echo 'Fixing permissions ...' + + if ! dpkg-statoverride --list /etc/trafficserver >/dev/null 2>&1; then + dpkg-statoverride --update --add "$USER" "$GROUP" 0755 /etc/trafficserver + fi + + if ! dpkg-statoverride --list /var/log/trafficserver >/dev/null 2>&1; then + dpkg-statoverride --update --add "$USER" adm 0750 /var/log/trafficserver + fi + + if [ -d /var/cache/trafficserver ] && [ "x$OWNER_CACHE_DIR" = "xroot" ] ; then + if ! dpkg-statoverride --list /var/cache/trafficserver >/dev/null 2>&1; then + dpkg-statoverride --update --add "$USER" adm 0750 /var/cache/trafficserver + fi + fi + fi + + if [ -n "$2" ] && dpkg --compare-versions "$2" 'le' '3.2~' ; then + RET=0 + invoke-rc.d trafficserver status > /dev/null 2>&1 || RET=$? + # 0 => ATS is running + # 4 => Status is unknown + # 1,2,3 => ATS is not running + + # using /bin/echo to make sure -e is supported + ECHO=`which echo` + if [ "$RET" -gt 0 ] && [ "$RET" -ne 4 ] && [ -f /var/cache/trafficserver/host.db ] ; then + echo "Purging TrafficServer cache upon upgrade." + RET=0 + traffic_server -Cclear > /dev/null 2>&1 || RET=$? + if [ "$RET" -ne 0 ] ; then + $ECHO "=======================================================================" + $ECHO -e "WARNING: Apache TrafficServer's cache couldn't be purged during the upgrade.\n" \ + "Please inspect the situation manually and call 'traffic_server -Cclear'\n" \ + "afterwards to purge the caches." + $ECHO "=======================================================================" + fi + else + $ECHO "=======================================================================" + $ECHO -e "WARNING: Apache TrafficServer is not running or its state couldn't be\n" \ + "determined. Please inspect the situation manually and call\n" \ + "'traffic_server -Cclear' afterwards to purge the caches.\n" + $ECHO "=======================================================================" + + fi + fi +;; + +abort-upgrade|abort-remove|abort-deconfigure) + +;; + +*) +echo "postinst called with unknown argument \`$1'" >&2 +exit 0 +;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/trafficserver.service b/trafficserver.service new file mode 100644 index 00000000..3fb0f534 --- /dev/null +++ b/trafficserver.service @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +[Unit] +Description=Apache Traffic Server is a fast, scalable and extensible caching proxy server. +Documentation=man:traffic_server(8) +After=network-online.target + +[Service] +Type=simple +EnvironmentFile=-/etc/default/trafficserver +PIDFile=/run/trafficserver/manager.lock +ExecStart=/usr/bin/traffic_manager $TM_DAEMON_ARGS +ExecReload=/usr/bin/traffic_ctl config reload + +[Install] +WantedBy=multi-user.target diff --git a/trafficserver.tmpfile b/trafficserver.tmpfile new file mode 100644 index 00000000..231e3171 --- /dev/null +++ b/trafficserver.tmpfile @@ -0,0 +1 @@ +d /run/trafficserver 0755 trafficserver trafficserver diff --git a/upstream/signing-key.asc b/upstream/signing-key.asc new file mode 100644 index 00000000..d485de24 --- /dev/null +++ b/upstream/signing-key.asc @@ -0,0 +1,686 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBEpLdmsBEACozcRKl7GNzB++3Wu+ORuF5X65dACbS1yKF1QgQK2y3Prm8+vi +7RxEj63i8r+XCDv0a5rySqvoz6n+YOojqiWdNZ7xbK4lbkrpk5hJQqdS+zKcRzOf +JB82YLNkS7EFeHU38pBZPVgqdTDTjTH8tnbxrbZEMOHHuvekwS8ZylZgGZqxg9lM +BvDV99taRQqN5HyRqzFKAM6ro0YJACrxCxQaYPAO+JtQHhnyzch7SdkTbn0ABdWz +w/nlqi6GjgmQkdlKBnXFJgg7c3SPtFgAz66RqCMb5XnTn3cxNQKtNsgIJzJrUl73 +sZHwO2MLyHD7W/gnf923ylmj8oMgUOFvbZZjj60G7/5KfloEEU3QaNFHlkPPWamZ +MMjBnSc+SrrI5JjKhtm3v5kKA9M3Ivp4x7zKc8OWIX3Mhv9Fn6Z4+u0pgyY56FFb +gQ7bp/sVG0eTYG2eJncRWrI3GYzQ8Fqsap/Zpoh0joFS0CnXM9VZDqz2VY6eMsmB +hf51FW2S5SjarLwLvBMhfQcpiUhqVSSBheEzzQtOLi7ST14aiZHDkMehWol0DLQR +W3+Hrk9qLj+B8ubKwM9t5Ql4H+UItDF9/887BvHGVZaRDbynzmDpNf+Ouh0UGY2b +jHXpYWL8IfeKirp3USH4KtNv9v+FAXASD5sszfaCNHlXKKv/ot8C8bNnUQARAQAB +tBpMdWNhIENhcGVsbG8gPGx1Y2FAcGNhLml0PokCWQQTAQgAQwIbAwULCQgHAwUV +CgkICwUWAgMBAAIeAQIXgAIZAQUCTLIz0x4YaGtwOi8vcG9vbC5za3Mta2V5c2Vy +dmVycy5uZXQACgkQBuqgZuOXgy++1Q//TkRScLykaSwiCaxu0n00uzTsQ4G0NODc +OLPR0q5hj12h1yWa0I/I1jV4hI5tNxe+0A19+1aSZbbqLQLNJiZ7BKIR77UdkDMC +uu6KAkBowlIALm0oWsatEN6uwKpbD7cOJu7WPVVyv248Wk04l8cK9yNJesnKKiSy +BGJaWgdg0OtwX145/Z/jzkp3nBZATi9RF5JMIUfhWkrUGzfdF2Vv9gpadf1H5qJn +3+OYJ61B6a/WheEGvv/jVU53b307T422nmlXe00s2ME62muNICM7vbnngEkgv5yQ +GdFvRe8bTUSVoTYdjO5RtdaJg4lkfm7/C9oeDjZYPzmnhUfbZoqsO1mg+Vu9UJNF +hgEvzkAKGCNEdIpL4TvGCNtwtHNOaSnQCUGpugOh+DrkmvKrEltSbc5/6+mfEAvS +Y/WCiwSrOwRypfle2+45ykOmU3/kul5qHDd1KmX2ujghUhAZ526GnKtgFFPhXb8V +f3krh63YYgGdsiss/wTsay/uTZWMhW4LBO/No6kyV7rOwBu/K3SMZ8uhjBmpi3fT +XlsokFtZm1cURbHd61sjo4XnW8G/6vXppjSo8PcLLw51k5eTybVc6KMiqZb8TbB5 +gKe4R7Ih6CEEIe1GoqR+xqSJ7NitcXpU8m8qpDi2c9Q2XMtCQOCmbMY2hVtiAQXY +cn4s3a924a20H0x1Y2EgQ2FwZWxsbyA8Z2lzbW9AZGViaWFuLm9yZz6JAlEEEwEI +ADsCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AFAkyyNZIZGGhrcDovL2tleXJp +bmcuZGViaWFuLm9yZwAKCRAG6qBm45eDL1TBD/0VMshQaANEAhdfM+Xjk0E6TEkg +Ctj5DFaC5QN10X2z8UPm6WCjXrEkuMBfIA+dIMDLVtGNO1Ob72Q4LeE1fafuHk8T +jjRxil9n2JLPpfFxjRPP3XbKQiK2h7BQuF6x1UQc7xVW/FN8mrhA8eDgtnUi4ZBs +dQw731SAI51mWOC8auDCLm698DVP4jPK+1q5oMiZrvi1X1dDbrM2/d1oeZLR3sXB +lFMQlhRLh61g+TcgssgPIkCgN4TI0s4zz5Cewajs+FW63cmAbC40sBeUNqrHlXcU +XBNrjrmYxmquyVvRxNHn2B0LgMQ8C3dyO3TJoDh4r7d/Hq1uzjtYrlc5ZxjZ0WA4 +HHhcNb3JOrfmd32ER3+gty4XWAdpfO5+plbuxZ+PImaK3dBCdvB5cHDypTBvSeOb +bxaP3jiYlwTbsORJSylz537nlxoPUbN6ipBbywP50T8Fc/duZSYJd/djnv4Z2vRx +08A116KF1vojniWGry3P8kEYw/eQJIWaDMO6je4/vXC+dRyDidCJ2YexhSJi22Xw +op60NJZZmeYM1/HyPPmFaukrH9g9Pj0AcdCewZbsImiPxR0QJconLqyaXAXihOJY +DLhOHbDK+7o+SQpMZBeATUTpA5fWdN3PwkcE5RsxdzK3c34r/b1B1mmeIXaFowKB +M+11uiYM3yqdwnwb/7QpTHVjYSBDYXBlbGxvIDxsdWNhLmNhcGVsbG9AaW5mb21h +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 +=VvDM +-----END PGP PUBLIC KEY BLOCK----- +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +yMLkPeLfh5LLCwL80fgxSp7Z+UB1 +=MC/H +-----END PGP PUBLIC KEY BLOCK----- +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=3xB/ +-----END PGP PUBLIC KEY BLOCK----- +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +DOI2xbN2Mhjbj0mkwaQ= +=CIzB +-----END PGP PUBLIC KEY BLOCK----- +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=Ycol +-----END PGP PUBLIC KEY BLOCK----- diff --git a/watch b/watch new file mode 100644 index 00000000..b39b7a76 --- /dev/null +++ b/watch @@ -0,0 +1,5 @@ +version=4 +opts="pgpsigurlmangle=s/$/.asc/,dversionmangle=s/\+ds\d*$//,repacksuffix=+ds,repack,compression=xz" \ + https://www.apache.org/dist/trafficserver \ + trafficserver-(\d+\.\d+.\d+)\.tar\.bz2 \ + debian uupdate