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: e6f5d977ade8c6ac8ba443f1d0a703818f69915f
Proposed branch: ~lucaskanashiro/ubuntu/+source/haproxy:focal-mre
Merge into: ubuntu/+source/haproxy:ubuntu/focal-devel
Diff against target: 3076 lines (+954/-440)
68 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 (+18/-0)
debian/patches/0002-Use-dpkg-buildflags-to-build-halog.patch (+0/-2)
debian/patches/haproxy.service-add-documentation.patch (+4/-4)
debian/patches/series (+0/-2)
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
git-ubuntu bot Approve
Sergio Durigan Junior (community) Approve
Canonical Server Pending
Canonical Server Reporter Pending
Review via email: mp+429079@code.launchpad.net

This proposal supersedes a proposal from 2022-08-29.

Description of the change

Update with upstream version 2.0.29. MRE bug: LP #1987914

A couple of patches needed a refresh because there is a change in the systemd service unit file where network.target was replaced by network-online.target. The maintainers explained why they did that here (a bug fix basically):

https://github.com/haproxy/haproxy/commit/f49a6049b88b7a9f0f0a18b076f34ab83820445a

Moreover, most of the patches were applied in this version, therefore removed from the package.

PPA with the proposed package:

https://launchpad.net/~lucaskanashiro/+archive/ubuntu/haproxy-mre

To post a comment you must log in.
Revision history for this message
Sergio Durigan Junior (sergiodj) wrote :

Thanks for the MP, Lucas.

I'd like to see an updated the d/changelog entry here to list the major bug fixes, just like you did for Jammy. I also took the liberty to trigger autopkgtest runs for every supported architecture against your PPA.

The first thing I did was to try to reproduce the upstream tarball import commit. I use a local, temporary gbp repository where I perform the import, and then I compare the commit generated by gbp against the commit in the MRE branch I'm reviewing.

For this specific MP, the two commits showed some differences. They are only related to files that seem to be auto-generated by github, and they're not really important for the build process per se, but I'm wondering why they're showing up. I grabbed the upstream tarball from this page:

https://www.haproxy.org/?_gl=1*xzll7y*_ga*MTQ0OTA0NDUxLjE2NTQ1OTYzNTE.*_ga_VKZPMRNGK5*MTY1NjMzMzA5Ni41Ny4xLjE2NTYzMzQ4MjcuMA..*_ga_MGHPDQ7WFP*MTY1NjMzMzA5Ni41Ni4xLjE2NTYzMzQ4MjcuMA..#down

Did you grab it from somewhere else?

The dropped patches are OK. The updated patches are also OK.

... and the autopkgtest run has finished, and everything looks OK.

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

Thanks for the review Sergio. I did update the changelog with major and critical bug fixes as I did in the Jammy MP.

Regarding the upstream tarball, I did get it using uscan and used uupdate to update the source package, after that I committed all the changes. Not sure why it diverge it TBH.

Revision history for this message
Sergio Durigan Junior (sergiodj) wrote :

Thanks, Lucas.

Maybe there's something that uupdate does to get rid of these files... Anyway, as I said above, it's not really important and shouldn't block this upload.

Therefore, LGTM, +1.

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

Thanks Sergio! Package uploaded:

Uploading haproxy_2.0.29-0ubuntu1.dsc
Uploading haproxy_2.0.29.orig.tar.gz
Uploading haproxy_2.0.29-0ubuntu1.debian.tar.xz
Uploading haproxy_2.0.29-0ubuntu1_source.buildinfo
Uploading haproxy_2.0.29-0ubuntu1_source.changes

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

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

review: Approve
cbc0ff8... by Lucas Kanashiro

Import upstream version 2.0.31 (LP: #2012557)

2f28b07... by Lucas Kanashiro

Remove patches applied by upstream

e6f5d97... by Lucas Kanashiro

changelog

95521a4... by Lucas Kanashiro

Refresh existing patches

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

Subscribers

People subscribed via source and target branches