Merge ~lucaskanashiro/ubuntu/+source/haproxy:focal-mre into ubuntu/+source/haproxy:ubuntu/focal-devel

Proposed by Lucas Kanashiro
Status: Merged
Approved by: git-ubuntu bot
Approved revision: not available
Merge reported by: Lucas Kanashiro
Merged at revision: 755142d4b5e5d59a7b76565fd3a63e4338bee448
Proposed branch: ~lucaskanashiro/ubuntu/+source/haproxy:focal-mre
Merge into: ubuntu/+source/haproxy:ubuntu/focal-devel
Diff against target: 3289 lines (+1130/-445)
72 files modified
.github/matrix.py (+6/-3)
.github/workflows/cross-zoo.yml (+110/-0)
.github/workflows/vtest.yml (+5/-2)
.github/workflows/windows.yml (+2/-1)
CHANGELOG (+100/-0)
Makefile (+5/-1)
SUBVERS (+1/-1)
VERDATE (+2/-2)
VERSION (+1/-1)
contrib/prometheus-exporter/service-prometheus.c (+1/-0)
contrib/wurfl/wurfl/wurfl.h (+10/-5)
debian/changelog (+20/-0)
debian/patches/0002-Use-dpkg-buildflags-to-build-halog.patch (+0/-2)
debian/patches/series (+0/-2)
debian/tests/control (+8/-0)
debian/tests/proxy-localhost (+5/-9)
debian/tests/proxy-ssl-pass-through (+59/-0)
debian/tests/proxy-ssl-termination (+48/-0)
debian/tests/utils (+58/-0)
dev/null (+0/-175)
doc/configuration.txt (+99/-41)
doc/management.txt (+4/-0)
doc/proxy-protocol.txt (+1/-1)
include/common/buf.h (+1/-1)
include/common/compiler.h (+3/-1)
include/common/memory.h (+2/-2)
include/common/standard.h (+3/-2)
include/proto/server.h (+1/-0)
include/proto/ssl_sock.h (+25/-0)
include/types/global.h (+1/-0)
include/types/listener.h (+11/-2)
reg-tests/http-messaging/http_abortonclose.vtc (+27/-5)
reg-tests/http-messaging/http_request_buffer.vtc (+29/-4)
scripts/announce-release (+12/-11)
scripts/make-releases-json (+103/-0)
scripts/publish-release (+6/-0)
src/backend.c (+0/-5)
src/cache.c (+7/-7)
src/cfgparse-listen.c (+2/-1)
src/cfgparse.c (+40/-12)
src/dns.c (+18/-6)
src/ev_epoll.c (+2/-2)
src/ev_evports.c (+2/-4)
src/ev_kqueue.c (+2/-2)
src/ev_poll.c (+2/-1)
src/flt_spoe.c (+22/-9)
src/h1.c (+4/-0)
src/haproxy.c (+10/-2)
src/hlua.c (+3/-1)
src/hlua_fcn.c (+3/-0)
src/hpack-dec.c (+9/-0)
src/http_fetch.c (+9/-6)
src/http_msg.c (+7/-1)
src/listener.c (+6/-0)
src/log.c (+8/-3)
src/memory.c (+6/-6)
src/mux_h1.c (+1/-1)
src/mux_h2.c (+9/-2)
src/mworker.c (+4/-2)
src/peers.c (+40/-23)
src/proto_http.c (+8/-2)
src/proto_htx.c (+1/-0)
src/proto_sockpair.c (+1/-1)
src/proxy.c (+24/-10)
src/sample.c (+2/-1)
src/server.c (+4/-5)
src/signal.c (+3/-0)
src/ssl_sock.c (+50/-27)
src/standard.c (+4/-3)
src/stick_table.c (+29/-17)
src/stream.c (+18/-8)
src/stream_interface.c (+1/-1)
Reviewer Review Type Date Requested Status
Andreas Hasenack Approve
git-ubuntu bot Approve
Canonical Server Reporter Pending
Review via email: mp+439428@code.launchpad.net

Description of the change

MRE update to version 2.0.31 (LP: #2012557). Some patches were removed (applied by upstream) and others refreshed.

the proposed package is available in this PPA:

https://launchpad.net/~lucaskanashiro/+archive/ubuntu/ha-stack

autopkgtest summary:

autopkgtest [18:17:03]: @@@@@@@@@@@@@@@@@@@@ summary
cli PASS
proxy-localhost PASS

To post a comment you must log in.
Revision history for this message
Andreas Hasenack (ahasenack) :
review: Needs Fixing
Revision history for this message
Lucas Kanashiro (lucaskanashiro) :
Revision history for this message
Lucas Kanashiro (lucaskanashiro) wrote :

Fixed. Up for review again.

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

+1

review: Approve
Revision history for this message
git-ubuntu bot (git-ubuntu-bot) wrote :

Approvers: lucaskanashiro, ahasenack
Uploaders: lucaskanashiro, ahasenack
MP auto-approved

review: Approve
fbe702c... by Lucas Kanashiro

Backport DEP-8 tests from Lunar

Revision history for this message
Lucas Kanashiro (lucaskanashiro) wrote :

I backported the DEP-8 tests from Lunar as I did in the Jammy MRE. All tests are still passing:

autopkgtest [17:14:27]: @@@@@@@@@@@@@@@@@@@@ summary
cli PASS
proxy-localhost PASS
proxy-ssl-termination PASS
proxy-ssl-pass-through PASS

Could you please re-check this part Andreas?

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

Looks good, thanks for that

review: Approve
Revision history for this message
Lucas Kanashiro (lucaskanashiro) wrote :

Thanks Andreas. Package uploaded:

Uploading haproxy_2.0.31-0ubuntu0.1.dsc
Uploading haproxy_2.0.31.orig.tar.gz
Uploading haproxy_2.0.31-0ubuntu0.1.debian.tar.xz
Uploading haproxy_2.0.31-0ubuntu0.1_source.changes

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.github/matrix.py b/.github/matrix.py
2index 2ea3053..3d704ae 100644
3--- a/.github/matrix.py
4+++ b/.github/matrix.py
5@@ -8,6 +8,7 @@
6
7 import json
8 import sys
9+from os import environ
10
11 if len(sys.argv) == 2:
12 build_type = sys.argv[1]
13@@ -50,7 +51,7 @@ matrix = []
14
15 # Ubuntu
16
17-os = "ubuntu-latest"
18+os = "ubuntu-20.04"
19 TARGET = "linux-glibc"
20 for CC in ["gcc", "clang"]:
21 matrix.append(
22@@ -125,7 +126,7 @@ for CC in ["gcc", "clang"]:
23
24 # ASAN
25
26-os = "ubuntu-latest"
27+os = "ubuntu-20.04"
28 CC = "clang"
29 TARGET = "linux-glibc"
30 matrix.append(
31@@ -173,4 +174,6 @@ for CC in ["clang"]:
32
33 print(json.dumps(matrix, indent=4, sort_keys=True))
34
35-print("::set-output name=matrix::{}".format(json.dumps({"include": matrix})))
36+if environ.get('GITHUB_OUTPUT') is not None:
37+ with open(environ.get('GITHUB_OUTPUT'), 'a') as f:
38+ print("matrix={}".format(json.dumps({"include": matrix})), file=f)
39diff --git a/.github/workflows/cross-zoo.yml b/.github/workflows/cross-zoo.yml
40new file mode 100644
41index 0000000..e2a5816
42--- /dev/null
43+++ b/.github/workflows/cross-zoo.yml
44@@ -0,0 +1,110 @@
45+#
46+# this is naamed "zoo" after OpenSSL "cross zoo pipeline"
47+#
48+name: Cross Compile
49+
50+on:
51+ schedule:
52+ - cron: "0 0 21 * *"
53+
54+permissions:
55+ contents: read
56+
57+jobs:
58+ cross-compilation:
59+ strategy:
60+ matrix:
61+ platform: [
62+ {
63+ arch: aarch64-linux-gnu,
64+ libs: libc6-dev-arm64-cross,
65+ target: linux-aarch64
66+ }, {
67+ arch: alpha-linux-gnu,
68+ libs: libc6.1-dev-alpha-cross,
69+ target: linux-alpha-gcc
70+ }, {
71+ arch: arm-linux-gnueabi,
72+ libs: libc6-dev-armel-cross,
73+ target: linux-armv4
74+ }, {
75+ arch: arm-linux-gnueabihf,
76+ libs: libc6-dev-armhf-cross,
77+ target: linux-armv4
78+ }, {
79+ arch: hppa-linux-gnu,
80+ libs: libc6-dev-hppa-cross,
81+ target: -static linux-generic32
82+ }, {
83+ arch: m68k-linux-gnu,
84+ libs: libc6-dev-m68k-cross,
85+ target: -static -m68040 linux-latomic
86+ }, {
87+ arch: mips-linux-gnu,
88+ libs: libc6-dev-mips-cross,
89+ target: -static linux-mips32
90+ }, {
91+ arch: mips64-linux-gnuabi64,
92+ libs: libc6-dev-mips64-cross,
93+ target: -static linux64-mips64
94+ }, {
95+ arch: mipsel-linux-gnu,
96+ libs: libc6-dev-mipsel-cross,
97+ target: linux-mips32
98+ }, {
99+ arch: powerpc64le-linux-gnu,
100+ libs: libc6-dev-ppc64el-cross,
101+ target: linux-ppc64le
102+ }, {
103+ arch: riscv64-linux-gnu,
104+ libs: libc6-dev-riscv64-cross,
105+ target: linux64-riscv64
106+ }, {
107+ arch: s390x-linux-gnu,
108+ libs: libc6-dev-s390x-cross,
109+ target: linux64-s390x
110+ }, {
111+ arch: sh4-linux-gnu,
112+ libs: libc6-dev-sh4-cross,
113+ target: no-async linux-latomic
114+ }, {
115+ arch: hppa-linux-gnu,
116+ libs: libc6-dev-hppa-cross,
117+ target: linux-generic32,
118+ }, {
119+ arch: m68k-linux-gnu,
120+ libs: libc6-dev-m68k-cross,
121+ target: -mcfv4e linux-latomic
122+ }, {
123+ arch: mips-linux-gnu,
124+ libs: libc6-dev-mips-cross,
125+ target: linux-mips32
126+ }, {
127+ arch: mips64-linux-gnuabi64,
128+ libs: libc6-dev-mips64-cross,
129+ target: linux64-mips64
130+ }, {
131+ arch: sparc64-linux-gnu,
132+ libs: libc6-dev-sparc64-cross,
133+ target: linux64-sparcv9
134+ }
135+ ]
136+ runs-on: ubuntu-latest
137+ steps:
138+ - name: install packages
139+ run: |
140+ sudo apt-get update
141+ sudo apt-get -yq --force-yes install \
142+ gcc-${{ matrix.platform.arch }} \
143+ ${{ matrix.platform.libs }}
144+ - uses: actions/checkout@v2
145+
146+
147+ - name: install quictls
148+ run: |
149+ QUICTLS_EXTRA_ARGS="--cross-compile-prefix=${{ matrix.platform.arch }}- ${{ matrix.platform.target }}" QUICTLS=yes scripts/build-ssl.sh
150+
151+ - name: Build
152+ run: |
153+ make ERR=1 CC=${{ matrix.platform.arch }}-gcc TARGET=linux-glibc USE_LIBCRYPT= USE_OPENSSL=1 USE_QUIC=1 USE_PROMEX=1 SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include ADDLIB="-Wl,-rpath,${HOME}/opt/lib"
154+
155diff --git a/.github/workflows/vtest.yml b/.github/workflows/vtest.yml
156index 8c838cc..b919418 100644
157--- a/.github/workflows/vtest.yml
158+++ b/.github/workflows/vtest.yml
159@@ -49,7 +49,7 @@ jobs:
160 - name: Generate cache key
161 id: generate-cache-key
162 run: |
163- echo "::set-output name=key::$(echo ${{ matrix.name }} | sha256sum | awk '{print $1}')"
164+ echo "key=$(echo ${{ matrix.name }} | sha256sum | awk '{print $1}')" >> $GITHUB_OUTPUT
165
166 - name: Cache SSL libs
167 if: ${{ matrix.ssl && matrix.ssl != 'stock' && matrix.ssl != 'BORINGSSL=yes' && matrix.ssl != 'QUICTLS=yes' }}
168@@ -96,6 +96,9 @@ jobs:
169 run: make -C contrib/wurfl
170 - name: Compile HAProxy with ${{ matrix.CC }}
171 run: |
172+ echo "::group::Show compiler's version"
173+ echo | ${{ matrix.CC }} -v
174+ echo "::endgroup::"
175 echo "::group::Show platform specific defines"
176 echo | ${{ matrix.CC }} -dM -xc -E -
177 echo "::endgroup::"
178@@ -119,7 +122,7 @@ jobs:
179 fi
180 echo "::endgroup::"
181 haproxy -vv
182- echo "::set-output name=version::$(haproxy -v |awk 'NR==1{print $3}')"
183+ echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
184 - name: Install problem matcher for VTest
185 # This allows one to more easily see which tests fail.
186 run: echo "::add-matcher::.github/vtest.json"
187diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
188index ceac981..0292e09 100644
189--- a/.github/workflows/windows.yml
190+++ b/.github/workflows/windows.yml
191@@ -55,9 +55,10 @@ jobs:
192 ERR=1 \
193 TARGET=${{ matrix.TARGET }} \
194 CC=${{ matrix.CC }} \
195+ DEBUG_CFLAGS="-g -Wno-deprecated-declarations" \
196 ${{ join(matrix.FLAGS, ' ') }}
197 - name: Show HAProxy version
198 id: show-version
199 run: |
200 ./haproxy -vv
201- echo "::set-output name=version::$(./haproxy -v |awk 'NR==1{print $3}')"
202+ echo "version=$(./haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
203diff --git a/CHANGELOG b/CHANGELOG
204index 6ae4bed..4b5713f 100644
205--- a/CHANGELOG
206+++ b/CHANGELOG
207@@ -1,6 +1,106 @@
208 ChangeLog :
209 ===========
210
211+2023/02/14 : 2.0.31
212+ - SCRIPTS: announce-release: add a link to the data plane API
213+ - CI: github: change "ubuntu-latest" to "ubuntu-20.04"
214+ - BUG/MEDIUM: ssl: Verify error codes can exceed 63
215+ - BUG/MINOR: ssl: Fix potential overflow
216+ - BUG/MEDIUM: mworker: fix segv in early failure of mworker mode with peers
217+ - BUG/MEDIUM: resolvers: Use tick_first() to update the resolvers task timeout
218+ - LICENSE: wurfl: clarify the dummy library license.
219+ - BUG/MEDIUM: mux-h2: Refuse interim responses with end-stream flag set
220+ - BUG/MINOR: pool/stats: Use ullong to report total pool usage in bytes in stats
221+ - BUILD: makefile: build the features list dynamically
222+ - BUILD: makefile: sort the features list
223+ - BUG/MAJOR: buf: Fix copy of wrapping output data when a buffer is realigned
224+ - BUG/MINOR: resolvers: Wait the resolution execution for a do_resolv action
225+ - BUG/MINOR: promex: Don't forget to consume the request on error
226+ - BUG/MINOR: http-fetch: Don't block HTTP sample fetch eval in HTTP_MSG_ERROR state
227+ - BUG/MINOR: http-ana: make set-status also update txn->status
228+ - BUG/MEDIUM: ssl: wrong eviction from the session cache tree
229+ - BUG/MEDIUM: stick-table: do not leave entries in end of window during purge
230+ - BUG/MEDIUM: cache: use the correct time reference when comparing dates
231+ - DOC: config: fix option spop-check proxy compatibility
232+ - DOC: config: 'http-send-name-header' option may be used in default section
233+ - DOC: proxy-protocol: fix wrong byte in provided example
234+ - CI: github: don't warn on deprecated openssl functions on windows
235+ - BUG/CRITICAL: http: properly reject empty http header field names
236+
237+2022/12/09 : 2.0.30
238+ - CI: determine actual LibreSSL version dynamically
239+ - BUILD: fix build warning on solaris based systems with __maybe_unused.
240+ - REGTESTS: abortonclose: Fix some race conditions
241+ - BUG/MINOR: peers: fix error reporting of "bind" lines
242+ - BUG/MEDIUM: http: Properly reject non-HTTP/1.x protocols
243+ - BUG/MEDIUM: peers: fix segfault using multiple bind on peers sections
244+ - BUG/MEDIUM: peers: prevent unitialized multiple listeners on peers section
245+ - BUG/MEDIUM: sample: Fix adjusting size in word converter
246+ - SCRIPTS: add make-releases-json to recreate a releases.json file in download dirs
247+ - SCRIPTS: make publish-release try to launch make-releases-json
248+ - DOC: peers: indicate that some server settings are not usable
249+ - DOC: peers: clarify when entry expiration date is renewed.
250+ - DOC: peers: fix port number and addresses on new peers section format
251+ - BUG/MINOR: conn_stream: do not confirm a connection from the frontend path
252+ - REGTESTS: abortonclose: Add a barrier to not mix up log messages
253+ - REGTESTS: http_request_buffer: Increase client timeout to wait "slow" clients
254+ - BUILD: compiler: implement unreachable for older compilers too
255+ - BUG/MINOR: server: do not enable DNS resolution on disabled proxies
256+ - BUG/MINOR: http-ana: Set method to HTTP_METH_OTHER when an HTTP txn is created
257+ - BUG/MINOR: http-fetch: Use integer value when possible in "method" sample fetch
258+ - BUG/MINOR: peers/config: always fill the bind_conf's argument
259+ - BUG/MINOR: peers: fix possible NULL dereferences at config parsing
260+ - BUG/MINOR: backend: Fallback on RR algo if balance on source is impossible
261+ - BUG/MINOR: sockpair: wrong return value for fd_send_uxst()
262+ - BUG/MINOR: ssl: free the fields in srv->ssl_ctx
263+ - MINOR: peers: Use a dedicated reconnect timeout when stopping the local peer
264+ - BUG/MEDIUM: peers: limit reconnect attempts of the old process on reload
265+ - BUG/MINOR: peers: Use right channel flag to consider the peer as connected
266+ - MINOR: server: Constify source server to copy its settings
267+ - REORG: server: Export srv_settings_cpy() function
268+ - BUG/MEDIUM: proxy: Perform a custom copy for default server settings
269+ - BUILD: http: silence an uninitialized warning affecting gcc-5
270+ - BUG/MEDIUM: mux-h2: do not fiddle with ->dsi to indicate demux is idle
271+ - BUG/MINOR: resolvers: return the correct value in resolvers_finalize_config()
272+ - DOC: configuration: do-resolve doesn't work with a port in the string
273+ - BUG/MEDIUM: spoe: Properly update streams waiting for a ACK in async mode
274+ - BUG/MEDIUM: peers: Add connect and server timeut to peers proxy
275+ - BUG/MEDIUM: peers: Don't use resync timer when local resync is in progress
276+ - BUG/MEDIUM: peers: Don't start resync on reload if local peer is not up-to-date
277+ - REGTESTS: http_request_buffer: Add a barrier to not mix up log messages
278+ - BUG/MINOR: h1: Support headers case adjustment for TCP proxies
279+ - BUG/MINOR: signals/poller: set the poller timeout to 0 when there are signals
280+ - BUG/MINOR: signals/poller: ensure wakeup from signals
281+ - BUG/MEDIUM: proxy: ensure pause_proxy() and resume_proxy() own PROXY_LOCK
282+ - BUG/MEDIUM: captures: free() an error capture out of the proxy lock
283+ - SCRIPTS: announce-release: update some URLs to https
284+ - BUG/MINOR: log: improper behavior when escaping log data
285+ - BUILD: fix compilation for OpenSSL-3.0.0-alpha17
286+ - BUILD: cfgparse: Fix GCC warning about a variable used after realloc
287+ - BUG/MEDIUM: lua: handle stick table implicit arguments right.
288+ - BUG/MINOR: http-fetch: Update method after a prefetch in smp_fetch_meth()
289+ - BUILD: http_fetch: silence an uninitiialized warning with gcc-4/5/6 at -Os
290+ - DOC: configuration: missing 'if' in tcp-request content example
291+ - BUG/MAJOR: stick-tables: do not try to index a server name for applets
292+ - CI: Replace the deprecated `::set-output` command by writing to $GITHUB_OUTPUT in matrix.py
293+ - CI: Replace the deprecated `::set-output` command by writing to $GITHUB_OUTPUT in workflow definition
294+ - DOC: management: add forgotten "show startup-logs"
295+ - BUG/MAJOR: stick-table: don't process store-response rules for applets
296+ - BUG/MEDIUM: stick-table: fix a race condition when updating the expiration task
297+ - CI: add monthly gcc cross compile jobs
298+ - CI: emit the compiler's version in the build reports
299+ - BUG/MEDIUM: listener: Fix race condition when updating the global mngmt task
300+ - BUG/MINOR: http_ana/txn: don't re-initialize txn and req var lists
301+ - BUG/MEDIUM: peers: messages about unkown tables not correctly ignored
302+ - BUILD: peers: Remove unused variables
303+ - BUILD: listener: fix build warning on global_listener_rwlock without threads
304+ - DOC: config: provide some configuration hints for "http-reuse"
305+ - DOC: config: clarify the fact that SNI should not be used in HTTP scenarios
306+ - DOC: config: explain how default matching method for ACL works
307+ - DOC: config: clarify the fact that "retries" is not just for connections
308+ - DOC: config: clarify the -m dir and -m dom pattern matching methods
309+ - Revert "CI: determine actual LibreSSL version dynamically"
310+
311 2022/05/13 : 2.0.29
312 - BUG/MINOR: tools: fix url2sa return value with IPv4
313 - Revert "BUG/MAJOR: mux-pt: Always destroy the backend connection on detach"
314diff --git a/Makefile b/Makefile
315index 7eddac0..bd1042b 100644
316--- a/Makefile
317+++ b/Makefile
318@@ -449,7 +449,11 @@ ignore_implicit = $(if $(subst environment,,$(origin $(1))), \
319 # is used to report a list of all flags which were used to build this version.
320 # Do not assign anything to it.
321 BUILD_OPTIONS := $(foreach opt,$(use_opts),$(call ignore_implicit,$(opt)))
322-BUILD_FEATURES := $(foreach opt,$(patsubst USE_%,%,$(use_opts)),$(if $(USE_$(opt)),+$(opt),-$(opt)))
323+
324+# Make a list of all known features with +/- prepended depending on their
325+# activation status. Must be a macro so that dynamically enabled ones are
326+# evaluated with their current status.
327+BUILD_FEATURES = $(foreach opt,$(patsubst USE_%,%,$(sort $(use_opts))),$(if $(USE_$(opt)),+$(opt),-$(opt)))
328
329 # All USE_* options have their equivalent macro defined in the code (some might
330 # possibly be unused though)
331diff --git a/SUBVERS b/SUBVERS
332index 50af805..e1824da 100644
333--- a/SUBVERS
334+++ b/SUBVERS
335@@ -1,2 +1,2 @@
336--5e15b0f
337+-c8b1c15
338
339diff --git a/VERDATE b/VERDATE
340index cba5e36..2f36592 100644
341--- a/VERDATE
342+++ b/VERDATE
343@@ -1,2 +1,2 @@
344-2022-05-13 17:43:21 +0200
345-2022/05/13
346+2023-02-14 16:58:28 +0100
347+2023/02/14
348diff --git a/VERSION b/VERSION
349index 3df5a46..e235558 100644
350--- a/VERSION
351+++ b/VERSION
352@@ -1 +1 @@
353-2.0.29
354+2.0.31
355diff --git a/contrib/prometheus-exporter/service-prometheus.c b/contrib/prometheus-exporter/service-prometheus.c
356index d420cbc..3cc502c 100644
357--- a/contrib/prometheus-exporter/service-prometheus.c
358+++ b/contrib/prometheus-exporter/service-prometheus.c
359@@ -2465,6 +2465,7 @@ static void promex_appctx_handle_io(struct appctx *appctx)
360 res->flags |= CF_READ_NULL;
361 si_shutr(si);
362 si_shutw(si);
363+ goto out;
364 }
365
366 struct applet promex_applet = {
367diff --git a/contrib/wurfl/wurfl/wurfl.h b/contrib/wurfl/wurfl/wurfl.h
368index ca512da..4e089c2 100644
369--- a/contrib/wurfl/wurfl/wurfl.h
370+++ b/contrib/wurfl/wurfl/wurfl.h
371@@ -4,11 +4,16 @@
372 * Copyright (c) ScientiaMobile, Inc.
373 * http://www.scientiamobile.com
374 *
375- * This software package is the property of ScientiaMobile Inc. and is licensed
376- * commercially according to a contract between the Licensee and ScientiaMobile Inc. (Licensor).
377- * If you represent the Licensee, please refer to the licensing agreement which has been signed
378- * between the two parties. If you do not represent the Licensee, you are not authorized to use
379- * this software in any way.
380+ * This software package is the property of ScientiaMobile Inc. and is distributed under
381+ * a dual licensing scheme:
382+ *
383+ * 1) commercially according to a contract between the Licensee and ScientiaMobile Inc. (Licensor).
384+ * If you represent the Licensee, please refer to the licensing agreement which has been signed
385+ * between the two parties. If you do not represent the Licensee, you are not authorized to use
386+ * this software in any way.
387+ *
388+ * 2) LGPL when used in the context of the HAProxy project with the purpose of testing compatibility
389+ * of HAProxy with ScientiaMobile software.
390 *
391 */
392
393diff --git a/debian/changelog b/debian/changelog
394index 2e61d7f..9980b1d 100644
395--- a/debian/changelog
396+++ b/debian/changelog
397@@ -1,3 +1,23 @@
398+haproxy (2.0.31-0ubuntu0.1) focal; urgency=medium
399+
400+ * New upstream release (LP: #2012557).
401+ - Major and critical bug fixes according to the upstream changelog:
402+ + BUG/MAJOR: stick-tables: do not try to index a server name for applets
403+ + BUG/MAJOR: stick-table: don't process store-response rules for applets
404+ + BUG/MAJOR: buf: Fix copy of wrapping output data when a buffer is
405+ realigned
406+ + BUG/CRITICAL: http: properly reject empty http header field names
407+ - Remove patches applied by upstream in debian/patches:
408+ + CVE-2023-0056.patch
409+ + CVE-2023-25725.patch
410+ - Refresh existing patches in debian/patches:
411+ + 0002-Use-dpkg-buildflags-to-build-halog.patch
412+ * Backport DEP-8 tests from Lunar:
413+ - d/t/proxy-ssl-termination
414+ - d/t/proxy-ssl-pass-through
415+
416+ -- Lucas Kanashiro <kanashiro@ubuntu.com> Wed, 22 Mar 2023 17:39:46 -0300
417+
418 haproxy (2.0.29-0ubuntu1.3) focal-security; urgency=medium
419
420 * SECURITY UPDATE: incorrect handling of empty http header field names
421diff --git a/debian/patches/0002-Use-dpkg-buildflags-to-build-halog.patch b/debian/patches/0002-Use-dpkg-buildflags-to-build-halog.patch
422index a039316..94e1fb1 100644
423--- a/debian/patches/0002-Use-dpkg-buildflags-to-build-halog.patch
424+++ b/debian/patches/0002-Use-dpkg-buildflags-to-build-halog.patch
425@@ -8,8 +8,6 @@ Last-Update: 2013-07-02
426 contrib/halog/Makefile | 16 +++++-----------
427 1 file changed, 5 insertions(+), 11 deletions(-)
428
429-diff --git a/contrib/halog/Makefile b/contrib/halog/Makefile
430-index 5e687c0..ab34027 100644
431 --- a/contrib/halog/Makefile
432 +++ b/contrib/halog/Makefile
433 @@ -1,22 +1,16 @@
434diff --git a/debian/patches/CVE-2023-0056.patch b/debian/patches/CVE-2023-0056.patch
435deleted file mode 100644
436index a10f21b..0000000
437--- a/debian/patches/CVE-2023-0056.patch
438+++ /dev/null
439@@ -1,39 +0,0 @@
440-Backport of:
441-
442-From 827a6299e6995c5c3ba620d8b7cbacdaef67f2c4 Mon Sep 17 00:00:00 2001
443-From: Christopher Faulet <cfaulet@haproxy.com>
444-Date: Thu, 22 Dec 2022 09:47:01 +0100
445-Subject: [PATCH] BUG/MEDIUM: mux-h2: Refuse interim responses with end-stream
446- flag set
447-
448-As state in RFC9113#8.1, HEADERS frame with the ES flag set that carries an
449-informational status code is malformed. However, there is no test on this
450-condition.
451-
452-On 2.4 and higher, it is hard to predict consequences of this bug because
453-end of the message is only reported with a flag. But on 2.2 and lower, it
454-leads to a crash because there is an unexpected extra EOM block at the end
455-of an interim response.
456-
457-Now, when a ES flag is detected on a HEADERS frame for an interim message, a
458-stream error is sent (RST_STREAM/PROTOCOL_ERROR).
459-
460-This patch should solve the issue #1972. It should be backported as far as
461-2.0.
462----
463- src/mux_h2.c | 5 +++++
464- 1 file changed, 5 insertions(+)
465-
466---- a/src/mux_h2.c
467-+++ b/src/mux_h2.c
468-@@ -3935,6 +3935,10 @@ next_frame:
469- *flags |= H2_SF_HEADERS_RCVD;
470-
471- if ((h2c->dff & H2_F_HEADERS_END_STREAM)) {
472-+ if (msgf & H2_MSGF_RSP_1XX) {
473-+ /* RFC9113#8.1 : HEADERS frame with the ES flag set that carries an informational status code is malformed */
474-+ goto fail;
475-+ }
476- /* Mark the end of message, either using EOM in HTX or with the
477- * trailing CRLF after the end of trailers. Note that DATA_CHNK
478- * is not set during headers with END_STREAM. For HTX trailers,
479diff --git a/debian/patches/CVE-2023-25725.patch b/debian/patches/CVE-2023-25725.patch
480deleted file mode 100644
481index e57d84b..0000000
482--- a/debian/patches/CVE-2023-25725.patch
483+++ /dev/null
484@@ -1,175 +0,0 @@
485-From 7382253d6a3636fa85510063bdcc9f2e8f8f696d Mon Sep 17 00:00:00 2001
486-From: Willy Tarreau <w@1wt.eu>
487-Date: Thu, 9 Feb 2023 21:36:54 +0100
488-Subject: BUG/CRITICAL: http: properly reject empty http header field names
489-
490-The HTTP header parsers surprizingly accepts empty header field names,
491-and this is a leftover from the original code that was agnostic to this.
492-
493-When muxes were introduced, for H2 first, the HPACK decompressor needed
494-to feed headers lists, and since empty header names were strictly
495-forbidden by the protocol, the lists of headers were purposely designed
496-to be terminated by an empty header field name (a principle that is
497-similar to H1's empty line termination). This principle was preserved
498-and generalized to other protocols migrated to muxes (H1/FCGI/H3 etc)
499-without anyone ever noticing that the H1 parser was still able to deliver
500-empty header field names to this list. In addition to this it turns out
501-that the HPACK decompressor, despite a comment in the code, may
502-successfully decompress an empty header field name, and this mistake
503-was propagated to the QPACK decompressor as well.
504-
505-The impact is that an empty header field name may be used to truncate
506-the list of headers and thus make some headers disappear. While for
507-H2/H3 the impact is limited as haproxy sees a request with missing
508-headers, and headers are not used to delimit messages, in the case of
509-HTTP/1, the impact is significant because the presence (and sometimes
510-contents) of certain sensitive headers is detected during the parsing.
511-Thus, some of these headers may be seen, marked as present, their value
512-extracted, but never delivered to upper layers and obviously not
513-forwarded to the other side either. This can have for consequence that
514-certain important header fields such as Connection, Upgrade, Host,
515-Content-length, Transfer-Encoding etc are possibly seen as different
516-between what haproxy uses to parse/forward/route and what is observed
517-in http-request rules and of course, forwarded. One direct consequence
518-is that it is possible to exploit this property in HTTP/1 to make
519-affected versions of haproxy forward more data than is advertised on
520-the other side, and bypass some access controls or routing rules by
521-crafting extraneous requests. Note, however, that responses to such
522-requests will normally not be passed back to the client, but this can
523-still cause some harm.
524-
525-This specific risk can be mostly worked around in configuration using
526-the following rule that will rely on the bug's impact to precisely
527-detect the inconsistency between the known body size and the one
528-expected to be advertised to the server (the rule works from 2.0 to
529-2.8-dev):
530-
531- http-request deny if { fc_http_major 1 } !{ req.body_size 0 } !{ req.hdr(content-length) -m found } !{ req.hdr(transfer-encoding) -m found } !{ method CONNECT }
532-
533-This will exclusively block such carefully crafted requests delivered
534-over HTTP/1. HTTP/2 and HTTP/3 do not need content-length, and a body
535-that arrives without being announced with a content-length will be
536-forwarded using transfer-encoding, hence will not cause discrepancies.
537-In HAProxy 2.0 in legacy mode ("no option http-use-htx"), this rule will
538-simply have no effect but will not cause trouble either.
539-
540-A clean solution would consist in modifying the loops iterating over
541-these headers lists to check the header name's pointer instead of its
542-length (since both are zero at the end of the list), but this requires
543-to touch tens of places and it's very easy to miss one. Functions such
544-as htx_add_header(), htx_add_trailer(), htx_add_all_headers() would be
545-good starting points for such a possible future change.
546-
547-Instead the current fix focuses on blocking empty headers where they
548-are first inserted, hence in the H1/HPACK/QPACK decoders. One benefit
549-of the current solution (for H1) is that it allows "show errors" to
550-report a precise diagnostic when facing such invalid HTTP/1 requests,
551-with the exact location of the problem and the originating address:
552-
553- $ printf "GET / HTTP/1.1\r\nHost: localhost\r\n:empty header\r\n\r\n" | nc 0 8001
554- HTTP/1.1 400 Bad request
555- Content-length: 90
556- Cache-Control: no-cache
557- Connection: close
558- Content-Type: text/html
559-
560- <html><body><h1>400 Bad request</h1>
561- Your browser sent an invalid request.
562- </body></html>
563-
564- $ socat /var/run/haproxy.stat <<< "show errors"
565- Total events captured on [10/Feb/2023:16:29:37.530] : 1
566-
567- [10/Feb/2023:16:29:34.155] frontend decrypt (#2): invalid request
568- backend <NONE> (#-1), server <NONE> (#-1), event #0, src 127.0.0.1:31092
569- buffer starts at 0 (including 0 out), 16334 free,
570- len 50, wraps at 16336, error at position 33
571- H1 connection flags 0x00000000, H1 stream flags 0x00000810
572- H1 msg state MSG_HDR_NAME(17), H1 msg flags 0x00001410
573- H1 chunk len 0 bytes, H1 body len 0 bytes :
574-
575- 00000 GET / HTTP/1.1\r\n
576- 00016 Host: localhost\r\n
577- 00033 :empty header\r\n
578- 00048 \r\n
579-
580-I want to address sincere and warm thanks for their great work to the
581-team composed of the following security researchers who found the issue
582-together and reported it: Bahruz Jabiyev, Anthony Gavazzi, and Engin
583-Kirda from Northeastern University, Kaan Onarlioglu from Akamai
584-Technologies, Adi Peleg and Harvey Tuch from Google. And kudos to Amaury
585-Denoyelle from HAProxy Technologies for spotting that the HPACK and
586-QPACK decoders would let this pass despite the comment explicitly
587-saying otherwise.
588-
589-This fix must be backported as far as 2.0. The QPACK changes can be
590-dropped before 2.6. In 2.0 there is also the equivalent code for legacy
591-mode, which doesn't suffer from the list truncation, but it would better
592-be fixed regardless.
593-
594-CVE-2023-25725 was assigned to this issue.
595-
596----
597- src/h1.c | 4 ++++
598- src/hpack-dec.c | 9 +++++++++
599- src/http_msg.c | 8 +++++++-
600- 3 files changed, 20 insertions(+), 1 deletion(-)
601-
602-diff --git a/src/h1.c b/src/h1.c
603-index 3839fbe97..69a7cedcb 100644
604---- a/src/h1.c
605-+++ b/src/h1.c
606-@@ -672,6 +672,10 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
607-
608- if (likely(*ptr == ':')) {
609- col = ptr - start;
610-+ if (col <= sol) {
611-+ state = H1_MSG_HDR_NAME;
612-+ goto http_msg_invalid;
613-+ }
614- EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_l1_sp, http_msg_ood, state, H1_MSG_HDR_L1_SP);
615- }
616-
617-diff --git a/src/hpack-dec.c b/src/hpack-dec.c
618-index 7f3e7624b..0fc71ebeb 100644
619---- a/src/hpack-dec.c
620-+++ b/src/hpack-dec.c
621-@@ -425,6 +425,15 @@ int hpack_decode_frame(struct hpack_dht *dht, const uint8_t *raw, uint32_t len,
622- /* <name> and <value> are correctly filled here */
623- }
624-
625-+ /* We must not accept empty header names (forbidden by the spec and used
626-+ * as a list termination).
627-+ */
628-+ if (!name.len) {
629-+ hpack_debug_printf("##ERR@%d##\n", __LINE__);
630-+ ret = -HPACK_ERR_INVALID_ARGUMENT;
631-+ goto leave;
632-+ }
633-+
634- /* here's what we have here :
635- * - name.len > 0
636- * - value is filled with either const data or data allocated from tmp
637-diff --git a/src/http_msg.c b/src/http_msg.c
638-index 067a7f863..e07c81c67 100644
639---- a/src/http_msg.c
640-+++ b/src/http_msg.c
641-@@ -1006,8 +1006,14 @@ void http_msg_analyzer(struct http_msg *msg, struct hdr_idx *idx)
642- if (likely(HTTP_IS_TOKEN(*ptr)))
643- EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_name, http_msg_ood, state, HTTP_MSG_HDR_NAME);
644-
645-- if (likely(*ptr == ':'))
646-+ if (likely(*ptr == ':')) {
647-+ if (ptr == input + msg->sol) {
648-+ /* empty header names are not permitted */
649-+ state = HTTP_MSG_HDR_NAME;
650-+ goto http_msg_invalid;
651-+ }
652- EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_l1_sp, http_msg_ood, state, HTTP_MSG_HDR_L1_SP);
653-+ }
654-
655- if (likely(msg->err_pos < -1) || *ptr == '\n') {
656- state = HTTP_MSG_HDR_NAME;
657---
658-2.35.3
659-
660diff --git a/debian/patches/series b/debian/patches/series
661index f7b2e27..0945bb0 100644
662--- a/debian/patches/series
663+++ b/debian/patches/series
664@@ -1,5 +1,3 @@
665 0002-Use-dpkg-buildflags-to-build-halog.patch
666 haproxy.service-start-after-syslog.patch
667 haproxy.service-add-documentation.patch
668-CVE-2023-0056.patch
669-CVE-2023-25725.patch
670diff --git a/debian/tests/control b/debian/tests/control
671index 70649e3..ccc2b53 100644
672--- a/debian/tests/control
673+++ b/debian/tests/control
674@@ -5,3 +5,11 @@ Restrictions: needs-root
675 Tests: proxy-localhost
676 Depends: haproxy, wget, apache2
677 Restrictions: needs-root, allow-stderr, isolation-container
678+
679+Tests: proxy-ssl-termination
680+Depends: haproxy, wget, apache2, gnutls-bin, ssl-cert
681+Restrictions: needs-root, allow-stderr, isolation-container
682+
683+Tests: proxy-ssl-pass-through
684+Depends: haproxy, wget, apache2, gnutls-bin, ssl-cert
685+Restrictions: needs-root, allow-stderr, isolation-container
686diff --git a/debian/tests/proxy-localhost b/debian/tests/proxy-localhost
687index b1279e8..0736985 100644
688--- a/debian/tests/proxy-localhost
689+++ b/debian/tests/proxy-localhost
690@@ -2,6 +2,9 @@
691
692 set -eux
693
694+WDIR=$(dirname "$0")
695+. "${WDIR}/utils"
696+
697 cat > /etc/haproxy/haproxy.cfg <<EOF
698 global
699 chroot /var/lib/haproxy
700@@ -34,15 +37,8 @@ backend test-back
701 EOF
702
703 service haproxy restart
704+sleep 2 # Apache 2 could be still starting... See #976997.
705
706-# index.html is shipped with apache2
707-# Download it via haproxy and compare
708-if wget -t1 http://localhost:8080 -O- | cmp /var/www/html/index.html -; then
709- echo "OK: index.html downloaded via haproxy matches the source file."
710-else
711- echo "FAIL: downloaded index.html via haproxy is different from the"
712- echo " file delivered by apache."
713- exit 1
714-fi
715+check_index_file "http://localhost:8080"
716
717 exit 0
718diff --git a/debian/tests/proxy-ssl-pass-through b/debian/tests/proxy-ssl-pass-through
719new file mode 100644
720index 0000000..aa0bd2c
721--- /dev/null
722+++ b/debian/tests/proxy-ssl-pass-through
723@@ -0,0 +1,59 @@
724+#!/bin/sh
725+
726+set -eux
727+
728+WDIR=$(dirname "$0")
729+. "${WDIR}/utils"
730+
731+CERT_DIR=/etc/ssl/localhost
732+APACHE2_CONFIG=/etc/apache2/sites-available/default-ssl.conf
733+
734+create_ca
735+create_selfsigned_cert ${CERT_DIR}
736+
737+# Use the self-signed certificate in apache2 config
738+sed -i "s#/etc/ssl/certs/ssl-cert-snakeoil.pem#${CERT_DIR}/localhost_cert.pem#" ${APACHE2_CONFIG}
739+sed -i "s#/etc/ssl/private/ssl-cert-snakeoil.key#${CERT_DIR}/localhost_key.pem#" ${APACHE2_CONFIG}
740+
741+cat > /etc/haproxy/haproxy.cfg <<EOF
742+global
743+ chroot /var/lib/haproxy
744+ user haproxy
745+ group haproxy
746+ daemon
747+ maxconn 4096
748+
749+defaults
750+ log global
751+ option dontlognull
752+ option redispatch
753+ retries 3
754+ timeout client 50s
755+ timeout connect 10s
756+ timeout http-request 5s
757+ timeout server 50s
758+ maxconn 4096
759+
760+frontend test-front
761+ bind *:4433
762+ mode tcp
763+ option tcplog
764+ default_backend test-back
765+
766+backend test-back
767+ mode tcp
768+ stick store-request src
769+ stick-table type ip size 256k expire 30m
770+ option ssl-hello-chk
771+ server test-1 localhost:443 check
772+EOF
773+
774+systemctl restart haproxy
775+a2enmod ssl
776+a2ensite default-ssl
777+systemctl restart apache2
778+sleep 5 # Apache 2 could be still starting... See #976997. It needs some extra seconds because of SSL
779+
780+check_index_file "https://localhost:4433"
781+
782+exit 0
783diff --git a/debian/tests/proxy-ssl-termination b/debian/tests/proxy-ssl-termination
784new file mode 100644
785index 0000000..6cc1bcc
786--- /dev/null
787+++ b/debian/tests/proxy-ssl-termination
788@@ -0,0 +1,48 @@
789+#!/bin/sh
790+
791+set -eux
792+
793+WDIR=$(dirname "$0")
794+. "${WDIR}/utils"
795+
796+CERT_DIR=/etc/ssl/localhost
797+create_ca
798+create_selfsigned_cert ${CERT_DIR}
799+
800+cat > /etc/haproxy/haproxy.cfg <<EOF
801+global
802+ chroot /var/lib/haproxy
803+ user haproxy
804+ group haproxy
805+ daemon
806+ maxconn 4096
807+ ssl-default-bind-options ssl-min-ver SSLv3
808+
809+defaults
810+ log global
811+ option dontlognull
812+ option redispatch
813+ retries 3
814+ timeout client 50s
815+ timeout connect 10s
816+ timeout http-request 5s
817+ timeout server 50s
818+ maxconn 4096
819+
820+frontend test-front
821+ bind *:443 ssl crt ${CERT_DIR}/localhost.pem
822+ default_backend test-back
823+
824+backend test-back
825+ mode http
826+ stick store-request src
827+ stick-table type ip size 256k expire 30m
828+ server test-1 localhost:80 check
829+EOF
830+
831+systemctl restart haproxy
832+sleep 2 # Apache 2 could be still starting... See #976997.
833+
834+check_index_file "https://localhost"
835+
836+exit 0
837diff --git a/debian/tests/utils b/debian/tests/utils
838new file mode 100644
839index 0000000..df11b55
840--- /dev/null
841+++ b/debian/tests/utils
842@@ -0,0 +1,58 @@
843+
844+create_ca() {
845+ certtool --generate-privkey --bits 4096 --outfile /etc/ssl/private/mycakey.pem
846+
847+ cat <<EOF > /etc/ssl/ca.info
848+cn = Example Company
849+ca
850+cert_signing_key
851+expiration_days = 3650
852+EOF
853+
854+ certtool --generate-self-signed \
855+ --load-privkey /etc/ssl/private/mycakey.pem \
856+ --template /etc/ssl/ca.info \
857+ --outfile /usr/local/share/ca-certificates/mycacert.crt
858+
859+ update-ca-certificates
860+}
861+
862+create_selfsigned_cert() {
863+ dir="$1"
864+ mkdir -p "${dir}"
865+
866+ certtool --generate-privkey --bits 2048 --outfile "${dir}/localhost_key.pem"
867+
868+ cat <<EOF > "${dir}/localhost.info"
869+organization = Example Company
870+cn = localhost
871+tls_www_server
872+encryption_key
873+signing_key
874+expiration_days = 365
875+EOF
876+
877+ certtool --generate-certificate \
878+ --load-privkey "${dir}/localhost_key.pem" \
879+ --load-ca-certificate /etc/ssl/certs/mycacert.pem \
880+ --load-ca-privkey /etc/ssl/private/mycakey.pem \
881+ --template "${dir}/localhost.info" \
882+ --outfile "${dir}/localhost_cert.pem"
883+
884+ cat "${dir}/localhost_cert.pem" "${dir}/localhost_key.pem" | tee "${dir}/localhost.pem"
885+ chgrp haproxy "${dir}/localhost_key.pem" "${dir}/localhost.pem"
886+ chmod 0640 "${dir}/localhost_key.pem" "${dir}/localhost.pem"
887+}
888+
889+check_index_file() {
890+ haproxy_url="$1"
891+ # index.html is shipped with apache2
892+ # Download it via haproxy and compare
893+ if wget -t1 "${haproxy_url}" -O- | cmp /var/www/html/index.html -; then
894+ echo "OK: index.html downloaded via haproxy matches the source file."
895+ else
896+ echo "FAIL: downloaded index.html via haproxy is different from the"
897+ echo " file delivered by apache."
898+ exit 1
899+ fi
900+}
901diff --git a/doc/configuration.txt b/doc/configuration.txt
902index 4394d07..4c7c59e 100644
903--- a/doc/configuration.txt
904+++ b/doc/configuration.txt
905@@ -3,7 +3,7 @@
906 Configuration Manual
907 ----------------------
908 version 2.0
909- 2022/05/13
910+ 2023/02/14
911
912
913 This document covers the configuration language as implemented in the version
914@@ -2106,9 +2106,10 @@ default-server [param*]
915 Arguments:
916 <param*> is a list of parameters for this server. The "default-server"
917 keyword accepts an important number of options and has a complete
918- section dedicated to it. Please refer to section 5 for more
919- details.
920-
921+ section dedicated to it. In a peers section, the transport
922+ parameters of a "default-server" line are supported. Please refer
923+ to section 5 for more details, and the "server" keyword below in
924+ this section for some of the restrictions.
925
926 See also: "server" and section 5 about server options
927
928@@ -2140,12 +2141,16 @@ peer <peername> <ip>:<port> [param*]
929
930 server <peername> [<ip>:<port>] [param*]
931 As previously mentioned, "peer" keyword may be replaced by "server" keyword
932- with a support for all "server" parameters found in 5.2 paragraph.
933- If the underlying peer is local, <ip>:<port> parameters must not be present.
934- These parameters must be provided on a "bind" line (see "bind" keyword
935- of this "peers" section).
936- Some of these parameters are irrelevant for "peers" sections.
937+ with a support for all "server" parameters found in 5.2 paragraph that are
938+ related to transport settings. If the underlying peer is local, <ip>:<port>
939+ parameters must not be present; these parameters must be provided on a "bind"
940+ line (see "bind" keyword of this "peers" section).
941
942+ A number of "server" parameters are irrelevant for "peers" sections. Peers by
943+ nature do not support dynamic host name resolution nor health checks, hence
944+ parameters like "init_addr", "resolvers", "check", "agent-check", or "track"
945+ are not supported. Similarly, there is no load balancing nor stickiness, thus
946+ parameters such as "weight" or "cookie" have no effect.
947
948 Example:
949 # The old way.
950@@ -2165,10 +2170,11 @@ server <peername> [<ip>:<port>] [param*]
951
952 Example:
953 peers mypeers
954- bind 127.0.0.11:10001 ssl crt mycerts/pem
955- default-server ssl verify none
956- server hostA 127.0.0.10:10000
957- server hostB #local peer
958+ bind 192.168.0.1:1024 ssl crt mycerts/pem
959+ default-server ssl verify none
960+ server haproxy1 #local peer
961+ server haproxy2 192.168.0.2:1024
962+ server haproxy3 10.2.0.1:1024
963
964
965 table <tablename> type {ip | integer | string [len <length>] | binary [len <length>]}
966@@ -2433,7 +2439,7 @@ http-check send-state X - X X
967 http-request - X X X
968 http-response - X X X
969 http-reuse X - X X
970-http-send-name-header - - X X
971+http-send-name-header X - X X
972 id - X X X
973 ignore-persist - - X X
974 load-server-state-from-file X - X X
975@@ -2493,7 +2499,7 @@ option socket-stats (*) X X X -
976 option splice-auto (*) X X X X
977 option splice-request (*) X X X X
978 option splice-response (*) X X X X
979-option spop-check - - - X
980+option spop-check X - X X
981 option srvtcpka (*) X - X X
982 option ssl-hello-chk X - X X
983 -- keyword -------------------------- defaults - frontend - listen -- backend -
984@@ -4466,7 +4472,8 @@ http-request do-resolve(<var>,<resolvers>,[ipv4,ipv6]) <expr> :
985 based on information found in the request (IE a Host header).
986 If this action is used to find the server's IP address (using the
987 "set-dst" action), then the server IP address in the backend must be set
988- to 0.0.0.0.
989+ to 0.0.0.0. The do-resolve action takes an host-only parameter, any port must
990+ be removed from the string.
991
992 Example:
993 resolvers mydns
994@@ -4481,7 +4488,7 @@ http-request do-resolve(<var>,<resolvers>,[ipv4,ipv6]) <expr> :
995
996 frontend fe
997 bind 10.42.0.1:80
998- http-request do-resolve(txn.myip,mydns,ipv4) hdr(Host),lower
999+ http-request do-resolve(txn.myip,mydns,ipv4) hdr(Host),lower,regsub(:[0-9]*$,)
1000 http-request capture var(txn.myip) len 40
1001
1002 # return 503 when the variable is not set,
1003@@ -5420,7 +5427,26 @@ http-reuse { never | safe | aggressive | always }
1004 because almost no new connection will be established while idle connections
1005 remain available. This is particularly true with the "always" strategy.
1006
1007- See also : "option http-keep-alive", "server maxconn"
1008+ The rules to decide to keep an idle connection opened or to close it after
1009+ processing are also governed by the "tune.pool-low-fd-ratio" (default: 20%)
1010+ and "tune.pool-high-fd-ratio" (default: 25%). These correspond to the
1011+ percentage of total file descriptors spent in idle connections above which
1012+ haproxy will respectively refrain from keeping a connection opened after a
1013+ response, and actively kill idle connections. Some setups using a very high
1014+ ratio of idle connections, either because of too low a global "maxconn", or
1015+ due to a lot of HTTP/2 or HTTP/3 traffic on the frontend (few connections)
1016+ but HTTP/1 connections on the backend, may observe a lower reuse rate because
1017+ too few connections are kept open. It may be desirable in this case to adjust
1018+ such thresholds or simply to increase the global "maxconn" value.
1019+
1020+ Similarly, when thread groups are explicitly enabled, it is important to
1021+ understand that idle connections are only usable between threads from a same
1022+ group. As such it may happen that unfair load between groups leads to more
1023+ idle connections being needed, causing a lower reuse rate. The same solution
1024+ may then be applied (increase global "maxconn" or increase pool ratios).
1025+
1026+ See also : "option http-keep-alive", "server maxconn", "thread-groups",
1027+ "tune.pool-high-fd-ratio", "tune.pool-low-fd-ratio"
1028
1029
1030 http-send-name-header [<header>]
1031@@ -7490,7 +7516,7 @@ no option splice-response
1032 option spop-check
1033 Use SPOP health checks for server testing
1034 May be used in sections : defaults | frontend | listen | backend
1035- no | no | no | yes
1036+ yes | no | yes | yes
1037 Arguments : none
1038
1039 It is possible to test that the server correctly talks SPOP protocol instead
1040@@ -8333,24 +8359,26 @@ reqitarpit <search> [{if | unless} <cond>] (ignore case) (deprecated)
1041
1042
1043 retries <value>
1044- Set the number of retries to perform on a server after a connection failure
1045+ Set the number of retries to perform on a server after a failure
1046 May be used in sections: defaults | frontend | listen | backend
1047 yes | no | yes | yes
1048 Arguments :
1049- <value> is the number of times a connection attempt should be retried on
1050- a server when a connection either is refused or times out. The
1051- default value is 3.
1052+ <value> is the number of times a request or connection attempt should be
1053+ retried on a server after a failure.
1054
1055- It is important to understand that this value applies to the number of
1056- connection attempts, not full requests. When a connection has effectively
1057- been established to a server, there will be no more retry.
1058+ By default, retries apply only to new connection attempts. However, when
1059+ the "retry-on" directive is used, other conditions might trigger a retry
1060+ (e.g. empty response, undesired status code), and each of them will count
1061+ one attempt, and when the total number attempts reaches the value here, an
1062+ error will be returned.
1063
1064 In order to avoid immediate reconnections to a server which is restarting,
1065 a turn-around timer of min("timeout connect", one second) is applied before
1066- a retry occurs.
1067+ a retry occurs on the same server.
1068
1069- When "option redispatch" is set, the last retry may be performed on another
1070- server even if a cookie references a different server.
1071+ When "option redispatch" is set, some retries may be performed on another
1072+ server even if a cookie references a different server. By default this will
1073+ only be the last retry unless an argument is passed to "option redispatch".
1074
1075 See also : "option redispatch"
1076
1077@@ -9607,13 +9635,16 @@ stick-table type {ip | integer | string [len <length>] | binary [len <length>]}
1078 belonging to the same unique process.
1079
1080 <expire> defines the maximum duration of an entry in the table since it
1081- was last created, refreshed or matched. The expiration delay is
1082+ was last created, refreshed using 'track-sc' or matched using
1083+ 'stick match' or 'stick on' rule. The expiration delay is
1084 defined using the standard time format, similarly as the various
1085 timeouts. The maximum duration is slightly above 24 days. See
1086 section 2.4 for more information. If this delay is not specified,
1087 the session won't automatically expire, but older entries will
1088 be removed once full. Be sure not to use the "nopurge" parameter
1089 if not expiration delay is specified.
1090+ Note: 'table_*' converters performs lookups but won't update touch
1091+ expire since they don't require 'track-sc'.
1092
1093 <data_type> is used to store additional information in the stick-table. This
1094 may be used by ACLs in order to control various criteria related
1095@@ -10448,7 +10479,7 @@ tcp-request content <action> [{if | unless} <condition>]
1096 evaluated.
1097
1098 Example:
1099- tcp-request content use-service lua.deny { src -f /etc/haproxy/blacklist.lst }
1100+ tcp-request content use-service lua.deny if { src -f /etc/haproxy/blacklist.lst }
1101
1102 Example:
1103
1104@@ -12880,8 +12911,10 @@ sni <expression>
1105 The "sni" parameter evaluates the sample fetch expression, converts it to a
1106 string and uses the result as the host name sent in the SNI TLS extension to
1107 the server. A typical use case is to send the SNI received from the client in
1108- a bridged HTTPS scenario, using the "ssl_fc_sni" sample fetch for the
1109- expression, though alternatives such as req.hdr(host) can also make sense. If
1110+ a bridged TCP/SSL scenario, using the "ssl_fc_sni" sample fetch for the
1111+ expression. THIS MUST NOT BE USED FOR HTTPS, where req.hdr(host) should be
1112+ used instead, since SNI in HTTPS must always match the Host field and clients
1113+ are allowed to use different host names over the same connection). If
1114 "verify required" is set (which is the recommended setting), the resulting
1115 name will also be matched against the server certificate's names. See the
1116 "verify" directive for more details. If you want to set a SNI for health
1117@@ -13520,7 +13553,11 @@ possible to convert the sample to lowercase before matching, like this :
1118 All ACL-specific criteria imply a default matching method. Most often, these
1119 criteria are composed by concatenating the name of the original sample fetch
1120 method and the matching method. For example, "hdr_beg" applies the "beg" match
1121-to samples retrieved using the "hdr" fetch method. Since all ACL-specific
1122+to samples retrieved using the "hdr" fetch method. This matching method is only
1123+usable when the keyword is used alone, without any converter. In case any such
1124+converter were to be applied after such an ACL keyword, the default matching
1125+method from the ACL keyword is simply ignored since what will matter for the
1126+matching is the output type of the last converter. Since all ACL-specific
1127 criteria rely on a sample fetch method, it is always possible instead to use
1128 the original sample fetch method and the explicit matching method using "-m".
1129
1130@@ -13646,13 +13683,24 @@ different forms :
1131 - suffix match (-m end) : the patterns are compared with the end of the
1132 extracted string, and the ACL matches if any of them matches.
1133
1134- - subdir match (-m dir) : the patterns are looked up inside the extracted
1135- string, delimited with slashes ("/"), and the ACL matches if any of them
1136- matches.
1137-
1138- - domain match (-m dom) : the patterns are looked up inside the extracted
1139- string, delimited with dots ("."), and the ACL matches if any of them
1140- matches.
1141+ - subdir match (-m dir) : the patterns are looked up anywhere inside the
1142+ extracted string, delimited with slashes ("/"), the beginning or the end
1143+ of the string. The ACL matches if any of them matches. As such, the string
1144+ "/images/png/logo/32x32.png", would match "/images", "/images/png",
1145+ "images/png", "/png/logo", "logo/32x32.png" or "32x32.png" but not "png"
1146+ nor "32x32".
1147+
1148+ - domain match (-m dom) : the patterns are looked up anywhere inside the
1149+ extracted string, delimited with dots ("."), colons (":"), slashes ("/"),
1150+ question marks ("?"), the beginning or the end of the string. This is made
1151+ to be used with URLs. Leading and trailing delimiters in the pattern are
1152+ ignored. The ACL matches if any of them matches. As such, in the example
1153+ string "http://www1.dc-eu.example.com:80/blah", the patterns "http",
1154+ "www1", ".www1", "dc-eu", "example", "com", "80", "dc-eu.example",
1155+ "blah", ":www1:", "dc-eu.example:80" would match, but not "eu" nor "dc".
1156+ Using it to match domain suffixes for filtering or routing is generally
1157+ not a good idea, as the routing could easily be fooled by prepending the
1158+ matching prefix in front of another domain for example.
1159
1160 String matching applies to verbatim strings as they are passed, with the
1161 exception of the backslash ("\") which makes it possible to escape some
1162@@ -16168,6 +16216,16 @@ ssl_fc_sni : string
1163 requires that the SSL library is built with support for TLS extensions
1164 enabled (check haproxy -vv).
1165
1166+ CAUTION! Except under very specific conditions, it is normally not correct to
1167+ use this field as a substitute for the HTTP "Host" header field. For example,
1168+ when forwarding an HTTPS connection to a server, the SNI field must be set
1169+ from the HTTP Host header field using "req.hdr(host)" and not from the front
1170+ SNI value. The reason is that SNI is solely used to select the certificate
1171+ the server side will present, and that clients are then allowed to send
1172+ requests with different Host values as long as they match the names in the
1173+ certificate. As such, "ssl_fc_sni" should normally not be used as an argument
1174+ to the "sni" server keyword, unless the backend works in TCP mode.
1175+
1176 ACL derivatives :
1177 ssl_fc_sni_end : suffix match
1178 ssl_fc_sni_reg : regex match
1179diff --git a/doc/management.txt b/doc/management.txt
1180index 337cc14..f90110b 100644
1181--- a/doc/management.txt
1182+++ b/doc/management.txt
1183@@ -2458,6 +2458,10 @@ show stat [{<iid>|<proxy>} <type> <sid>] [typed|json]
1184 $ echo "show stat json" | socat /var/run/haproxy.sock stdio | \
1185 python -m json.tool
1186
1187+show startup-logs
1188+ Dump all messages emitted during the startup of the current haproxy process,
1189+ each startup-logs buffer is unique to its haproxy worker.
1190+
1191 show table
1192 Dump general information on all known stick-tables. Their name is returned
1193 (the name of the proxy which holds them), their type (currently zero, always
1194diff --git a/doc/proxy-protocol.txt b/doc/proxy-protocol.txt
1195index ff64c8b..45bafe3 100644
1196--- a/doc/proxy-protocol.txt
1197+++ b/doc/proxy-protocol.txt
1198@@ -500,7 +500,7 @@ protocol. Identifying the protocol version is easy :
1199 - if the incoming byte count is 16 or above and the 13 first bytes match
1200 the protocol signature block followed by the protocol version 2 :
1201
1202- \x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x02
1203+ \x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x20
1204
1205 - otherwise, if the incoming byte count is 8 or above, and the 5 first
1206 characters match the US-ASCII representation of "PROXY" then the protocol
1207diff --git a/include/common/buf.h b/include/common/buf.h
1208index 1ee6420..3770b9a 100644
1209--- a/include/common/buf.h
1210+++ b/include/common/buf.h
1211@@ -469,7 +469,7 @@ static inline void b_slow_realign(struct buffer *b, char *swap, size_t output)
1212
1213 /* process output data in two steps to cover wrapping */
1214 if (block1 > b_size(b) - b_head_ofs(b)) {
1215- block2 = b_size(b) - b_head_ofs(b);
1216+ block2 = b_peek_ofs(b, block1);
1217 block1 -= block2;
1218 }
1219 memcpy(swap + b_size(b) - output, b_head(b), block1);
1220diff --git a/include/common/compiler.h b/include/common/compiler.h
1221index c023c9d..4b76cec 100644
1222--- a/include/common/compiler.h
1223+++ b/include/common/compiler.h
1224@@ -77,10 +77,12 @@
1225 #endif
1226 #endif
1227
1228+#ifndef __maybe_unused
1229 /* silence the "unused" warnings without having to place painful #ifdefs.
1230 * For use with variables or functions.
1231 */
1232 #define __maybe_unused __attribute__((unused))
1233+#endif
1234
1235 /* This allows gcc to know that some locations are never reached, for example
1236 * after a longjmp() in the Lua code, hence that some errors caught by such
1237@@ -91,7 +93,7 @@
1238 #if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
1239 #define my_unreachable() __builtin_unreachable()
1240 #else
1241-#define my_unreachable()
1242+#define my_unreachable() do { } while (1)
1243 #endif
1244
1245 /* This macro may be used to block constant propagation that lets the compiler
1246diff --git a/include/common/memory.h b/include/common/memory.h
1247index 6c0b137..ee3b7f0 100644
1248--- a/include/common/memory.h
1249+++ b/include/common/memory.h
1250@@ -131,8 +131,8 @@ void create_pool_callback(struct pool_head **ptr, char *name, unsigned int size)
1251 void dump_pools_to_trash();
1252 void dump_pools(void);
1253 int pool_total_failures();
1254-unsigned long pool_total_allocated();
1255-unsigned long pool_total_used();
1256+unsigned long long pool_total_allocated();
1257+unsigned long long pool_total_used();
1258
1259 /*
1260 * This function frees whatever can be freed in pool <pool>.
1261diff --git a/include/common/standard.h b/include/common/standard.h
1262index 38e2bb7..49b72c8 100644
1263--- a/include/common/standard.h
1264+++ b/include/common/standard.h
1265@@ -498,7 +498,8 @@ char *encode_chunk(char *start, char *stop,
1266
1267 /*
1268 * Tries to prefix characters tagged in the <map> with the <escape>
1269- * character. The input <string> must be zero-terminated. The result will
1270+ * character. The input <string> is processed until string_stop
1271+ * is reached or NULL-byte is encountered. The result will
1272 * be stored between <start> (included) and <stop> (excluded). This
1273 * function will always try to terminate the resulting string with a '\0'
1274 * before <stop>, and will return its position if the conversion
1275@@ -506,7 +507,7 @@ char *encode_chunk(char *start, char *stop,
1276 */
1277 char *escape_string(char *start, char *stop,
1278 const char escape, const long *map,
1279- const char *string);
1280+ const char *string, const char *string_stop);
1281
1282 /*
1283 * Tries to prefix characters tagged in the <map> with the <escape>
1284diff --git a/include/proto/server.h b/include/proto/server.h
1285index 8934075..7f8c725 100644
1286--- a/include/proto/server.h
1287+++ b/include/proto/server.h
1288@@ -46,6 +46,7 @@ extern struct list toremove_connections[MAX_THREADS];
1289 int srv_downtime(const struct server *s);
1290 int srv_lastsession(const struct server *s);
1291 int srv_getinter(const struct check *check);
1292+void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl);
1293 int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy, int parse_addr, int in_peers_section, int initial_resolve);
1294 int update_server_addr(struct server *s, void *ip, int ip_sin_family, const char *updater);
1295 const char *update_server_addr_port(struct server *s, const char *addr, const char *port, char *updater);
1296diff --git a/include/proto/ssl_sock.h b/include/proto/ssl_sock.h
1297index d6d01a7..f23cbdc 100644
1298--- a/include/proto/ssl_sock.h
1299+++ b/include/proto/ssl_sock.h
1300@@ -102,6 +102,31 @@ void ssl_async_fd_free(int fd);
1301
1302 #define sh_ssl_sess_tree_lookup(k) (struct sh_ssl_sess_hdr *)ebmb_lookup(sh_ssl_sess_tree, \
1303 (k), SSL_MAX_SSL_SESSION_ID_LENGTH);
1304+
1305+static inline int cert_ignerr_bitfield_get(const unsigned long long *bitfield, int bit_index)
1306+{
1307+ int byte_index = bit_index >> 6;
1308+ int val = 0;
1309+
1310+ if (byte_index < IGNERR_BF_SIZE)
1311+ val = bitfield[byte_index] & (1ULL << (bit_index & 0x3F));
1312+
1313+ return val != 0;
1314+}
1315+
1316+static inline void cert_ignerr_bitfield_set(unsigned long long *bitfield, int bit_index)
1317+{
1318+ int byte_index = bit_index >> 6;
1319+
1320+ if (byte_index < IGNERR_BF_SIZE)
1321+ bitfield[byte_index] |= (1ULL << (bit_index & 0x3F));
1322+}
1323+
1324+static inline void cert_ignerr_bitfield_set_all(unsigned long long *bitfield)
1325+{
1326+ memset(bitfield, -1, IGNERR_BF_SIZE*sizeof(*bitfield));
1327+}
1328+
1329 #endif /* USE_OPENSSL */
1330 #endif /* _PROTO_SSL_SOCK_H */
1331
1332diff --git a/include/types/global.h b/include/types/global.h
1333index eab3345..a505009 100644
1334--- a/include/types/global.h
1335+++ b/include/types/global.h
1336@@ -240,6 +240,7 @@ extern char hostname[MAX_HOSTNAME_LEN];
1337 extern char localpeer[MAX_HOSTNAME_LEN];
1338 extern struct list global_listener_queue; /* list of the temporarily limited listeners */
1339 extern struct task *global_listener_queue_task;
1340+__decl_hathreads(extern HA_RWLOCK_T global_listener_rwlock);
1341 extern unsigned int warned; /* bitfield of a few warnings to emit just once */
1342 extern volatile unsigned long sleeping_thread_mask;
1343 extern struct list proc_list; /* list of process in mworker mode */
1344diff --git a/include/types/listener.h b/include/types/listener.h
1345index def48b0..10464fa 100644
1346--- a/include/types/listener.h
1347+++ b/include/types/listener.h
1348@@ -139,12 +139,21 @@ struct ssl_bind_conf {
1349 #endif
1350 };
1351
1352+/*
1353+ * In OpenSSL 3.0.0, the biggest verify error code's value is 94 and on the
1354+ * latest 1.1.1 it already reaches 79 so we need to size the ca/crt-ignore-err
1355+ * arrays accordingly. If the max error code increases, the arrays might need to
1356+ * be resized.
1357+ */
1358+#define SSL_MAX_VFY_ERROR_CODE 94
1359+#define IGNERR_BF_SIZE ((SSL_MAX_VFY_ERROR_CODE >> 6) + 1)
1360+
1361 /* "bind" line settings */
1362 struct bind_conf {
1363 #ifdef USE_OPENSSL
1364 struct ssl_bind_conf ssl_conf; /* ssl conf for ctx setting */
1365- unsigned long long ca_ignerr; /* ignored verify errors in handshake if depth > 0 */
1366- unsigned long long crt_ignerr; /* ignored verify errors in handshake if depth == 0 */
1367+ unsigned long long ca_ignerr_bitfield[IGNERR_BF_SIZE]; /* ignored verify errors in handshake if depth > 0 */
1368+ unsigned long long crt_ignerr_bitfield[IGNERR_BF_SIZE]; /* ignored verify errors in handshake if depth == 0 */
1369 SSL_CTX *initial_ctx; /* SSL context for initial negotiation */
1370 SSL_CTX *default_ctx; /* SSL context of first/default certificate */
1371 struct ssl_bind_conf *default_ssl_conf; /* custom SSL conf of default_ctx */
1372diff --git a/reg-tests/http-messaging/http_abortonclose.vtc b/reg-tests/http-messaging/http_abortonclose.vtc
1373index 733242b..b6066ba 100644
1374--- a/reg-tests/http-messaging/http_abortonclose.vtc
1375+++ b/reg-tests/http-messaging/http_abortonclose.vtc
1376@@ -7,10 +7,12 @@ feature ignore_unknown_macro
1377 #REQUIRE_VERSION=2.0
1378 #REGTEST_TYPE=slow
1379
1380+# b0 : Wait s1 was detected as DOWN to be sure it is stopped
1381 # b1 : Don't send /c4 before /c3 was received by s2 server
1382 # b2 : Don't finish c2 before c1 and c3 before c4 (from syslog POV)
1383 # b3 : finish c3 before s2
1384
1385+barrier b0 cond 2 -cyclic
1386 barrier b1 cond 2 -cyclic
1387 barrier b2 cond 2 -cyclic
1388 barrier b3 cond 2 -cyclic
1389@@ -32,12 +34,16 @@ server s2 {
1390 } -start
1391
1392 syslog S -level info {
1393+ recv alert
1394+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: Server check/srv1 is DOWN.*"
1395+ barrier b0 sync
1396+
1397 recv
1398- expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 be1/srv1 [0-9]*/[0-9]*/-1/-1/[0-9]* 503 .* - - SC-- .* .* \"GET /c1 HTTP/1\\.1\""
1399+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 be1_1/srv1 [0-9]*/[0-9]*/-1/-1/[0-9]* 503 .* - - SC-- .* .* \"GET /c1 HTTP/1\\.1\""
1400 barrier b2 sync
1401 recv
1402- expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 be1/srv1 [0-9]*/[0-9]*/-1/-1/[0-9]* 503 .* - - CC-- .* .* \"GET /c2 HTTP/1\\.1\""
1403-
1404+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 be1_2/srv1 [0-9]*/[0-9]*/-1/-1/[0-9]* 503 .* - - CC-- .* .* \"GET /c2 HTTP/1\\.1\""
1405+ barrier b2 sync
1406 recv
1407 expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe2 be2/<NOSRV> [0-9]*/[0-9]*/-1/-1/[0-9]* 503 .* - - CQ-- .* .* \"GET /c4 HTTP/1\\.1\""
1408 barrier b2 sync
1409@@ -60,7 +66,8 @@ haproxy h1 -conf {
1410 option httplog
1411 log ${S_addr}:${S_port} local0 debug err
1412 bind "fd@${fe1}"
1413- use_backend be1
1414+ use_backend be1_1 if { path /c1 }
1415+ use_backend be1_2 if { path /c2 }
1416
1417 frontend fe2
1418 option httplog
1419@@ -68,13 +75,25 @@ haproxy h1 -conf {
1420 bind "fd@${fe2}"
1421 use_backend be2
1422
1423- backend be1
1424+ backend be1_1
1425+ server srv1 ${s1_addr}:${s1_port}
1426+
1427+ backend be1_2
1428+ timeout connect 1s
1429+ retries 10
1430 server srv1 ${s1_addr}:${s1_port}
1431
1432 backend be2
1433 server srv1 ${s2_addr}:${s2_port} maxconn 1
1434+
1435+ backend check
1436+ server srv1 ${s1_addr}:${s1_port} check
1437+ log ${S_addr}:${S_port} local0 debug alert
1438 } -start
1439
1440+# Wait s1 was detected as DOWN
1441+barrier b0 sync
1442+
1443 # No server, wait all connection retries : SC--
1444 client c1 -connect ${h1_fe1_sock} {
1445 txreq -url /c1
1446@@ -90,6 +109,9 @@ client c2 -connect ${h1_fe1_sock} {
1447 txreq -url /c2
1448 } -run
1449
1450+# Wait c2 log entry
1451+barrier b2 sync
1452+
1453 # server with maxconn=1, abort waiting the server reply : CH--
1454 client c3 -connect ${h1_fe2_sock} {
1455 txreq -url /c3
1456diff --git a/reg-tests/http-messaging/http_request_buffer.vtc b/reg-tests/http-messaging/http_request_buffer.vtc
1457index 4fd7bb2..3c47599 100644
1458--- a/reg-tests/http-messaging/http_request_buffer.vtc
1459+++ b/reg-tests/http-messaging/http_request_buffer.vtc
1460@@ -9,6 +9,8 @@ feature ignore_unknown_macro
1461 # thanks to "http-buffer-request". If this was the case, c2 client
1462 # could not connect to s1 server and this would lead to make this test fail.
1463
1464+barrier b1 cond 2 -cyclic
1465+
1466 server s1 {
1467 rxreq
1468 expect req.bodylen == 257
1469@@ -24,12 +26,18 @@ server s1 {
1470 syslog S -level info {
1471 recv
1472 expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 fe1/<NOSRV> .* 408 .* - - cD-- .* .* \"GET /this-is-a-long-url-this-is-a-long-url-this-is-a-long-url-this-is-a-long-url-this-is-a-long-url-this-is-a-long-url-this-is-a-long-url HTTP/1\\.1\""
1473+ barrier b1 sync
1474+
1475 recv
1476 expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 be1/srv1 [0-9]*/[0-9]*/[0-9]*/[0-9]*/[0-9]* 200 .* - - ---- .* .* \"GET / HTTP/1\\.1\""
1477+ barrier b1 sync
1478+
1479 recv
1480- expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 be1/srv1 [0-9]*/[0-9]*/[0-9]*/[0-9]*/[0-9]* 200 .* - - ---- .* .* \"POST /1 HTTP/1\\.1\""
1481+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe2 be1/srv1 [0-9]*/[0-9]*/[0-9]*/[0-9]*/[0-9]* 200 .* - - ---- .* .* \"POST /1 HTTP/1\\.1\""
1482+ barrier b1 sync
1483+
1484 recv
1485- expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe1 be1/<NOSRV> [0-9]*/-1/-1/-1/[0-9]* -1 .* - - CR-- .* .* \"POST /2 HTTP/1\\.1\""
1486+ expect ~ "[^:\\[ ]*\\[[0-9]*\\]: .* .* fe2 be1/<NOSRV> [0-9]*/-1/-1/-1/[0-9]* -1 .* - - CR-- .* .* \"POST /2 HTTP/1\\.1\""
1487 } -start
1488
1489 haproxy h1 -conf {
1490@@ -49,6 +57,14 @@ haproxy h1 -conf {
1491 log ${S_addr}:${S_port} local0 debug err
1492 bind "fd@${fe1}"
1493 use_backend be1
1494+
1495+ frontend fe2
1496+ timeout client 10s
1497+ option httplog
1498+ option http-buffer-request
1499+ log ${S_addr}:${S_port} local0 debug err
1500+ bind "fd@${fe2}"
1501+ use_backend be1
1502 } -start
1503
1504 # 1 byte of the payload is missing.
1505@@ -80,6 +96,9 @@ client c1 -connect ${h1_fe1_sock} {
1506 expect resp.status == 408
1507 } -run
1508
1509+# Wait matching on log message
1510+barrier b1 sync
1511+
1512 # Payload is fully sent
1513 # ==> Request must be sent to the server. A 200 must be received
1514 client c2 -connect ${h1_fe1_sock} {
1515@@ -88,10 +107,13 @@ client c2 -connect ${h1_fe1_sock} {
1516 expect resp.status == 200
1517 } -run
1518
1519+# Wait matching on log message
1520+barrier b1 sync
1521+
1522 # Payload is fully sent in 2 steps (with a small delay, smaller than the client
1523 # timeout) and splitted on a chunk size.
1524 # ==> Request must be sent to the server. A 200 must be received
1525-client c3 -connect ${h1_fe1_sock} {
1526+client c3 -connect ${h1_fe2_sock} {
1527 send "POST /1 HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n1\r\n1\r\n1"
1528 delay 0.01
1529 send "\r\n1\r\n0\r\n\r\n"
1530@@ -99,11 +121,14 @@ client c3 -connect ${h1_fe1_sock} {
1531 expect resp.status == 200
1532 } -run
1533
1534+# Wait matching on log message
1535+barrier b1 sync
1536+
1537 # Last CRLF of the request payload is missing but payload is sent in 2 steps
1538 # (with a small delay, smaller than the client timeout) and splitted on a chunk
1539 # size. The client aborts before sending the last CRLF.
1540 # ==> Request must be handled as an error with 'CR--' termination state.
1541-client c4 -connect ${h1_fe1_sock} {
1542+client c4 -connect ${h1_fe2_sock} {
1543 send "POST /2 HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n1\r\n1\r\n1"
1544 delay 0.01
1545 send "\r\n1\r\n0\r\n"
1546diff --git a/scripts/announce-release b/scripts/announce-release
1547index a229301..4c75a3b 100755
1548--- a/scripts/announce-release
1549+++ b/scripts/announce-release
1550@@ -210,20 +210,21 @@ else
1551 fi
1552
1553 (echo "Please find the usual URLs below :"
1554- echo " Site index : http://www.haproxy.org/"
1555- echo " Documentation : http://docs.haproxy.org/"
1556+ echo " Site index : https://www.haproxy.org/"
1557+ echo " Documentation : https://docs.haproxy.org/"
1558 echo " Wiki : https://github.com/haproxy/wiki/wiki"
1559- echo " Discourse : http://discourse.haproxy.org/"
1560+ echo " Discourse : https://discourse.haproxy.org/"
1561 echo " Slack channel : https://slack.haproxy.org/"
1562 echo " Issue tracker : https://github.com/haproxy/haproxy/issues"
1563- echo " Sources : http://www.haproxy.org/download/${BRANCH}/src/"
1564- echo " Git repository : http://git.haproxy.org/git/${gitdir}/"
1565- echo " Git Web browsing : http://git.haproxy.org/?p=${gitdir}"
1566- echo " Changelog : http://www.haproxy.org/download/${BRANCH}/src/CHANGELOG"
1567- echo " Pending bugs : http://www.haproxy.org/l/pending-bugs"
1568- echo " Reviewed bugs : http://www.haproxy.org/l/reviewed-bugs"
1569- echo " Code reports : http://www.haproxy.org/l/code-reports"
1570- echo " Latest builds : http://www.haproxy.org/l/dev-packages"
1571+ echo " Sources : https://www.haproxy.org/download/${BRANCH}/src/"
1572+ echo " Git repository : https://git.haproxy.org/git/${gitdir}/"
1573+ echo " Git Web browsing : https://git.haproxy.org/?p=${gitdir}"
1574+ echo " Changelog : https://www.haproxy.org/download/${BRANCH}/src/CHANGELOG"
1575+ echo " Dataplane API : https://github.com/haproxytech/dataplaneapi/releases/latest"
1576+ echo " Pending bugs : https://www.haproxy.org/l/pending-bugs"
1577+ echo " Reviewed bugs : https://www.haproxy.org/l/reviewed-bugs"
1578+ echo " Code reports : https://www.haproxy.org/l/code-reports"
1579+ echo " Latest builds : https://www.haproxy.org/l/dev-packages"
1580 ) >> "$OUTPUT"
1581
1582 # sign
1583diff --git a/scripts/make-releases-json b/scripts/make-releases-json
1584new file mode 100755
1585index 0000000..38bb2b6
1586--- /dev/null
1587+++ b/scripts/make-releases-json
1588@@ -0,0 +1,103 @@
1589+#!/usr/bin/env bash
1590+#
1591+# Scan a branch directory for source tarballs and rebuild the releases.json
1592+# file for that branch. md5 and sha256 are added if present. The highest
1593+# numberred version is referenced as the latest release.
1594+#
1595+# Usage: $0 [-b branch] [-o outfile] /path/to/download/branch
1596+#
1597+
1598+USAGE="Usage: ${0##*/} [-b branch] [-o outfile] DIR"
1599+OUTPUT=
1600+BRANCH=
1601+DIR=
1602+
1603+die() {
1604+ [ "$#" -eq 0 ] || echo "$*" >&2
1605+ exit 1
1606+}
1607+
1608+err() {
1609+ echo "$*" >&2
1610+}
1611+
1612+quit() {
1613+ [ "$#" -eq 0 -o -n "$QUIET" ] || echo "$*"
1614+ exit 0
1615+}
1616+
1617+emit_json() {
1618+ printf '{\n "branch": "%s",\n' ${BRANCH}
1619+ latest=""
1620+ for file in $(find "$DIR/src" -name 'haproxy-[0-9]*.gz' -printf "%P\n" |grep -v '[0-9]-patches*' | sort -rV ); do
1621+ rel="${file##*haproxy-}"
1622+ rel="${rel%%.tar.*}"
1623+ if [ -z "$latest" ]; then
1624+ latest="$rel";
1625+ printf ' "latest_release": "%s",\n' ${latest}
1626+ printf ' "releases": {\n'
1627+ else
1628+ printf ",\n"
1629+ fi
1630+ printf ' "%s": {\n' ${rel}
1631+ printf ' "file": "%s"' ${file}
1632+ if [ -s "$DIR/src/$file.md5" ]; then
1633+ printf ',\n "md5": "%s"' $(awk '{print $1}' "$DIR/src/$file.md5")
1634+ fi
1635+ if [ -s "$DIR/src/$file.sha256" ]; then
1636+ printf ',\n "sha256": "%s"' $(awk '{print $1}' "$DIR/src/$file.sha256")
1637+ fi
1638+ printf '\n }'
1639+ done
1640+
1641+ if [ -n "$latest" ]; then
1642+ printf "\n }" ## "releases"
1643+ fi
1644+
1645+ printf '\n}\n'
1646+}
1647+
1648+
1649+### main
1650+
1651+while [ -n "$1" -a -z "${1##-*}" ]; do
1652+ case "$1" in
1653+ -b) BRANCH="$2" ; shift 2 ;;
1654+ -o) OUTPUT="$2" ; shift 2 ;;
1655+ -h|--help) quit "$USAGE" ;;
1656+ *) die "$USAGE" ;;
1657+ esac
1658+done
1659+
1660+if [ $# -ne 1 ]; then
1661+ die "$USAGE"
1662+fi
1663+
1664+DIR="$1" ; shift
1665+if [ -z "$DIR" ]; then
1666+ die "Missing download directory name."
1667+fi
1668+
1669+if [ ! -d "$DIR/." ]; then
1670+ die "Download directory doesn't exist : $DIR"
1671+fi
1672+
1673+if [ ! -d "$DIR/src" ]; then
1674+ die "Download directory must contain 'src' : $DIR"
1675+fi
1676+
1677+if [ -z "$BRANCH" ]; then
1678+ BRANCH=${DIR##*/}
1679+ if [ -n "${BRANCH//[0-9.]}" ]; then
1680+ die "Couldn't determine branch number from dir name: $BRANCH"
1681+ fi
1682+fi
1683+
1684+# echo "debug: DIR=$DIR BRANCH=$BRANCH"
1685+if [ -n "$OUTPUT" ]; then
1686+ emit_json > "$OUTPUT.tmp"
1687+ mv -f "$OUTPUT.tmp" "$OUTPUT"
1688+ rm -f "$OUTPUT.tmp"
1689+else
1690+ emit_json
1691+fi
1692diff --git a/scripts/publish-release b/scripts/publish-release
1693index abde56f..d785244 100755
1694--- a/scripts/publish-release
1695+++ b/scripts/publish-release
1696@@ -17,6 +17,7 @@ BRANCH=
1697 DEVEL=
1698 QUIET=
1699 AUTO=
1700+ARG0="$0"
1701 NEW=
1702 DIR=
1703 DOC=( )
1704@@ -178,6 +179,11 @@ for i in "${DOC[@]}"; do
1705 $CMD_GZIP < "$TARGET_DIR/doc/${i#doc/}" > "$TARGET_DIR/doc/${i#doc/}.gz"
1706 done
1707
1708+if [ -x "${ARG0%/*}/make-releases-json" ]; then
1709+ # regenerate versions
1710+ "${ARG0%/*}/make-releases-json" -o "$TARGET_DIR/src/releases.json" "$TARGET_DIR"
1711+fi
1712+
1713 echo "Done : ls -l ${TARGET_DIR}"
1714 ( cd "$TARGET_DIR" ;
1715 ls -l src/CHANGELOG "src${DEVEL}/haproxy-${NEW}".tar.gz{,.md5,.sha256} $(for i in "${DOC[@]}"; do echo "doc/${i#doc/}"{,.gz}; done)
1716diff --git a/src/backend.c b/src/backend.c
1717index 056e53a..3fe9fdc 100644
1718--- a/src/backend.c
1719+++ b/src/backend.c
1720@@ -728,11 +728,6 @@ int assign_server(struct stream *s)
1721 (void *)&((struct sockaddr_in6 *)&conn->addr.from)->sin6_addr,
1722 16, prev_srv);
1723 }
1724- else {
1725- /* unknown IP family */
1726- err = SRV_STATUS_INTERNAL;
1727- goto out;
1728- }
1729 break;
1730
1731 case BE_LB_HASH_URI:
1732diff --git a/src/cache.c b/src/cache.c
1733index 07c406c..d94a76e 100644
1734--- a/src/cache.c
1735+++ b/src/cache.c
1736@@ -81,7 +81,7 @@ struct cache_st {
1737
1738 struct cache_entry {
1739 unsigned int latest_validation; /* latest validation date */
1740- unsigned int expire; /* expiration date */
1741+ unsigned int expire; /* expiration date (wall clock time) */
1742 unsigned int age; /* Origin server "Age" header value */
1743 unsigned int eoh; /* Origin server end of headers offset. */ // field used in legacy mode only
1744
1745@@ -114,7 +114,7 @@ struct cache_entry *entry_exist(struct cache *cache, char *hash)
1746 if (memcmp(entry->hash, hash, sizeof(entry->hash)))
1747 return NULL;
1748
1749- if (entry->expire > now.tv_sec) {
1750+ if (entry->expire > date.tv_sec) {
1751 return entry;
1752 } else {
1753 eb32_delete(node);
1754@@ -864,8 +864,8 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
1755 shctx_unlock(shctx);
1756
1757 /* store latest value and expiration time */
1758- object->latest_validation = now.tv_sec;
1759- object->expire = now.tv_sec + http_calc_maxage(s, cconf->c.cache);
1760+ object->latest_validation = date.tv_sec;
1761+ object->expire = date.tv_sec + http_calc_maxage(s, cconf->c.cache);
1762 return ACT_RET_CONT;
1763 }
1764
1765@@ -1059,7 +1059,7 @@ static int htx_cache_add_age_hdr(struct appctx *appctx, struct htx *htx)
1766 char *end;
1767
1768 chunk_reset(&trash);
1769- age = MAX(0, (int)(now.tv_sec - cache_ptr->latest_validation)) + cache_ptr->age;
1770+ age = MAX(0, (int)(date.tv_sec - cache_ptr->latest_validation)) + cache_ptr->age;
1771 if (unlikely(age > CACHE_ENTRY_MAX_AGE))
1772 age = CACHE_ENTRY_MAX_AGE;
1773 end = ultoa_o(age, b_head(&trash), b_size(&trash));
1774@@ -1877,11 +1877,11 @@ static int cli_io_handler_show_cache(struct appctx *appctx)
1775 entry = container_of(node, struct cache_entry, eb);
1776 next_key = node->key + 1;
1777
1778- if (entry->expire > now.tv_sec) {
1779+ if (entry->expire > date.tv_sec) {
1780 chunk_printf(&trash, "%p hash:%u size:%u (%u blocks), refcount:%u, expire:%d\n",
1781 entry, (*(unsigned int *)entry->hash),
1782 block_ptr(entry)->len, block_ptr(entry)->block_count,
1783- block_ptr(entry)->refcount, entry->expire - (int)now.tv_sec);
1784+ block_ptr(entry)->refcount, entry->expire - (int)date.tv_sec);
1785 } else {
1786 /* time to remove that one */
1787 eb32_delete(&entry->eb);
1788diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
1789index 8bb85c6..7520620 100644
1790--- a/src/cfgparse-listen.c
1791+++ b/src/cfgparse-listen.c
1792@@ -381,7 +381,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
1793 }
1794
1795 /* set default values */
1796- memcpy(&curproxy->defsrv, &defproxy.defsrv, sizeof(curproxy->defsrv));
1797+ srv_settings_cpy(&curproxy->defsrv, &defproxy.defsrv, 0);
1798+
1799 curproxy->defsrv.id = "default-server";
1800
1801 curproxy->state = defproxy.state;
1802diff --git a/src/cfgparse.c b/src/cfgparse.c
1803index 36cb18d..e4a93dd 100644
1804--- a/src/cfgparse.c
1805+++ b/src/cfgparse.c
1806@@ -540,9 +540,10 @@ static struct bind_conf *bind_conf_uniq_alloc(struct proxy *p,
1807
1808 if (!LIST_ISEMPTY(&p->conf.bind)) {
1809 bind_conf = LIST_ELEM((&p->conf.bind)->n, typeof(bind_conf), by_fe);
1810- free(bind_conf->file);
1811- bind_conf->file = strdup(file);
1812- bind_conf->line = line;
1813+ /*
1814+ * We keep bind_conf->file and bind_conf->line unchanged
1815+ * to make them available for error messages
1816+ */
1817 if (arg) {
1818 free(bind_conf->arg);
1819 bind_conf->arg = strdup(arg);
1820@@ -627,7 +628,12 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm)
1821 }
1822
1823 bind_conf = bind_conf_uniq_alloc(curpeers->peers_fe, file, linenum,
1824- NULL, xprt_get(XPRT_RAW));
1825+ args[1], xprt_get(XPRT_RAW));
1826+ if (!bind_conf) {
1827+ ha_alert("parsing [%s:%d] : '%s %s' : cannot allocate memory.\n", file, linenum, args[0], args[1]);
1828+ err_code |= ERR_FATAL;
1829+ goto out;
1830+ }
1831 if (*args[0] == 'b') {
1832 struct listener *l;
1833
1834@@ -637,6 +643,11 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm)
1835 goto out;
1836 }
1837
1838+ if (!LIST_ISEMPTY(&bind_conf->listeners)) {
1839+ ha_alert("parsing [%s:%d] : One listener per \"peers\" section is authorized but another is already configured at [%s:%d].\n", file, linenum, bind_conf->file, bind_conf->line);
1840+ err_code |= ERR_FATAL;
1841+ }
1842+
1843 if (!str2listener(args[1], curpeers->peers_fe, bind_conf, file, linenum, &errmsg)) {
1844 if (errmsg && *errmsg) {
1845 indent_msg(&errmsg, 2);
1846@@ -644,11 +655,14 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm)
1847 }
1848 else
1849 ha_alert("parsing [%s:%d] : '%s %s' : error encountered while parsing listening address %s.\n",
1850- file, linenum, args[0], args[1], args[2]);
1851+ file, linenum, args[0], args[1], args[1]);
1852 err_code |= ERR_FATAL;
1853 goto out;
1854 }
1855- l = LIST_ELEM(bind_conf->listeners.n, typeof(l), by_bind);
1856+ /*
1857+ * Newly allocated listener is at the end of the list
1858+ */
1859+ l = LIST_ELEM(bind_conf->listeners.p, typeof(l), by_bind);
1860 l->maxaccept = 1;
1861 l->accept = session_accept_fd;
1862 l->analysers |= curpeers->peers_fe->fe_req_ana;
1863@@ -848,6 +862,16 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm)
1864 goto out;
1865
1866 bind_conf = bind_conf_uniq_alloc(curpeers->peers_fe, file, linenum, args[2], xprt_get(XPRT_RAW));
1867+ if (!bind_conf) {
1868+ ha_alert("parsing [%s:%d] : '%s %s' : Cannot allocate memory.\n", file, linenum, args[0], args[1]);
1869+ err_code |= ERR_FATAL;
1870+ goto out;
1871+ }
1872+
1873+ if (!LIST_ISEMPTY(&bind_conf->listeners)) {
1874+ ha_alert("parsing [%s:%d] : One listener per \"peers\" section is authorized but another is already configured at [%s:%d].\n", file, linenum, bind_conf->file, bind_conf->line);
1875+ err_code |= ERR_FATAL;
1876+ }
1877
1878 if (!str2listener(args[2], curpeers->peers_fe, bind_conf, file, linenum, &errmsg)) {
1879 if (errmsg && *errmsg) {
1880@@ -861,7 +885,10 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm)
1881 goto out;
1882 }
1883
1884- l = LIST_ELEM(bind_conf->listeners.n, typeof(l), by_bind);
1885+ /*
1886+ * Newly allocated listener is at the end of the list
1887+ */
1888+ l = LIST_ELEM(bind_conf->listeners.p, typeof(l), by_bind);
1889 l->maxaccept = 1;
1890 l->accept = session_accept_fd;
1891 l->analysers |= curpeers->peers_fe->fe_req_ana;
1892@@ -2078,6 +2105,7 @@ next_line:
1893
1894 /* if not enough space in thisline */
1895 if (newlinesize > linesize) {
1896+ uintptr_t thisline_addr = (uintptr_t)thisline; /* Save thisline address to make GCC happy ! */
1897 char *newline;
1898
1899 newline = realloc(thisline, newlinesize * sizeof(*thisline));
1900@@ -2087,20 +2115,20 @@ next_line:
1901 goto next_line; /* slip current line */
1902 }
1903 /* recompute pointers if realloc returns a new pointer */
1904- if (newline != thisline) {
1905+ if (newline != (char *)thisline_addr) {
1906 int i;
1907 int diff;
1908
1909 for (i = 0; i <= arg; i++) {
1910- diff = args[i] - thisline;
1911+ diff = args[i] - (char *)thisline_addr;
1912 args[i] = newline + diff;
1913 }
1914
1915- diff = var_end - thisline;
1916+ diff = var_end - (char *)thisline_addr;
1917 var_end = newline + diff;
1918- diff = end - thisline;
1919+ diff = end - (char *)thisline_addr;
1920 end = newline + diff;
1921- diff = line - thisline;
1922+ diff = line - (char *)thisline_addr;
1923 line = newline + diff;
1924 thisline = newline;
1925 }
1926diff --git a/src/dns.c b/src/dns.c
1927index 694f840..de9892b 100644
1928--- a/src/dns.c
1929+++ b/src/dns.c
1930@@ -175,11 +175,11 @@ static void dns_update_resolvers_timeout(struct dns_resolvers *resolvers)
1931 next = tick_add(now_ms, resolvers->timeout.resolve);
1932 if (!LIST_ISEMPTY(&resolvers->resolutions.curr)) {
1933 res = LIST_NEXT(&resolvers->resolutions.curr, struct dns_resolution *, list);
1934- next = MIN(next, tick_add(res->last_query, resolvers->timeout.retry));
1935+ next = tick_first(next, tick_add(res->last_query, resolvers->timeout.retry));
1936 }
1937
1938 list_for_each_entry(res, &resolvers->resolutions.wait, list)
1939- next = MIN(next, tick_add(res->last_resolution, dns_resolution_timeout(res)));
1940+ next = tick_first(next, tick_add(res->last_resolution, dns_resolution_timeout(res)));
1941
1942 resolvers->t->expire = next;
1943 task_queue(resolvers->t);
1944@@ -1990,7 +1990,7 @@ static void dns_deinit(void)
1945
1946 /* Finalizes the DNS configuration by allocating required resources and checking
1947 * live parameters.
1948- * Returns 0 on success, ERR_* flags otherwise.
1949+ * Returns 0 on success, 1 on error.
1950 */
1951 static int dns_finalize_config(void)
1952 {
1953@@ -2058,6 +2058,13 @@ static int dns_finalize_config(void)
1954 for (px = proxies_list; px; px = px->next) {
1955 struct server *srv;
1956
1957+ if (px->state == PR_STSTOPPED) {
1958+ /* must not run and will not work anyway since
1959+ * nothing in the proxy is initialized.
1960+ */
1961+ continue;
1962+ }
1963+
1964 for (srv = px->srv; srv; srv = srv->next) {
1965 struct dns_resolvers *resolvers;
1966
1967@@ -2093,10 +2100,10 @@ static int dns_finalize_config(void)
1968 if (err_code & (ERR_ALERT|ERR_ABORT))
1969 goto err;
1970
1971- return err_code;
1972+ return 0;
1973 err:
1974 dns_deinit();
1975- return err_code;
1976+ return 1;
1977
1978 }
1979
1980@@ -2262,7 +2269,12 @@ enum act_return dns_action_do_resolve(struct act_rule *rule, struct proxy *px,
1981 if (resolution->step == RSLV_STEP_RUNNING)
1982 goto yield;
1983 if (resolution->step == RSLV_STEP_NONE) {
1984- /* We update the variable only if we have a valid response. */
1985+ /* We update the variable only if we have a valid
1986+ * response. If the response was not received yet, we
1987+ * must yield.
1988+ */
1989+ if (resolution->status == RSLV_STATUS_NONE)
1990+ goto yield;
1991 if (resolution->status == RSLV_STATUS_VALID) {
1992 struct sample smp;
1993 short ip_sin_family = 0;
1994diff --git a/src/ev_epoll.c b/src/ev_epoll.c
1995index 6c09c04..d836306 100644
1996--- a/src/ev_epoll.c
1997+++ b/src/ev_epoll.c
1998@@ -146,7 +146,7 @@ REGPRM3 static void _do_poll(struct poller *p, int exp, int wake)
1999
2000 thread_harmless_now();
2001
2002- /* now let's wait for polled events */
2003+ /* Now let's wait for polled events. */
2004 wait_time = wake ? 0 : compute_poll_timeout(exp);
2005 tv_entering_poll();
2006 activity_count_runtime();
2007@@ -160,7 +160,7 @@ REGPRM3 static void _do_poll(struct poller *p, int exp, int wake)
2008 break;
2009 if (timeout || !wait_time)
2010 break;
2011- if (signal_queue_len || wake)
2012+ if (wake)
2013 break;
2014 if (tick_isset(exp) && tick_is_expired(exp, now_ms))
2015 break;
2016diff --git a/src/ev_evports.c b/src/ev_evports.c
2017index eae72d0..2442579 100644
2018--- a/src/ev_evports.c
2019+++ b/src/ev_evports.c
2020@@ -141,9 +141,7 @@ REGPRM3 static void _do_poll(struct poller *p, int exp, int wake)
2021
2022 thread_harmless_now();
2023
2024- /*
2025- * Determine how long to wait for events to materialise on the port.
2026- */
2027+ /* Now let's wait for polled events. */
2028 wait_time = wake ? 0 : compute_poll_timeout(exp);
2029 tv_entering_poll();
2030 activity_count_runtime();
2031@@ -183,7 +181,7 @@ REGPRM3 static void _do_poll(struct poller *p, int exp, int wake)
2032 break;
2033 if (timeout || !wait_time)
2034 break;
2035- if (signal_queue_len || wake)
2036+ if (wake)
2037 break;
2038 if (tick_isset(exp) && tick_is_expired(exp, now_ms))
2039 break;
2040diff --git a/src/ev_kqueue.c b/src/ev_kqueue.c
2041index 56e7147..47f4647 100644
2042--- a/src/ev_kqueue.c
2043+++ b/src/ev_kqueue.c
2044@@ -131,7 +131,7 @@ REGPRM3 static void _do_poll(struct poller *p, int exp, int wake)
2045 }
2046 fd_nbupdt = 0;
2047
2048- /* now let's wait for events */
2049+ /* Now let's wait for polled events. */
2050 wait_time = wake ? 0 : compute_poll_timeout(exp);
2051 fd = global.tune.maxpollevents;
2052 tv_entering_poll();
2053@@ -155,7 +155,7 @@ REGPRM3 static void _do_poll(struct poller *p, int exp, int wake)
2054 break;
2055 if (timeout || !wait_time)
2056 break;
2057- if (signal_queue_len || wake)
2058+ if (wake)
2059 break;
2060 if (tick_isset(exp) && tick_is_expired(exp, now_ms))
2061 break;
2062diff --git a/src/ev_poll.c b/src/ev_poll.c
2063index 86bf8e8..39e5fd9 100644
2064--- a/src/ev_poll.c
2065+++ b/src/ev_poll.c
2066@@ -24,6 +24,7 @@
2067 #include <common/time.h>
2068
2069 #include <types/global.h>
2070+#include <types/signal.h>
2071
2072 #include <proto/activity.h>
2073 #include <proto/fd.h>
2074@@ -193,7 +194,7 @@ REGPRM3 static void _do_poll(struct poller *p, int exp, int wake)
2075 }
2076 }
2077
2078- /* now let's wait for events */
2079+ /* Now let's wait for polled events. */
2080 wait_time = wake ? 0 : compute_poll_timeout(exp);
2081 tv_entering_poll();
2082 activity_count_runtime();
2083diff --git a/src/flt_spoe.c b/src/flt_spoe.c
2084index 16b5d6f..128461b 100644
2085--- a/src/flt_spoe.c
2086+++ b/src/flt_spoe.c
2087@@ -1290,7 +1290,7 @@ spoe_release_appctx(struct appctx *appctx)
2088 /* Destroy the task attached to this applet */
2089 task_destroy(spoe_appctx->task);
2090
2091- /* Notify all waiting streams */
2092+ /* Report an error to all streams in the appctx waiting queue */
2093 list_for_each_entry_safe(ctx, back, &spoe_appctx->waiting_queue, list) {
2094 LIST_DEL(&ctx->list);
2095 LIST_INIT(&ctx->list);
2096@@ -1302,8 +1302,8 @@ spoe_release_appctx(struct appctx *appctx)
2097 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
2098 }
2099
2100- /* If the applet was processing a fragmented frame, notify the
2101- * corresponding stream. */
2102+ /* If the applet was processing a fragmented frame, report an error to
2103+ * the corresponding stream. */
2104 if (spoe_appctx->frag_ctx.ctx) {
2105 ctx = spoe_appctx->frag_ctx.ctx;
2106 ctx->spoe_appctx = NULL;
2107@@ -1312,7 +1312,11 @@ spoe_release_appctx(struct appctx *appctx)
2108 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
2109 }
2110
2111- if (!LIST_ISEMPTY(&agent->rt[tid].waiting_queue)) {
2112+ if (!LIST_ISEMPTY(&agent->rt[tid].applets)) {
2113+ /* If there are still some running applets, remove reference on
2114+ * the current one from streams in the async waiting queue. In
2115+ * async mode, the ACK may be received from another appctx.
2116+ */
2117 list_for_each_entry_safe(ctx, back, &agent->rt[tid].waiting_queue, list) {
2118 if (ctx->spoe_appctx == spoe_appctx)
2119 ctx->spoe_appctx = NULL;
2120@@ -1320,16 +1324,25 @@ spoe_release_appctx(struct appctx *appctx)
2121 goto end;
2122 }
2123 else {
2124- /* It is the last running applet and the sending and waiting
2125- * queues are not empty. Try to start a new one if HAproxy is
2126- * not stopping.
2127+ /* It is the last running applet and the sending and async
2128+ * waiting queues are not empty. So try to start a new applet if
2129+ * HAproxy is not stopping. On success, we remove reference on
2130+ * the current appctx from streams in the async waiting queue.
2131+ * In async mode, the ACK may be received from another appctx.
2132 */
2133 if (!stopping &&
2134 (!LIST_ISEMPTY(&agent->rt[tid].sending_queue) || !LIST_ISEMPTY(&agent->rt[tid].waiting_queue)) &&
2135- spoe_create_appctx(agent->spoe_conf))
2136+ spoe_create_appctx(agent->spoe_conf)) {
2137+ list_for_each_entry_safe(ctx, back, &agent->rt[tid].waiting_queue, list) {
2138+ if (ctx->spoe_appctx == spoe_appctx)
2139+ ctx->spoe_appctx = NULL;
2140+ }
2141 goto end;
2142+ }
2143
2144- /* otherwise, notify all waiting streams */
2145+ /* Otherwise, report an error to all streams in the sending and
2146+ * async waiting queues.
2147+ */
2148 list_for_each_entry_safe(ctx, back, &agent->rt[tid].sending_queue, list) {
2149 LIST_DEL(&ctx->list);
2150 LIST_INIT(&ctx->list);
2151diff --git a/src/h1.c b/src/h1.c
2152index 3839fbe..69a7ced 100644
2153--- a/src/h1.c
2154+++ b/src/h1.c
2155@@ -672,6 +672,10 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
2156
2157 if (likely(*ptr == ':')) {
2158 col = ptr - start;
2159+ if (col <= sol) {
2160+ state = H1_MSG_HDR_NAME;
2161+ goto http_msg_invalid;
2162+ }
2163 EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_l1_sp, http_msg_ood, state, H1_MSG_HDR_L1_SP);
2164 }
2165
2166diff --git a/src/haproxy.c b/src/haproxy.c
2167index aa37504..f6166d4 100644
2168--- a/src/haproxy.c
2169+++ b/src/haproxy.c
2170@@ -1,6 +1,6 @@
2171 /*
2172 * HA-Proxy : High Availability-enabled HTTP/TCP proxy
2173- * Copyright 2000-2022 Willy Tarreau <willy@haproxy.org>.
2174+ * Copyright 2000-2023 Willy Tarreau <willy@haproxy.org>.
2175 *
2176 * This program is free software; you can redistribute it and/or
2177 * modify it under the terms of the GNU General Public License
2178@@ -242,6 +242,7 @@ struct mworker_proc *proc_self = NULL;
2179 /* list of the temporarily limited listeners because of lack of resource */
2180 struct list global_listener_queue = LIST_HEAD_INIT(global_listener_queue);
2181 struct task *global_listener_queue_task;
2182+__decl_hathreads(HA_RWLOCK_T global_listener_rwlock);
2183 static struct task *manage_global_listener_queue(struct task *t, void *context, unsigned short state);
2184
2185 static void *run_thread_poll_loop(void *data);
2186@@ -2071,6 +2072,7 @@ static void init(int argc, char **argv)
2187 /* very simple initialization, users will queue the task if needed */
2188 global_listener_queue_task->context = NULL; /* not even a context! */
2189 global_listener_queue_task->process = manage_global_listener_queue;
2190+ HA_RWLOCK_INIT(&global_listener_rwlock);
2191
2192 /* now we know the buffer size, we can initialize the channels and buffers */
2193 init_buffer();
2194@@ -2778,7 +2780,7 @@ static void run_poll_loop()
2195 if (killed > 1)
2196 break;
2197
2198- /* expire immediately if events are pending */
2199+ /* expire immediately if events or signals are pending */
2200 wake = 1;
2201 if (fd_cache_mask & tid_bit)
2202 activity[tid].wake_cache++;
2203@@ -2792,6 +2794,10 @@ static void run_poll_loop()
2204 if (global_tasks_mask & tid_bit) {
2205 activity[tid].wake_tasks++;
2206 _HA_ATOMIC_AND(&sleeping_thread_mask, ~tid_bit);
2207+ } else if (signal_queue_len) {
2208+ /* this check is required to avoid
2209+ * a race with wakeup on signals using wake_threads() */
2210+ _HA_ATOMIC_AND(&sleeping_thread_mask, ~tid_bit);
2211 } else
2212 wake = 0;
2213 }
2214@@ -2946,7 +2952,9 @@ static struct task *manage_global_listener_queue(struct task *t, void *context,
2215 dequeue_all_listeners(&global_listener_queue);
2216
2217 out:
2218+ HA_RWLOCK_WRLOCK(LISTENER_LOCK, &global_listener_rwlock);
2219 t->expire = next;
2220+ HA_RWLOCK_WRUNLOCK(LISTENER_LOCK, &global_listener_rwlock);
2221 task_queue(t);
2222 return t;
2223 }
2224diff --git a/src/hlua.c b/src/hlua.c
2225index 888890f..bd8a679 100644
2226--- a/src/hlua.c
2227+++ b/src/hlua.c
2228@@ -649,7 +649,9 @@ __LJMP int hlua_lua2arg_check(lua_State *L, int first, struct arg *argp,
2229 break;
2230
2231 case ARGT_TAB:
2232- argp[idx].data.prx = p;
2233+ if (!p->table)
2234+ WILL_LJMP(luaL_argerror(L, first + idx, "Mandatory argument expected"));
2235+ argp[idx].data.t = p->table;
2236 argp[idx].type = ARGT_TAB;
2237 argp[idx+1].type = ARGT_STOP;
2238 break;
2239diff --git a/src/hlua_fcn.c b/src/hlua_fcn.c
2240index 955a1ce..c42a60a 100644
2241--- a/src/hlua_fcn.c
2242+++ b/src/hlua_fcn.c
2243@@ -1298,6 +1298,7 @@ int hlua_proxy_pause(lua_State *L)
2244 struct proxy *px;
2245
2246 px = hlua_check_proxy(L, 1);
2247+ /* safe to call without PROXY_LOCK - pause_proxy takes it */
2248 pause_proxy(px);
2249 return 0;
2250 }
2251@@ -1307,6 +1308,7 @@ int hlua_proxy_resume(lua_State *L)
2252 struct proxy *px;
2253
2254 px = hlua_check_proxy(L, 1);
2255+ /* safe to call without PROXY_LOCK - resume_proxy takes it */
2256 resume_proxy(px);
2257 return 0;
2258 }
2259@@ -1316,6 +1318,7 @@ int hlua_proxy_stop(lua_State *L)
2260 struct proxy *px;
2261
2262 px = hlua_check_proxy(L, 1);
2263+ /* safe to call without PROXY_LOCK - stop_proxy takes it */
2264 stop_proxy(px);
2265 return 0;
2266 }
2267diff --git a/src/hpack-dec.c b/src/hpack-dec.c
2268index 7f3e762..0fc71eb 100644
2269--- a/src/hpack-dec.c
2270+++ b/src/hpack-dec.c
2271@@ -425,6 +425,15 @@ int hpack_decode_frame(struct hpack_dht *dht, const uint8_t *raw, uint32_t len,
2272 /* <name> and <value> are correctly filled here */
2273 }
2274
2275+ /* We must not accept empty header names (forbidden by the spec and used
2276+ * as a list termination).
2277+ */
2278+ if (!name.len) {
2279+ hpack_debug_printf("##ERR@%d##\n", __LINE__);
2280+ ret = -HPACK_ERR_INVALID_ARGUMENT;
2281+ goto leave;
2282+ }
2283+
2284 /* here's what we have here :
2285 * - name.len > 0
2286 * - value is filled with either const data or data allocated from tmp
2287diff --git a/src/http_fetch.c b/src/http_fetch.c
2288index d40bc1d..ea87a52 100644
2289--- a/src/http_fetch.c
2290+++ b/src/http_fetch.c
2291@@ -244,7 +244,7 @@ struct htx *smp_prefetch_htx(struct sample *smp, struct channel *chn, int vol)
2292 if (IS_HTX_STRM(s)) {
2293 htx = htxbuf(&chn->buf);
2294
2295- if (msg->msg_state == HTTP_MSG_ERROR || (htx->flags & HTX_FL_PARSING_ERROR))
2296+ if (htx->flags & HTX_FL_PARSING_ERROR)
2297 return NULL;
2298
2299 if (msg->msg_state < HTTP_MSG_BODY) {
2300@@ -460,6 +460,7 @@ static int smp_fetch_meth(const struct arg *args, struct sample *smp, const char
2301 struct channel *chn = SMP_REQ_CHN(smp);
2302 int meth;
2303 struct http_txn *txn;
2304+ struct htx *htx = NULL;
2305
2306 if (smp->px->options2 & PR_O2_USE_HTX) {
2307 /* HTX version */
2308@@ -468,15 +469,17 @@ static int smp_fetch_meth(const struct arg *args, struct sample *smp, const char
2309 return 0;
2310
2311 meth = txn->meth;
2312- smp->data.type = SMP_T_METH;
2313- smp->data.u.meth.meth = meth;
2314 if (meth == HTTP_METH_OTHER) {
2315- struct htx *htx;
2316- struct htx_sl *sl;
2317-
2318 htx = smp_prefetch_htx(smp, chn, 1);
2319 if (!htx)
2320 return 0;
2321+ meth = txn->meth;
2322+ }
2323+
2324+ smp->data.type = SMP_T_METH;
2325+ smp->data.u.meth.meth = meth;
2326+ if (meth == HTTP_METH_OTHER) {
2327+ struct htx_sl *sl;
2328
2329 if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) {
2330 /* ensure the indexes are not affected */
2331diff --git a/src/http_msg.c b/src/http_msg.c
2332index 067a7f8..e07c81c 100644
2333--- a/src/http_msg.c
2334+++ b/src/http_msg.c
2335@@ -1006,8 +1006,14 @@ void http_msg_analyzer(struct http_msg *msg, struct hdr_idx *idx)
2336 if (likely(HTTP_IS_TOKEN(*ptr)))
2337 EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_name, http_msg_ood, state, HTTP_MSG_HDR_NAME);
2338
2339- if (likely(*ptr == ':'))
2340+ if (likely(*ptr == ':')) {
2341+ if (ptr == input + msg->sol) {
2342+ /* empty header names are not permitted */
2343+ state = HTTP_MSG_HDR_NAME;
2344+ goto http_msg_invalid;
2345+ }
2346 EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_l1_sp, http_msg_ood, state, HTTP_MSG_HDR_L1_SP);
2347+ }
2348
2349 if (likely(msg->err_pos < -1) || *ptr == '\n') {
2350 state = HTTP_MSG_HDR_NAME;
2351diff --git a/src/listener.c b/src/listener.c
2352index 2141017..0ba2ee1 100644
2353--- a/src/listener.c
2354+++ b/src/listener.c
2355@@ -766,7 +766,9 @@ void listener_accept(int fd)
2356 */
2357 next_actconn = 0;
2358 limit_listener(l, &global_listener_queue);
2359+ HA_RWLOCK_RDLOCK(LISTENER_LOCK, &global_listener_rwlock);
2360 task_schedule(global_listener_queue_task, tick_add(now_ms, 1000)); /* try again in 1 second */
2361+ HA_RWLOCK_RDUNLOCK(LISTENER_LOCK, &global_listener_rwlock);
2362 goto end;
2363 }
2364 next_actconn = count + 1;
2365@@ -870,7 +872,9 @@ void listener_accept(int fd)
2366 p->id);
2367 close(cfd);
2368 limit_listener(l, &global_listener_queue);
2369+ HA_RWLOCK_RDLOCK(LISTENER_LOCK, &global_listener_rwlock);
2370 task_schedule(global_listener_queue_task, tick_add(now_ms, 1000)); /* try again in 1 second */
2371+ HA_RWLOCK_RDUNLOCK(LISTENER_LOCK, &global_listener_rwlock);
2372 goto end;
2373 }
2374
2375@@ -1038,7 +1042,9 @@ void listener_accept(int fd)
2376 wait_expire:
2377 /* switch the listener to LI_LIMITED and wait until up to <expire> in the global queue */
2378 limit_listener(l, &global_listener_queue);
2379+ HA_RWLOCK_RDLOCK(LISTENER_LOCK, &global_listener_rwlock);
2380 task_schedule(global_listener_queue_task, tick_first(expire, global_listener_queue_task->expire));
2381+ HA_RWLOCK_RDUNLOCK(LISTENER_LOCK, &global_listener_rwlock);
2382 end:
2383 if (next_conn)
2384 _HA_ATOMIC_SUB(&l->nbconn, 1);
2385diff --git a/src/log.c b/src/log.c
2386index e1c47ff..a63ba61 100644
2387--- a/src/log.c
2388+++ b/src/log.c
2389@@ -1262,17 +1262,21 @@ char *lf_text_len(char *dst, const char *src, size_t len, size_t size, const str
2390 }
2391
2392 if (src && len) {
2393- if (++len > size)
2394- len = size;
2395+ /* escape_string and strlcpy2 will both try to add terminating NULL-byte
2396+ * to dst, so we need to make sure that extra byte will fit into dst
2397+ * before calling them
2398+ */
2399 if (node->options & LOG_OPT_ESC) {
2400 char *ret;
2401
2402- ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
2403+ ret = escape_string(dst, (dst + size - 1), '\\', rfc5424_escape_map, src, src + len);
2404 if (ret == NULL || *ret != '\0')
2405 return NULL;
2406 len = ret - dst;
2407 }
2408 else {
2409+ if (++len > size)
2410+ len = size;
2411 len = strlcpy2(dst, src, len);
2412 }
2413
2414@@ -1283,6 +1287,7 @@ char *lf_text_len(char *dst, const char *src, size_t len, size_t size, const str
2415 if (size < 2)
2416 return NULL;
2417 *(dst++) = '-';
2418+ size -= 1;
2419 }
2420
2421 if (node->options & LOG_OPT_QUOTE) {
2422diff --git a/src/memory.c b/src/memory.c
2423index 242264c..bf19df3 100644
2424--- a/src/memory.c
2425+++ b/src/memory.c
2426@@ -531,24 +531,24 @@ int pool_total_failures()
2427 }
2428
2429 /* This function returns the total amount of memory allocated in pools (in bytes) */
2430-unsigned long pool_total_allocated()
2431+unsigned long long pool_total_allocated()
2432 {
2433 struct pool_head *entry;
2434- unsigned long allocated = 0;
2435+ unsigned long long allocated = 0;
2436
2437 list_for_each_entry(entry, &pools, list)
2438- allocated += entry->allocated * entry->size;
2439+ allocated += entry->allocated * (unsigned long long)entry->size;
2440 return allocated;
2441 }
2442
2443 /* This function returns the total amount of memory used in pools (in bytes) */
2444-unsigned long pool_total_used()
2445+unsigned long long pool_total_used()
2446 {
2447 struct pool_head *entry;
2448- unsigned long used = 0;
2449+ unsigned long long used = 0;
2450
2451 list_for_each_entry(entry, &pools, list)
2452- used += entry->used * entry->size;
2453+ used += entry->used * (unsigned long long)entry->size;
2454 return used;
2455 }
2456
2457diff --git a/src/mux_h1.c b/src/mux_h1.c
2458index 560632a..4c91672 100644
2459--- a/src/mux_h1.c
2460+++ b/src/mux_h1.c
2461@@ -623,7 +623,7 @@ static int h1_process_req_vsn(struct h1s *h1s, struct h1m *h1m, union h1_sl sl)
2462 if (sl.rq.v.len != 8)
2463 return 0;
2464
2465- if (*(sl.rq.v.ptr + 4) != '/' ||
2466+ if (!istnmatch(sl.rq.v, ist("HTTP/"), 5) ||
2467 !isdigit((unsigned char)*(sl.rq.v.ptr + 5)) ||
2468 *(sl.rq.v.ptr + 6) != '.' ||
2469 !isdigit((unsigned char)*(sl.rq.v.ptr + 7)))
2470diff --git a/src/mux_h2.c b/src/mux_h2.c
2471index 09f8fa5..3141b36 100644
2472--- a/src/mux_h2.c
2473+++ b/src/mux_h2.c
2474@@ -60,6 +60,7 @@ static const struct h2s *h2_idle_stream;
2475 // (SHORT_READ is also excluded)
2476
2477 #define H2_CF_DEM_SHORT_READ 0x00000200 // demux blocked on incomplete frame
2478+#define H2_CF_DEM_IN_PROGRESS 0x00000400 // demux in progress (dsi,dfl,dft are valid)
2479
2480 /* other flags */
2481 #define H2_CF_GOAWAY_SENT 0x00001000 // a GOAWAY frame was successfully sent
2482@@ -310,7 +311,6 @@ static struct task *h2_deferred_shut(struct task *t, void *ctx, unsigned short s
2483 static struct h2s *h2c_bck_stream_new(struct h2c *h2c, struct conn_stream *cs, struct session *sess);
2484 static void h2s_alert(struct h2s *h2s);
2485
2486-
2487 /* Detect a pending read0 for a H2 connection. It happens if a read0 was
2488 * already reported on a previous xprt->rcvbuf() AND a frame parser failed
2489 * to parse pending data, confirming no more progress is possible because
2490@@ -2546,6 +2546,7 @@ static void h2_process_demux(struct h2c *h2c)
2491 h2c->dft = hdr.ft;
2492 h2c->dff = hdr.ff;
2493 h2c->dpl = padlen;
2494+ h2c->flags |= H2_CF_DEM_IN_PROGRESS;
2495 h2c->st0 = H2_CS_FRAME_P;
2496
2497 /* check for minimum basic frame format validity */
2498@@ -2821,8 +2822,10 @@ static void h2_process_demux(struct h2c *h2c)
2499 ret = MIN(b_data(&h2c->dbuf), h2c->dfl);
2500 b_del(&h2c->dbuf, ret);
2501 h2c->dfl -= ret;
2502- if (!h2c->dfl)
2503+ if (!h2c->dfl) {
2504+ h2c->flags &= ~H2_CF_DEM_IN_PROGRESS;
2505 h2c->st0 = H2_CS_FRAME_H;
2506+ }
2507 }
2508 }
2509
2510@@ -3935,6 +3938,10 @@ next_frame:
2511 *flags |= H2_SF_HEADERS_RCVD;
2512
2513 if ((h2c->dff & H2_F_HEADERS_END_STREAM)) {
2514+ if (msgf & H2_MSGF_RSP_1XX) {
2515+ /* RFC9113#8.1 : HEADERS frame with the ES flag set that carries an informational status code is malformed */
2516+ goto fail;
2517+ }
2518 /* Mark the end of message, either using EOM in HTX or with the
2519 * trailing CRLF after the end of trailers. Note that DATA_CHNK
2520 * is not set during headers with END_STREAM. For HTX trailers,
2521diff --git a/src/mworker.c b/src/mworker.c
2522index 5ff1ef9..487d071 100644
2523--- a/src/mworker.c
2524+++ b/src/mworker.c
2525@@ -417,8 +417,10 @@ void mworker_cleanlisteners()
2526
2527 stop_proxy(curpeers->peers_fe);
2528 /* disable this peer section so that it kills itself */
2529- signal_unregister_handler(curpeers->sighandler);
2530- task_destroy(curpeers->sync_task);
2531+ if (curpeers->sighandler)
2532+ signal_unregister_handler(curpeers->sighandler);
2533+ if (curpeers->sync_task)
2534+ task_destroy(curpeers->sync_task);
2535 curpeers->sync_task = NULL;
2536 task_destroy(curpeers->peers_fe->task);
2537 curpeers->peers_fe->task = NULL;
2538diff --git a/src/peers.c b/src/peers.c
2539index ec8de36..a206d9f 100644
2540--- a/src/peers.c
2541+++ b/src/peers.c
2542@@ -105,6 +105,7 @@
2543
2544 #define PEER_RESYNC_TIMEOUT 5000 /* 5 seconds */
2545 #define PEER_RECONNECT_TIMEOUT 5000 /* 5 seconds */
2546+#define PEER_LOCAL_RECONNECT_TIMEOUT 500 /* 500ms */
2547 #define PEER_HEARTBEAT_TIMEOUT 3000 /* 3 seconds */
2548
2549 /*****************************/
2550@@ -1369,7 +1370,6 @@ static inline int peer_send_teach_stage2_msgs(struct appctx *appctx, struct peer
2551 static int peer_treat_updatemsg(struct appctx *appctx, struct peer *p, int updt, int exp,
2552 char **msg_cur, char *msg_end, int msg_len, int totl)
2553 {
2554- struct stream_interface *si = appctx->owner;
2555 struct shared_table *st = p->remote_table;
2556 struct stksess *ts, *newts;
2557 uint32_t update;
2558@@ -1565,12 +1565,9 @@ static int peer_treat_updatemsg(struct appctx *appctx, struct peer *p, int updt,
2559
2560 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
2561 stktable_touch_remote(st->table, ts, 1);
2562- return 1;
2563
2564 ignore_msg:
2565- /* skip consumed message */
2566- co_skip(si_oc(si), totl);
2567- return 0;
2568+ return 1;
2569
2570 malformed_unlock:
2571 /* malformed message */
2572@@ -1671,7 +1668,6 @@ static inline int peer_treat_switchmsg(struct appctx *appctx, struct peer *p,
2573 static inline int peer_treat_definemsg(struct appctx *appctx, struct peer *p,
2574 char **msg_cur, char *msg_end, int totl)
2575 {
2576- struct stream_interface *si = appctx->owner;
2577 int table_id_len;
2578 struct shared_table *st;
2579 int table_type;
2580@@ -1728,11 +1724,9 @@ static inline int peer_treat_definemsg(struct appctx *appctx, struct peer *p,
2581
2582 p->remote_table->remote_data = table_data;
2583 p->remote_table->remote_id = table_id;
2584- return 1;
2585
2586 ignore_msg:
2587- co_skip(si_oc(si), totl);
2588- return 0;
2589+ return 1;
2590
2591 malformed_exit:
2592 /* malformed message */
2593@@ -2413,7 +2407,7 @@ switchstate:
2594 }
2595 }
2596
2597- if (si_ic(si)->flags & CF_WRITE_PARTIAL)
2598+ if (si_ic(si)->flags & CF_WROTE_DATA)
2599 curpeer->statuscode = PEER_SESS_SC_CONNECTEDCODE;
2600
2601 reql = peer_getline(appctx);
2602@@ -2593,7 +2587,9 @@ void peers_setup_frontend(struct proxy *fe)
2603 fe->cap = PR_CAP_FE | PR_CAP_BE;
2604 fe->maxconn = 0;
2605 fe->conn_retries = CONN_RETRIES;
2606+ fe->timeout.connect = MS_TO_TICKS(1000);
2607 fe->timeout.client = MS_TO_TICKS(5000);
2608+ fe->timeout.server = MS_TO_TICKS(5000);
2609 fe->accept = frontend_accept;
2610 fe->default_target = &peer_applet.obj_type;
2611 fe->options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
2612@@ -2613,7 +2609,7 @@ static struct appctx *peer_session_create(struct peers *peers, struct peer *peer
2613 struct conn_stream *cs;
2614
2615 peer->new_conn++;
2616- peer->reconnect = tick_add(now_ms, MS_TO_TICKS(PEER_RECONNECT_TIMEOUT));
2617+ peer->reconnect = tick_add(now_ms, (stopping ? MS_TO_TICKS(PEER_LOCAL_RECONNECT_TIMEOUT) : MS_TO_TICKS(PEER_RECONNECT_TIMEOUT)));
2618 peer->heartbeat = TICK_ETERNITY;
2619 peer->statuscode = PEER_SESS_SC_CONNECTCODE;
2620 s = NULL;
2621@@ -2888,6 +2884,10 @@ static struct task *process_peer_sync(struct task * task, void *context, unsigne
2622 peer_session_forceshutdown(ps);
2623 }
2624 }
2625+
2626+ /* Set resync timeout for the local peer and request a immediate reconnect */
2627+ peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(PEER_RESYNC_TIMEOUT));
2628+ peers->local->reconnect = now_ms;
2629 }
2630 }
2631
2632@@ -2902,19 +2902,33 @@ static struct task *process_peer_sync(struct task * task, void *context, unsigne
2633 }
2634 }
2635 else if (!ps->appctx) {
2636+ /* Re-arm resync timeout if necessary */
2637+ if (!tick_isset(peers->resync_timeout))
2638+ peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(PEER_RESYNC_TIMEOUT));
2639+
2640 /* If there's no active peer connection */
2641- if (ps->statuscode == 0 ||
2642- ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
2643- ps->statuscode == PEER_SESS_SC_CONNECTEDCODE ||
2644- ps->statuscode == PEER_SESS_SC_TRYAGAIN) {
2645- /* connection never tried
2646- * or previous peer connection was successfully established
2647- * or previous tcp connect succeeded but init state incomplete
2648- * or during previous connect, peer replies a try again statuscode */
2649-
2650- /* connect to the local peer if we must push a local sync */
2651- if (peers->flags & PEERS_F_DONOTSTOP) {
2652- peer_session_create(peers, ps);
2653+ if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FINISHED &&
2654+ !tick_is_expired(peers->resync_timeout, now_ms) &&
2655+ (ps->statuscode == 0 ||
2656+ ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
2657+ ps->statuscode == PEER_SESS_SC_CONNECTEDCODE ||
2658+ ps->statuscode == PEER_SESS_SC_TRYAGAIN)) {
2659+ /* The resync is finished for the local peer and
2660+ * the resync timeout is not expired and
2661+ * connection never tried
2662+ * or previous peer connection was successfully established
2663+ * or previous tcp connect succeeded but init state incomplete
2664+ * or during previous connect, peer replies a try again statuscode */
2665+
2666+ if (!tick_is_expired(ps->reconnect, now_ms)) {
2667+ /* reconnection timer is not expired. reschedule task for reconnect */
2668+ task->expire = tick_first(task->expire, ps->reconnect);
2669+ }
2670+ else {
2671+ /* connect to the local peer if we must push a local sync */
2672+ if (peers->flags & PEERS_F_DONOTSTOP) {
2673+ peer_session_create(peers, ps);
2674+ }
2675 }
2676 }
2677 else {
2678@@ -2929,6 +2943,9 @@ static struct task *process_peer_sync(struct task * task, void *context, unsigne
2679 }
2680 }
2681 else if (ps->statuscode == PEER_SESS_SC_SUCCESSCODE ) {
2682+ /* Reset resync timeout during a resync */
2683+ peers->resync_timeout = TICK_ETERNITY;
2684+
2685 /* current peer connection is active and established
2686 * wake up all peer handlers to push remaining local updates */
2687 for (st = ps->tables; st ; st = st->next) {
2688diff --git a/src/proto_http.c b/src/proto_http.c
2689index bdc97de..8fb2294 100644
2690--- a/src/proto_http.c
2691+++ b/src/proto_http.c
2692@@ -7240,6 +7240,7 @@ void http_init_txn(struct stream *s)
2693 struct proxy *fe = strm_fe(s);
2694 struct conn_stream *cs = objt_cs(s->si[0].end);
2695
2696+ txn->meth = HTTP_METH_OTHER;
2697 txn->flags = ((cs && cs->flags & CS_FL_NOT_FIRST)
2698 ? (TX_NOT_FIRST|TX_WAIT_NEXT_RQ)
2699 : 0);
2700@@ -7268,8 +7269,10 @@ void http_init_txn(struct stream *s)
2701 if (txn->hdr_idx.v)
2702 hdr_idx_init(&txn->hdr_idx);
2703
2704- vars_init(&s->vars_txn, SCOPE_TXN);
2705- vars_init(&s->vars_reqres, SCOPE_REQ);
2706+ /* here we don't want to re-initialize s->vars_txn and s->vars_reqres
2707+ * variable lists, because they were already initialized upon stream
2708+ * creation in stream_new(), and thus may already contain some variables
2709+ */
2710 }
2711
2712 /* to be used at the end of a transaction */
2713@@ -7300,6 +7303,9 @@ void http_reset_txn(struct stream *s)
2714 http_end_txn(s);
2715 http_init_txn(s);
2716
2717+ vars_init(&s->vars_txn, SCOPE_TXN);
2718+ vars_init(&s->vars_reqres, SCOPE_REQ);
2719+
2720 /* reinitialise the current rule list pointer to NULL. We are sure that
2721 * any rulelist match the NULL pointer.
2722 */
2723diff --git a/src/proto_htx.c b/src/proto_htx.c
2724index f79d495..51fdd47 100644
2725--- a/src/proto_htx.c
2726+++ b/src/proto_htx.c
2727@@ -2808,6 +2808,7 @@ void htx_res_set_status(unsigned int status, const char *reason, struct stream *
2728
2729 if (http_replace_res_status(htx, ist2(trash.area, trash.data)))
2730 http_replace_res_reason(htx, ist2(reason, strlen(reason)));
2731+ s->txn->status = status;
2732 }
2733
2734 /* Executes the http-request rules <rules> for stream <s>, proxy <px> and
2735diff --git a/src/proto_sockpair.c b/src/proto_sockpair.c
2736index 602cb6c..d3aeb4e 100644
2737--- a/src/proto_sockpair.c
2738+++ b/src/proto_sockpair.c
2739@@ -209,7 +209,7 @@ int send_fd_uxst(int fd, int send_fd)
2740
2741 if (sendmsg(fd, &msghdr, 0) != sizeof(iobuf)) {
2742 ha_warning("Failed to transfer socket\n");
2743- return 1;
2744+ return -1;
2745 }
2746
2747 return 0;
2748diff --git a/src/proxy.c b/src/proxy.c
2749index 92db68f..cb5f8e1 100644
2750--- a/src/proxy.c
2751+++ b/src/proxy.c
2752@@ -112,8 +112,8 @@ const struct cfg_opt cfg_opts2[] =
2753 { "http-pretend-keepalive", PR_O2_FAKE_KA, PR_CAP_BE, 0, PR_MODE_HTTP },
2754 { "http-no-delay", PR_O2_NODELAY, PR_CAP_FE|PR_CAP_BE, 0, PR_MODE_HTTP },
2755 { "http-use-htx", PR_O2_USE_HTX, PR_CAP_FE|PR_CAP_BE, 0, 0 },
2756- {"h1-case-adjust-bogus-client", PR_O2_H1_ADJ_BUGCLI, PR_CAP_FE, 0, PR_MODE_HTTP },
2757- {"h1-case-adjust-bogus-server", PR_O2_H1_ADJ_BUGSRV, PR_CAP_BE, 0, PR_MODE_HTTP },
2758+ {"h1-case-adjust-bogus-client", PR_O2_H1_ADJ_BUGCLI, PR_CAP_FE, 0, 0 },
2759+ {"h1-case-adjust-bogus-server", PR_O2_H1_ADJ_BUGSRV, PR_CAP_BE, 0, 0 },
2760 {"disable-h2-upgrade", PR_O2_NO_H2_UPGRADE, PR_CAP_FE, 0, PR_MODE_HTTP },
2761 { NULL, 0, 0, 0 }
2762 };
2763@@ -1209,14 +1209,18 @@ void soft_stop(void)
2764 * listener returns an error, then the proxy state is set to PR_STERROR
2765 * because we don't know how to resume from this. The function returns 0
2766 * if it fails, or non-zero on success.
2767+ * The function takes the proxy's lock so it's safe to
2768+ * call from multiple places.
2769 */
2770 int pause_proxy(struct proxy *p)
2771 {
2772 struct listener *l;
2773
2774+ HA_SPIN_LOCK(PROXY_LOCK, &p->lock);
2775+
2776 if (!(p->cap & PR_CAP_FE) || p->state == PR_STERROR ||
2777 p->state == PR_STSTOPPED || p->state == PR_STPAUSED)
2778- return 1;
2779+ goto end;
2780
2781 ha_warning("Pausing %s %s.\n", proxy_cap_str(p->cap), p->id);
2782 send_log(p, LOG_WARNING, "Pausing %s %s.\n", proxy_cap_str(p->cap), p->id);
2783@@ -1229,10 +1233,13 @@ int pause_proxy(struct proxy *p)
2784 if (p->state == PR_STERROR) {
2785 ha_warning("%s %s failed to enter pause mode.\n", proxy_cap_str(p->cap), p->id);
2786 send_log(p, LOG_WARNING, "%s %s failed to enter pause mode.\n", proxy_cap_str(p->cap), p->id);
2787+ HA_SPIN_UNLOCK(PROXY_LOCK, &p->lock);
2788 return 0;
2789 }
2790
2791 p->state = PR_STPAUSED;
2792+end:
2793+ HA_SPIN_UNLOCK(PROXY_LOCK, &p->lock);
2794 return 1;
2795 }
2796
2797@@ -1279,7 +1286,8 @@ void zombify_proxy(struct proxy *p)
2798 * to be called when going down in order to release the ports so that another
2799 * process may bind to them. It must also be called on disabled proxies at the
2800 * end of start-up. If all listeners are closed, the proxy is set to the
2801- * PR_STSTOPPED state. The function takes the proxy's lock so it's safe to
2802+ * PR_STSTOPPED state.
2803+ * The function takes the proxy's lock so it's safe to
2804 * call from multiple places.
2805 */
2806 void stop_proxy(struct proxy *p)
2807@@ -1314,14 +1322,18 @@ void stop_proxy(struct proxy *p)
2808 * listeners and tries to enable them all. If any of them fails, the proxy is
2809 * put back to the paused state. It returns 1 upon success, or zero if an error
2810 * is encountered.
2811+ * The function takes the proxy's lock so it's safe to
2812+ * call from multiple places.
2813 */
2814 int resume_proxy(struct proxy *p)
2815 {
2816 struct listener *l;
2817 int fail;
2818
2819+ HA_SPIN_LOCK(PROXY_LOCK, &p->lock);
2820+
2821 if (p->state != PR_STPAUSED)
2822- return 1;
2823+ goto end;
2824
2825 ha_warning("Enabling %s %s.\n", proxy_cap_str(p->cap), p->id);
2826 send_log(p, LOG_WARNING, "Enabling %s %s.\n", proxy_cap_str(p->cap), p->id);
2827@@ -1353,9 +1365,13 @@ int resume_proxy(struct proxy *p)
2828
2829 p->state = PR_STREADY;
2830 if (fail) {
2831+ HA_SPIN_UNLOCK(PROXY_LOCK, &p->lock);
2832+ /* pause_proxy will take PROXY_LOCK */
2833 pause_proxy(p);
2834 return 0;
2835 }
2836+end:
2837+ HA_SPIN_UNLOCK(PROXY_LOCK, &p->lock);
2838 return 1;
2839 }
2840
2841@@ -1648,8 +1664,8 @@ void proxy_capture_error(struct proxy *proxy, int is_back,
2842 } else {
2843 es = HA_ATOMIC_XCHG(&proxy->invalid_req, es);
2844 }
2845- free(es);
2846 HA_SPIN_UNLOCK(PROXY_LOCK, &proxy->lock);
2847+ free(es);
2848 }
2849
2850 /* Configure all proxies which lack a maxconn setting to use the global one by
2851@@ -2199,9 +2215,8 @@ static int cli_parse_disable_frontend(char **args, char *payload, struct appctx
2852 return 1;
2853 }
2854
2855- HA_SPIN_LOCK(PROXY_LOCK, &px->lock);
2856+ /* pause_proxy will take PROXY_LOCK */
2857 ret = pause_proxy(px);
2858- HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
2859
2860 if (!ret) {
2861 appctx->ctx.cli.severity = LOG_ERR;
2862@@ -2242,9 +2257,8 @@ static int cli_parse_enable_frontend(char **args, char *payload, struct appctx *
2863 return 1;
2864 }
2865
2866- HA_SPIN_LOCK(PROXY_LOCK, &px->lock);
2867+ /* resume_proxy will take PROXY_LOCK */
2868 ret = resume_proxy(px);
2869- HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
2870
2871 if (!ret) {
2872 appctx->ctx.cli.severity = LOG_ERR;
2873diff --git a/src/sample.c b/src/sample.c
2874index 61e7097..e713f29 100644
2875--- a/src/sample.c
2876+++ b/src/sample.c
2877@@ -2196,13 +2196,14 @@ found:
2878 if (!smp->data.u.str.data)
2879 return 1;
2880
2881- smp->data.u.str.area = start;
2882
2883 /* Compute remaining size if needed
2884 Note: smp->data.u.str.size cannot be set to 0 */
2885 if (smp->data.u.str.size)
2886 smp->data.u.str.size -= start - smp->data.u.str.area;
2887
2888+ smp->data.u.str.area = start;
2889+
2890 return 1;
2891 }
2892
2893diff --git a/src/server.c b/src/server.c
2894index 81639b7..bef825d 100644
2895--- a/src/server.c
2896+++ b/src/server.c
2897@@ -1582,8 +1582,7 @@ static void display_parser_err(const char *file, int linenum, char **args, int c
2898 file, linenum, args[0], args[1], args[cur_arg]);
2899 }
2900
2901-static void srv_conn_src_sport_range_cpy(struct server *srv,
2902- struct server *src)
2903+static void srv_conn_src_sport_range_cpy(struct server *srv, const struct server *src)
2904 {
2905 int range_sz;
2906
2907@@ -1604,7 +1603,7 @@ static void srv_conn_src_sport_range_cpy(struct server *srv,
2908 /*
2909 * Copy <src> server connection source settings to <srv> server everything needed.
2910 */
2911-static void srv_conn_src_cpy(struct server *srv, struct server *src)
2912+static void srv_conn_src_cpy(struct server *srv, const struct server *src)
2913 {
2914 srv->conn_src.opts = src->conn_src.opts;
2915 srv->conn_src.source_addr = src->conn_src.source_addr;
2916@@ -1630,7 +1629,7 @@ static void srv_conn_src_cpy(struct server *srv, struct server *src)
2917 * everything needed.
2918 */
2919 #if defined(USE_OPENSSL)
2920-static void srv_ssl_settings_cpy(struct server *srv, struct server *src)
2921+static void srv_ssl_settings_cpy(struct server *srv, const struct server *src)
2922 {
2923 if (src->ssl_ctx.ca_file != NULL)
2924 srv->ssl_ctx.ca_file = strdup(src->ssl_ctx.ca_file);
2925@@ -1732,7 +1731,7 @@ static int srv_prepare_for_resolution(struct server *srv, const char *hostname)
2926 * <srv_tmpl> distinguishes these two cases (must be 1 if <srv> is a template,
2927 * 0 if not).
2928 */
2929-static void srv_settings_cpy(struct server *srv, struct server *src, int srv_tmpl)
2930+void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl)
2931 {
2932 /* Connection source settings copy */
2933 srv_conn_src_cpy(srv, src);
2934diff --git a/src/signal.c b/src/signal.c
2935index 288ef00..58c1710 100644
2936--- a/src/signal.c
2937+++ b/src/signal.c
2938@@ -58,6 +58,9 @@ void signal_handler(int sig)
2939 signal_state[sig].count++;
2940 if (sig)
2941 signal(sig, signal_handler); /* re-arm signal */
2942+
2943+ /* If the thread is TH_FL_SLEEPING we need to wake it */
2944+ wake_thread(tid);
2945 }
2946
2947 /* Call handlers of all pending signals and clear counts and queue length. The
2948diff --git a/src/ssl_sock.c b/src/ssl_sock.c
2949index 7ffcefc..b33580f 100644
2950--- a/src/ssl_sock.c
2951+++ b/src/ssl_sock.c
2952@@ -105,16 +105,20 @@
2953 #define SSL_SOCK_SEND_UNLIMITED 0x00000004
2954 #define SSL_SOCK_RECV_HEARTBEAT 0x00000008
2955
2956-/* bits 0xFFFF0000 are reserved to store verify errors */
2957+/* bits 0xFFFFFF00 are reserved to store verify errors.
2958+ * The CA en CRT error codes will be stored on 7 bits each
2959+ * (since the max verify error code does not exceed 127)
2960+ * and the CA error depth will be stored on 4 bits.
2961+ */
2962
2963 /* Verify errors macros */
2964-#define SSL_SOCK_CA_ERROR_TO_ST(e) (((e > 63) ? 63 : e) << (16))
2965-#define SSL_SOCK_CAEDEPTH_TO_ST(d) (((d > 15) ? 15 : d) << (6+16))
2966-#define SSL_SOCK_CRTERROR_TO_ST(e) (((e > 63) ? 63 : e) << (4+6+16))
2967+#define SSL_SOCK_CA_ERROR_TO_ST(e) (((e > 127) ? 127 : e) << (8))
2968+#define SSL_SOCK_CAEDEPTH_TO_ST(d) (((d > 15) ? 15 : d) << (7+8))
2969+#define SSL_SOCK_CRTERROR_TO_ST(e) (((e > 127) ? 127 : e) << (4+7+8))
2970
2971-#define SSL_SOCK_ST_TO_CA_ERROR(s) ((s >> (16)) & 63)
2972-#define SSL_SOCK_ST_TO_CAEDEPTH(s) ((s >> (6+16)) & 15)
2973-#define SSL_SOCK_ST_TO_CRTERROR(s) ((s >> (4+6+16)) & 63)
2974+#define SSL_SOCK_ST_TO_CA_ERROR(s) ((s >> (8)) & 127)
2975+#define SSL_SOCK_ST_TO_CAEDEPTH(s) ((s >> (7+8)) & 15)
2976+#define SSL_SOCK_ST_TO_CRTERROR(s) ((s >> (4+7+8)) & 127)
2977
2978 /* ssl_methods flags for ssl options */
2979 #define MC_SSL_O_ALL 0x0000
2980@@ -1606,7 +1610,8 @@ int ssl_sock_bind_verifycbk(int ok, X509_STORE_CTX *x_store)
2981 ctx->xprt_st |= SSL_SOCK_CAEDEPTH_TO_ST(depth);
2982 }
2983
2984- if (err < 64 && __objt_listener(conn->target)->bind_conf->ca_ignerr & (1ULL << err)) {
2985+ if (err <= SSL_MAX_VFY_ERROR_CODE &&
2986+ cert_ignerr_bitfield_get(__objt_listener(conn->target)->bind_conf->ca_ignerr_bitfield, err)) {
2987 ssl_sock_dump_errors(conn);
2988 ERR_clear_error();
2989 return 1;
2990@@ -1620,7 +1625,8 @@ int ssl_sock_bind_verifycbk(int ok, X509_STORE_CTX *x_store)
2991 ctx->xprt_st |= SSL_SOCK_CRTERROR_TO_ST(err);
2992
2993 /* check if certificate error needs to be ignored */
2994- if (err < 64 && __objt_listener(conn->target)->bind_conf->crt_ignerr & (1ULL << err)) {
2995+ if (err <= SSL_MAX_VFY_ERROR_CODE &&
2996+ cert_ignerr_bitfield_get(__objt_listener(conn->target)->bind_conf->crt_ignerr_bitfield, err)) {
2997 ssl_sock_dump_errors(conn);
2998 ERR_clear_error();
2999 return 1;
3000@@ -2175,13 +2181,13 @@ static void ssl_set_TLSv12_func(SSL *ssl, set_context_func c) {
3001 : SSL_set_min_proto_version(ssl, TLS1_2_VERSION);
3002 }
3003 static void ctx_set_TLSv13_func(SSL_CTX *ctx, set_context_func c) {
3004-#if SSL_OP_NO_TLSv1_3
3005+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
3006 c == SET_MAX ? SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION)
3007 : SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
3008 #endif
3009 }
3010 static void ssl_set_TLSv13_func(SSL *ssl, set_context_func c) {
3011-#if SSL_OP_NO_TLSv1_3
3012+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
3013 c == SET_MAX ? SSL_set_max_proto_version(ssl, TLS1_3_VERSION)
3014 : SSL_set_min_proto_version(ssl, TLS1_3_VERSION);
3015 #endif
3016@@ -4113,6 +4119,7 @@ static int sh_ssl_sess_store(unsigned char *s_id, unsigned char *data, int data_
3017 if (oldsh_ssl_sess != sh_ssl_sess) {
3018 /* NOTE: Row couldn't be in use because we lock read & write function */
3019 /* release the reserved row */
3020+ first->len = 0; /* the len must be liberated in order not to call the release callback on it */
3021 shctx_row_dec_hot(ssl_shctx, first);
3022 /* replace the previous session already in the tree */
3023 sh_ssl_sess = oldsh_ssl_sess;
3024@@ -5069,20 +5076,17 @@ int ssl_sock_prepare_bind_conf(struct bind_conf *bind_conf)
3025 return -err;
3026 }
3027
3028-/* release ssl context allocated for servers. */
3029+/* release ssl context allocated for servers. Most of the field free here
3030+ * must also be allocated in srv_ssl_settings_cpy() */
3031 void ssl_sock_free_srv_ctx(struct server *srv)
3032 {
3033 #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3034- if (srv->ssl_ctx.alpn_str) {
3035- free(srv->ssl_ctx.alpn_str);
3036- srv->ssl_ctx.alpn_str = NULL;
3037- }
3038+ free(srv->ssl_ctx.alpn_str);
3039+ srv->ssl_ctx.alpn_str = NULL;
3040 #endif
3041 #ifdef OPENSSL_NPN_NEGOTIATED
3042- if (srv->ssl_ctx.npn_str) {
3043- free(srv->ssl_ctx.npn_str);
3044- srv->ssl_ctx.npn_str = NULL;
3045- }
3046+ free(srv->ssl_ctx.npn_str);
3047+ srv->ssl_ctx.npn_str = NULL;
3048 #endif
3049 if (srv->ssl_ctx.reused_sess) {
3050 int i;
3051@@ -5101,6 +5105,25 @@ void ssl_sock_free_srv_ctx(struct server *srv)
3052 SSL_CTX_free(srv->ssl_ctx.ctx);
3053 srv->ssl_ctx.ctx = NULL;
3054 }
3055+
3056+ free(srv->ssl_ctx.ca_file);
3057+ srv->ssl_ctx.ca_file = NULL;
3058+ free(srv->ssl_ctx.crl_file);
3059+ srv->ssl_ctx.crl_file = NULL;
3060+ free(srv->ssl_ctx.client_crt);
3061+ srv->ssl_ctx.client_crt = NULL;
3062+ free(srv->ssl_ctx.verify_host);
3063+ srv->ssl_ctx.verify_host = NULL;
3064+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
3065+ free(srv->sni_expr);
3066+ srv->sni_expr = NULL;
3067+#endif
3068+ free(srv->ssl_ctx.ciphers);
3069+ srv->ssl_ctx.ciphers = NULL;
3070+#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
3071+ free(srv->ssl_ctx.ciphersuites);
3072+ srv->ssl_ctx.ciphersuites = NULL;
3073+#endif
3074 }
3075
3076 /* Walks down the two trees in bind_conf and frees all the certs. The pointer may
3077@@ -8083,7 +8106,7 @@ static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, str
3078 {
3079 int code;
3080 char *p = args[cur_arg + 1];
3081- unsigned long long *ignerr = &conf->crt_ignerr;
3082+ unsigned long long *ignerr = conf->crt_ignerr_bitfield;
3083
3084 if (!*p) {
3085 if (err)
3086@@ -8092,22 +8115,22 @@ static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, str
3087 }
3088
3089 if (strcmp(args[cur_arg], "ca-ignore-err") == 0)
3090- ignerr = &conf->ca_ignerr;
3091+ ignerr = conf->ca_ignerr_bitfield;
3092
3093 if (strcmp(p, "all") == 0) {
3094- *ignerr = ~0ULL;
3095+ cert_ignerr_bitfield_set_all(ignerr);
3096 return 0;
3097 }
3098
3099 while (p) {
3100 code = atoi(p);
3101- if ((code <= 0) || (code > 63)) {
3102+ if ((code <= 0) || (code > SSL_MAX_VFY_ERROR_CODE)) {
3103 if (err)
3104- memprintf(err, "'%s' : ID '%d' out of range (1..63) in error IDs list '%s'",
3105- args[cur_arg], code, args[cur_arg + 1]);
3106+ memprintf(err, "'%s' : ID '%d' out of range (1..%d) in error IDs list '%s'",
3107+ args[cur_arg], code, SSL_MAX_VFY_ERROR_CODE, args[cur_arg + 1]);
3108 return ERR_ALERT | ERR_FATAL;
3109 }
3110- *ignerr |= 1ULL << code;
3111+ cert_ignerr_bitfield_set(ignerr, code);
3112 p = strchr(p, ',');
3113 if (p)
3114 p++;
3115diff --git a/src/standard.c b/src/standard.c
3116index 3dd3d6d..e941627 100644
3117--- a/src/standard.c
3118+++ b/src/standard.c
3119@@ -1613,7 +1613,8 @@ char *encode_chunk(char *start, char *stop,
3120
3121 /*
3122 * Tries to prefix characters tagged in the <map> with the <escape>
3123- * character. The input <string> must be zero-terminated. The result will
3124+ * character. The input <string> is processed until string_stop
3125+ * is reached or NULL-byte is encountered. The result will
3126 * be stored between <start> (included) and <stop> (excluded). This
3127 * function will always try to terminate the resulting string with a '\0'
3128 * before <stop>, and will return its position if the conversion
3129@@ -1621,11 +1622,11 @@ char *encode_chunk(char *start, char *stop,
3130 */
3131 char *escape_string(char *start, char *stop,
3132 const char escape, const long *map,
3133- const char *string)
3134+ const char *string, const char *string_stop)
3135 {
3136 if (start < stop) {
3137 stop--; /* reserve one byte for the final '\0' */
3138- while (start < stop && *string != '\0') {
3139+ while (start < stop && string < string_stop && *string != '\0') {
3140 if (!ha_bit_test((unsigned char)(*string), map))
3141 *start++ = *string;
3142 else {
3143diff --git a/src/stick_table.c b/src/stick_table.c
3144index 015463b..a3d63f3 100644
3145--- a/src/stick_table.c
3146+++ b/src/stick_table.c
3147@@ -215,7 +215,18 @@ int __stktable_trash_oldest(struct stktable *t, int to_batch)
3148 ts->exp.key = ts->expire;
3149 eb32_insert(&t->exps, &ts->exp);
3150
3151- if (!eb || eb->key > ts->exp.key)
3152+ /* the update might have jumped beyond the next element,
3153+ * possibly causing a wrapping. We need to check whether
3154+ * the next element should be used instead. If the next
3155+ * element doesn't exist it means we're on the right
3156+ * side and have to check the first one then. If it
3157+ * exists and is closer, we must use it, otherwise we
3158+ * use the current one.
3159+ */
3160+ if (!eb)
3161+ eb = eb32_first(&t->exps);
3162+
3163+ if (!eb || tick_is_lt(ts->exp.key, eb->key))
3164 eb = &ts->exp;
3165
3166 continue;
3167@@ -547,11 +558,12 @@ struct stksess *stktable_set_entry(struct stktable *table, struct stksess *nts)
3168 return ts;
3169 }
3170 /*
3171- * Trash expired sticky sessions from table <t>. The next expiration date is
3172- * returned.
3173+ * Task processing function to trash expired sticky sessions. A pointer to the
3174+ * task itself is returned since it never dies.
3175 */
3176-static int stktable_trash_expired(struct stktable *t)
3177+struct task *process_table_expire(struct task *task, void *context, unsigned short state)
3178 {
3179+ struct stktable *t = context;
3180 struct stksess *ts;
3181 struct eb32_node *eb;
3182 int looped = 0;
3183@@ -597,7 +609,18 @@ static int stktable_trash_expired(struct stktable *t)
3184 ts->exp.key = ts->expire;
3185 eb32_insert(&t->exps, &ts->exp);
3186
3187- if (!eb || eb->key > ts->exp.key)
3188+ /* the update might have jumped beyond the next element,
3189+ * possibly causing a wrapping. We need to check whether
3190+ * the next element should be used instead. If the next
3191+ * element doesn't exist it means we're on the right
3192+ * side and have to check the first one then. If it
3193+ * exists and is closer, we must use it, otherwise we
3194+ * use the current one.
3195+ */
3196+ if (!eb)
3197+ eb = eb32_first(&t->exps);
3198+
3199+ if (!eb || tick_is_lt(ts->exp.key, eb->key))
3200 eb = &ts->exp;
3201 continue;
3202 }
3203@@ -611,19 +634,8 @@ static int stktable_trash_expired(struct stktable *t)
3204 /* We have found no task to expire in any tree */
3205 t->exp_next = TICK_ETERNITY;
3206 out_unlock:
3207+ task->expire = t->exp_next;
3208 HA_SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
3209- return t->exp_next;
3210-}
3211-
3212-/*
3213- * Task processing function to trash expired sticky sessions. A pointer to the
3214- * task itself is returned since it never dies.
3215- */
3216-static struct task *process_table_expire(struct task *task, void *context, unsigned short state)
3217-{
3218- struct stktable *t = context;
3219-
3220- task->expire = stktable_trash_expired(t);
3221 return task;
3222 }
3223
3224diff --git a/src/stream.c b/src/stream.c
3225index 96bdce7..01f5ed5 100644
3226--- a/src/stream.c
3227+++ b/src/stream.c
3228@@ -228,8 +228,17 @@ struct stream *stream_new(struct session *sess, enum obj_type *origin)
3229 s->req_cap = NULL;
3230 s->res_cap = NULL;
3231
3232- /* Initialise all the variables contexts even if not used.
3233+ /* Initialize all the variables contexts even if not used.
3234 * This permits to prune these contexts without errors.
3235+ *
3236+ * We need to make sure that those lists are not re-initialized
3237+ * by stream-dependant underlying code because we could lose
3238+ * track of already defined variables, leading to data inconsistency
3239+ * and memory leaks...
3240+ *
3241+ * For reference: we had a very old bug caused by vars_txn and
3242+ * vars_reqres being accidentally re-initialized in http_create_txn()
3243+ * (https://github.com/haproxy/haproxy/issues/1935)
3244 */
3245 vars_init(&s->vars_txn, SCOPE_TXN);
3246 vars_init(&s->vars_reqres, SCOPE_REQ);
3247@@ -1708,7 +1717,7 @@ static int process_store_rules(struct stream *s, struct channel *rep, int an_bit
3248 void *ptr;
3249 struct dict_entry *de;
3250
3251- if (objt_server(s->target) && objt_server(s->target)->flags & SRV_F_NON_STICK) {
3252+ if (!objt_server(s->target) || (__objt_server(s->target)->flags & SRV_F_NON_STICK)) {
3253 stksess_free(s->store[i].table, s->store[i].ts);
3254 s->store[i].ts = NULL;
3255 continue;
3256@@ -1724,14 +1733,15 @@ static int process_store_rules(struct stream *s, struct channel *rep, int an_bit
3257 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
3258 ptr = __stktable_data_ptr(s->store[i].table, ts, STKTABLE_DT_SERVER_ID);
3259 stktable_data_cast(ptr, server_id) = __objt_server(s->target)->puid;
3260- HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
3261
3262- HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
3263- de = dict_insert(&server_name_dict, __objt_server(s->target)->id);
3264- if (de) {
3265- ptr = __stktable_data_ptr(s->store[i].table, ts, STKTABLE_DT_SERVER_NAME);
3266- stktable_data_cast(ptr, server_name) = de;
3267+ if (__objt_server(s->target)->id) {
3268+ de = dict_insert(&server_name_dict, __objt_server(s->target)->id);
3269+ if (de) {
3270+ ptr = __stktable_data_ptr(s->store[i].table, ts, STKTABLE_DT_SERVER_NAME);
3271+ stktable_data_cast(ptr, server_name) = de;
3272+ }
3273 }
3274+
3275 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
3276
3277 stktable_touch_local(s->store[i].table, ts, 1);
3278diff --git a/src/stream_interface.c b/src/stream_interface.c
3279index ab5c574..1adecf3 100644
3280--- a/src/stream_interface.c
3281+++ b/src/stream_interface.c
3282@@ -1149,7 +1149,7 @@ static void stream_int_chk_snd_conn(struct stream_interface *si)
3283 struct channel *oc = si_oc(si);
3284 struct conn_stream *cs = __objt_cs(si->end);
3285
3286- if (unlikely(!si_state_in(si->state, SI_SB_CON|SI_SB_RDY|SI_SB_EST) ||
3287+ if (unlikely(!si_state_in(si->state, SI_SB_RDY|SI_SB_EST) ||
3288 (oc->flags & CF_SHUTW)))
3289 return;
3290

Subscribers

People subscribed via source and target branches