mirror of
https://github.com/haproxy/haproxy.git
synced 2026-05-26 03:03:51 -04:00
Compare commits
118 commits
v3.4-dev12
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8c9aabd62 | ||
|
|
32fc35ef09 | ||
|
|
6bb8cb51e6 | ||
|
|
8fe8d5fbe3 | ||
|
|
b78b023d55 | ||
|
|
7d182a2ed5 | ||
|
|
c0e302fe79 | ||
|
|
478e7e52cb | ||
|
|
8e1d33a648 | ||
|
|
49d6306de3 | ||
|
|
01ebb668a4 | ||
|
|
340cc86efb | ||
|
|
f62d020140 | ||
|
|
bbef74fb21 | ||
|
|
608951844e | ||
|
|
f9088a5d75 | ||
|
|
007d5946b4 | ||
|
|
4db85fc53e | ||
|
|
41bb1c24f6 | ||
|
|
9091cfa617 | ||
|
|
57b526e022 | ||
|
|
2644f9ddf9 | ||
|
|
7cab3a3c3a | ||
|
|
04b9215a2e | ||
|
|
75f72c2eb9 | ||
|
|
1ed4ef6659 | ||
|
|
3fab21ea42 | ||
|
|
f9d4d659a4 | ||
|
|
c0aa91a202 | ||
|
|
e2c3cd9eb7 | ||
|
|
6717531053 | ||
|
|
812962d110 | ||
|
|
8fe8f78473 | ||
|
|
cdeb2aa4ef | ||
|
|
9e6e0fd149 | ||
|
|
e98595e4e5 | ||
|
|
413f6f9a1f | ||
|
|
3475a5bb9f | ||
|
|
050e06dd66 | ||
|
|
bcf768f157 | ||
|
|
897c5ddb8c | ||
|
|
f5477c8d45 | ||
|
|
b62ba7592a | ||
|
|
3e25104a9c | ||
|
|
482b6763a3 | ||
|
|
2a87629052 | ||
|
|
56e7f8ef31 | ||
|
|
99d48c3aec | ||
|
|
47a61eb86d | ||
|
|
b7c607e207 | ||
|
|
05e65489cb | ||
|
|
b9acb4415f | ||
|
|
3c35e7f137 | ||
|
|
d142c7f421 | ||
|
|
8dd31dcd07 | ||
|
|
f521581922 | ||
|
|
de3f245df0 | ||
|
|
e139dd90e3 | ||
|
|
89f3975acc | ||
|
|
d21ec4c707 | ||
|
|
e4adba6e64 | ||
|
|
33c8270903 | ||
|
|
ad3562fea1 | ||
|
|
641fe4f119 | ||
|
|
2d2980408f | ||
|
|
7004bb3b8c | ||
|
|
a59e6e5efd | ||
|
|
cb5d98c495 | ||
|
|
f2bf3483ba | ||
|
|
f2b152c95e | ||
|
|
e30bcfe6cd | ||
|
|
879c78c909 | ||
|
|
356f1ab5d7 | ||
|
|
86ffbaa0f5 | ||
|
|
6aab6d4e98 | ||
|
|
022681eca2 | ||
|
|
50354f929d | ||
|
|
6f6bf3fecc | ||
|
|
1279bd80e9 | ||
|
|
b74b5289c8 | ||
|
|
8dd49dfaba | ||
|
|
c53256adbc | ||
|
|
18c5cd6674 | ||
|
|
b786eaf1b1 | ||
|
|
307294b30a | ||
|
|
0284be5456 | ||
|
|
11bad01760 | ||
|
|
b59fe471a5 | ||
|
|
29b9da7821 | ||
|
|
d4a4be6c34 | ||
|
|
bbc41785d9 | ||
|
|
3b825d2745 | ||
|
|
3da2b63274 | ||
|
|
fdb569c2ea | ||
|
|
fd31df765f | ||
|
|
b44d60eb42 | ||
|
|
015933794e | ||
|
|
4519906c70 | ||
|
|
2f88b4bc4b | ||
|
|
9ebb00e673 | ||
|
|
3460626148 | ||
|
|
6cbcb4f9db | ||
|
|
677fdfe126 | ||
|
|
b15e9b1b29 | ||
|
|
0c8c9b1c2a | ||
|
|
bed842390f | ||
|
|
569f1e2f37 | ||
|
|
493dc352ad | ||
|
|
8aa99dfc74 | ||
|
|
5b468a0820 | ||
|
|
6c663a9374 | ||
|
|
2a43a1306b | ||
|
|
b6bd6f5b9a | ||
|
|
ace19fd638 | ||
|
|
bb5c18ab74 | ||
|
|
fefce297ab | ||
|
|
bcb4f9cd4a | ||
|
|
da4a4976d7 |
91 changed files with 1520 additions and 411 deletions
80
CHANGELOG
80
CHANGELOG
|
|
@ -1,6 +1,86 @@
|
||||||
ChangeLog :
|
ChangeLog :
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
2026/05/20 : 3.4-dev13
|
||||||
|
- BUG/MINOR: backend: correct parameter value validation in get_server_ph_post()
|
||||||
|
- BUG/MINOR: config/dns: properly fail on duplicate nameserver name detection
|
||||||
|
- BUG/MEDIUM: dns: fix long loops in additional records parse on name failure
|
||||||
|
- BUG/MEDIUM: resolvers: fix name compression pointer validation in resolv_read_name()
|
||||||
|
- BUG/MEDIUM: dns: fix memory leak of sockaddr in dns_session_init() error path
|
||||||
|
- CLEANUP: proxy: fix tiny mistakes in parse error messages
|
||||||
|
- CLEANUP: dns: fix misleading error messages in dns_stream_init()
|
||||||
|
- BUG/MINOR: server: better handling of OOM in srv_set_fqdn()
|
||||||
|
- BUG/MINOR: servers: use proper source of pool_conn_name in srv_settings_cpy()
|
||||||
|
- BUG/MEDIUM: server/cli: unlock server lock on failure in cli_parse_set_server
|
||||||
|
- BUG/MINOR: resolvers: fix dangling list pointer in resolvers_new() error paths
|
||||||
|
- BUG/MINOR: dns: fix dangling dgram pointer on dns_dgram_init() failure path
|
||||||
|
- BUG/MINOR: proxy: use proxy_drop() in parse_new_proxy() error path
|
||||||
|
- CLEANUP: resolvers: properly initialize the sample in resolv_action_do_resolve()
|
||||||
|
- BUG/MINOR: resolvers: report the expression error in the do-resolve() action parser
|
||||||
|
- BUG/MINOR: resolvers: fix leaked dgram and dns_ring struct in parse_resolve_conf()
|
||||||
|
- BUG/MINOR: resolvers: fix leaked fields on cfg_parse_resolvers() error paths
|
||||||
|
- BUG/MINOR: resolvers: fix missing task_idle destruction in resolvers_destroy()
|
||||||
|
- CLEANUP: proxy: fix duplicate declaration of cli_find_frontend in proxy.h
|
||||||
|
- CLEANUP: address a few typos and copy-paste errors in httpclient and dns
|
||||||
|
- DOC: internal: add a few rules about internal core principles
|
||||||
|
- BUG/MINOR: session/trace: use distinct flags for SESS_EV_END and _ERR
|
||||||
|
- CLEANUP: stick-table: uniformize the different action_inc_gpc*()
|
||||||
|
- REGTESTS: do not run quic/tls13_ssl_crt-list_filters in quic openssl compat mode
|
||||||
|
- REGTESTS: quic/issuers_chain_path: do not forget to enable QUIC compat mode
|
||||||
|
- BUG/MINOR: sock: store the connection error status
|
||||||
|
- BUG/MINOR: check: properly report errno in chk_report_conn_err()
|
||||||
|
- CLEANUP: tcpcheck: mention that we're a bit far for a sync errno
|
||||||
|
- BUG/MINOR: jwt: fix possible memory leak in convert_ecdsa_sig() error path
|
||||||
|
- CLEANUP: jwe: fix theoretical overflow in AAD length calculation
|
||||||
|
- DOC: config: further clarify that resolvers "default" exists
|
||||||
|
- MINOR: proxy: remove the experimental status on dynamic backends
|
||||||
|
- BUG/MEDIUM: limits: properly account for global.maxpipes in compute_ideal_maxconn()
|
||||||
|
- BUG/MINOR: jws: fix OpenSSL 3.0 version check from > to >=
|
||||||
|
- BUG/MINOR: jws: Add missing return value check (EVP_PKEY_get_bn_param)
|
||||||
|
- BUG/MINOR: server: Properly handle init-state value during haproxy startup
|
||||||
|
- BUG/MINOR: httpclient-cli: Destroy http-client context if failing to start it
|
||||||
|
- BUG/MEDIUM: h1: Skip all h2c values from Upgrade headers during parsing
|
||||||
|
- BUG/MINOR: h1: Don't mask websocket protocol if multiple protocols used
|
||||||
|
- MINOR: haterm: Don't init haterm master pipe if not used
|
||||||
|
- CLEANUP: haterm: Remove "(too old kernel)" from warning message during init
|
||||||
|
- BUG/MINOR: httpclient-cli: fix uninit variable in error label
|
||||||
|
- MINOR: mux: Rename the "token" from mux_proto_list to mux_proto
|
||||||
|
- MEDIUM: connections: Use both mux_proto and alpn to pick a mux
|
||||||
|
- MINOR: connection: define conn_select_mux_fe()
|
||||||
|
- MINOR: connection: define conn_select_mux_be()
|
||||||
|
- MINOR: connection/mux_quic: add MUX <init_xprt> field for QMux handshake
|
||||||
|
- MINOR: proxy/server: reject TCP ALPN h3 without experimental
|
||||||
|
- MEDIUM: ssl: allow h3/QMux negotiation without explicit proto
|
||||||
|
- BUG/MINOR: server: accept server IDs above 2^31 and clarify error message
|
||||||
|
- BUG/MINOR: backend: fix balance hash calculation when using hash-type none
|
||||||
|
- MINOR: server: support hash-key id32 for a cleaner distribution
|
||||||
|
- MINOR: backend: support hash-key guid for a stabler distribution
|
||||||
|
- MINOR: startup: support unprivileged chroot if possible
|
||||||
|
- MEDIUM: startup: add automatic chroot feature
|
||||||
|
- MINOR: h2: explain committed_extra_streams dec on h2_init() error
|
||||||
|
- OPTIM: h2: do not update committed streams if elasticity disabled
|
||||||
|
- MINOR: mux_quic: implement basic committed_extra_streams accounting
|
||||||
|
- MINOR: quic: use stream elasticity value for initial advertisement
|
||||||
|
- MINOR: mux_quic: define ms_bidi_rel QCC member
|
||||||
|
- MAJOR: mux_quic: support stream elasticity during connection lifetime
|
||||||
|
- BUG/MEDIUM: servers: Store the connection hash with the parameter cache
|
||||||
|
- BUG/MINOR: prevent conn leak in case of xprt_qmux init failure
|
||||||
|
- BUILD: traces: set a few __maybe_unused on vars used only for traces
|
||||||
|
- BUILD: traces: add USE_TRACE allowing to disable traces
|
||||||
|
- MINOR: startup: do not execute chroot() when "/"
|
||||||
|
- MEDIUM: startup: warn when chroot is not set for root
|
||||||
|
- BUG/MEDIUM: servers: Don't forget to set srv_hash when needed
|
||||||
|
- DOC: fix typo on QUIC stream.max-concurrent reference
|
||||||
|
- BUG/MINOR: mux_quic: do not exceed stream.max-concurrent on backend side
|
||||||
|
- BUG/MINOR: htx: Fix value of HTX_XFER_HDRS_ONLY flag
|
||||||
|
- MEDIUM: htx: Improve htx_xfer API to not count HTX meta-data
|
||||||
|
- BUG/MEDIUM: applet: Fix transfer of HTX data to the applet
|
||||||
|
- BUG/MEDIUM: htx: Alloc a chunk of right size in htx_replace_blk_value()
|
||||||
|
- MEDIUM: stick-tables: Avoid freeing elements while holding a lock
|
||||||
|
- MINOR: intops: add a multiply overflow detection for ulong and size_t
|
||||||
|
- CLEANUP: tree-wide: use array_size_or_fail() in array size for allocations
|
||||||
|
- DOC: update supported gcc and openssl versions in INSTALL
|
||||||
|
|
||||||
2026/05/13 : 3.4-dev12
|
2026/05/13 : 3.4-dev12
|
||||||
- SCRIPTS: announce-release: add a link to the OpenTelemetry filter
|
- SCRIPTS: announce-release: add a link to the OpenTelemetry filter
|
||||||
- BUG/MEDIUM: servers: Only requeue servers if they are up
|
- BUG/MEDIUM: servers: Only requeue servers if they are up
|
||||||
|
|
|
||||||
6
INSTALL
6
INSTALL
|
|
@ -111,12 +111,12 @@ HAProxy requires a working GCC or Clang toolchain and GNU make :
|
||||||
may want to retry with "gmake" which is the name commonly used for GNU make
|
may want to retry with "gmake" which is the name commonly used for GNU make
|
||||||
on BSD systems.
|
on BSD systems.
|
||||||
|
|
||||||
- GCC >= 4.7 (up to 15 tested). Older versions are no longer supported due to
|
- GCC >= 4.7 (up to 16 tested). Older versions are no longer supported due to
|
||||||
the latest mt_list update which only uses c11-like atomics. Newer versions
|
the latest mt_list update which only uses c11-like atomics. Newer versions
|
||||||
may sometimes break due to compiler regressions or behaviour changes. The
|
may sometimes break due to compiler regressions or behaviour changes. The
|
||||||
version shipped with your operating system is very likely to work with no
|
version shipped with your operating system is very likely to work with no
|
||||||
trouble. Clang >= 3.0 is also known to work as an alternative solution, and
|
trouble. Clang >= 3.0 is also known to work as an alternative solution, and
|
||||||
versions up to 19 were successfully tested. Recent versions may emit a bit
|
versions up to 21 were successfully tested. Recent versions may emit a bit
|
||||||
more warnings that are worth reporting as they may reveal real bugs. TCC
|
more warnings that are worth reporting as they may reveal real bugs. TCC
|
||||||
(https://repo.or.cz/tinycc.git) is also usable for developers but will not
|
(https://repo.or.cz/tinycc.git) is also usable for developers but will not
|
||||||
support threading and was found at least once to produce bad code in some
|
support threading and was found at least once to produce bad code in some
|
||||||
|
|
@ -237,7 +237,7 @@ to forcefully enable it using "USE_LIBCRYPT=1".
|
||||||
-----------------
|
-----------------
|
||||||
For SSL/TLS, it is necessary to use a cryptography library. HAProxy currently
|
For SSL/TLS, it is necessary to use a cryptography library. HAProxy currently
|
||||||
supports the OpenSSL library, and is known to build and work with branches
|
supports the OpenSSL library, and is known to build and work with branches
|
||||||
1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, and 3.0 to 3.6. It is recommended to use
|
1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, and 3.0 to 4.0. It is recommended to use
|
||||||
at least OpenSSL 1.1.1 to have support for all SSL keywords and configuration
|
at least OpenSSL 1.1.1 to have support for all SSL keywords and configuration
|
||||||
in HAProxy. OpenSSL follows a long-term support cycle similar to HAProxy's,
|
in HAProxy. OpenSSL follows a long-term support cycle similar to HAProxy's,
|
||||||
and each of the branches above receives its own fixes, without forcing you to
|
and each of the branches above receives its own fixes, without forcing you to
|
||||||
|
|
|
||||||
6
Makefile
6
Makefile
|
|
@ -44,6 +44,7 @@
|
||||||
# USE_CLOSEFROM : enable use of closefrom() on *bsd, solaris. Automatic.
|
# USE_CLOSEFROM : enable use of closefrom() on *bsd, solaris. Automatic.
|
||||||
# USE_PRCTL : enable use of prctl(). Automatic.
|
# USE_PRCTL : enable use of prctl(). Automatic.
|
||||||
# USE_PROCCTL : enable use of procctl(). Automatic.
|
# USE_PROCCTL : enable use of procctl(). Automatic.
|
||||||
|
# USE_TRACE : enable trace subsystem. Always on.
|
||||||
# USE_ZLIB : enable zlib library support and disable SLZ
|
# USE_ZLIB : enable zlib library support and disable SLZ
|
||||||
# USE_SLZ : enable slz library instead of zlib (default=enabled)
|
# USE_SLZ : enable slz library instead of zlib (default=enabled)
|
||||||
# USE_CPU_AFFINITY : enable pinning processes to CPU on Linux. Automatic.
|
# USE_CPU_AFFINITY : enable pinning processes to CPU on Linux. Automatic.
|
||||||
|
|
@ -343,7 +344,7 @@ use_opts = USE_EPOLL USE_KQUEUE USE_NETFILTER USE_POLL \
|
||||||
USE_TPROXY USE_LINUX_TPROXY USE_LINUX_CAP \
|
USE_TPROXY USE_LINUX_TPROXY USE_LINUX_CAP \
|
||||||
USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_ENGINE \
|
USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_ENGINE \
|
||||||
USE_GETADDRINFO USE_OPENSSL USE_OPENSSL_WOLFSSL USE_OPENSSL_AWSLC \
|
USE_GETADDRINFO USE_OPENSSL USE_OPENSSL_WOLFSSL USE_OPENSSL_AWSLC \
|
||||||
USE_ECH \
|
USE_ECH USE_TRACE \
|
||||||
USE_SSL USE_LUA USE_ACCEPT4 USE_CLOSEFROM USE_ZLIB USE_SLZ \
|
USE_SSL USE_LUA USE_ACCEPT4 USE_CLOSEFROM USE_ZLIB USE_SLZ \
|
||||||
USE_CPU_AFFINITY USE_TFO USE_NS USE_DL USE_RT USE_LIBATOMIC \
|
USE_CPU_AFFINITY USE_TFO USE_NS USE_DL USE_RT USE_LIBATOMIC \
|
||||||
USE_MATH USE_DEVICEATLAS USE_51DEGREES \
|
USE_MATH USE_DEVICEATLAS USE_51DEGREES \
|
||||||
|
|
@ -366,6 +367,9 @@ $(warn_unknown_options)
|
||||||
# on the make command line.
|
# on the make command line.
|
||||||
USE_POLL = default
|
USE_POLL = default
|
||||||
|
|
||||||
|
# traces are always enabled
|
||||||
|
USE_TRACE = default
|
||||||
|
|
||||||
# SLZ is always supported unless explicitly disabled by passing USE_SLZ=""
|
# SLZ is always supported unless explicitly disabled by passing USE_SLZ=""
|
||||||
# or disabled by enabling ZLIB using USE_ZLIB=1
|
# or disabled by enabling ZLIB using USE_ZLIB=1
|
||||||
ifeq ($(USE_ZLIB:0=),)
|
ifeq ($(USE_ZLIB:0=),)
|
||||||
|
|
|
||||||
2
VERDATE
2
VERDATE
|
|
@ -1,2 +1,2 @@
|
||||||
$Format:%ci$
|
$Format:%ci$
|
||||||
2026/05/13
|
2026/05/20
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
3.4-dev12
|
3.4-dev13
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Configuration Manual
|
Configuration Manual
|
||||||
----------------------
|
----------------------
|
||||||
version 3.4
|
version 3.4
|
||||||
2026/05/13
|
2026/05/20
|
||||||
|
|
||||||
|
|
||||||
This document covers the configuration language as implemented in the version
|
This document covers the configuration language as implemented in the version
|
||||||
|
|
@ -2126,13 +2126,28 @@ ca-base <dir>
|
||||||
directives. Absolute locations specified in "ca-file", "ca-verify-file" and
|
directives. Absolute locations specified in "ca-file", "ca-verify-file" and
|
||||||
"crl-file" prevail and ignore "ca-base".
|
"crl-file" prevail and ignore "ca-base".
|
||||||
|
|
||||||
chroot <jail dir>
|
chroot { <jail dir> | auto }
|
||||||
Changes current directory to <jail dir> and performs a chroot() there before
|
Changes current directory to <jail dir> and performs a chroot() there before
|
||||||
dropping privileges. This increases the security level in case an unknown
|
dropping privileges. This increases the security level in case an unknown
|
||||||
vulnerability would be exploited, since it would make it very hard for the
|
vulnerability would be exploited, since it would make it very hard for the
|
||||||
attacker to exploit the system. This only works when the process is started
|
attacker to exploit the system. It is important to ensure that <jail dir>
|
||||||
with superuser privileges. It is important to ensure that <jail_dir> is both
|
is both empty and non-writable to anyone. When the process is started with
|
||||||
empty and non-writable to anyone.
|
superuser privileges, the chroot() is performed directly. On Linux, when
|
||||||
|
started unprivileged, haproxy attempts to perform it from inside a new
|
||||||
|
user namespace created with unshare(CLONE_NEWUSER); if that mechanism is
|
||||||
|
unavailable the chroot() will fail with the usual error.
|
||||||
|
|
||||||
|
As a special case, <jail dir> may be set to "auto", in which case haproxy
|
||||||
|
creates an anonymous temporary directory, unlinks it, and chroots into it.
|
||||||
|
The resulting jail has no name in the filesystem and is empty and read-only,
|
||||||
|
removing the need to prepare a dedicated jail directory.
|
||||||
|
|
||||||
|
When starting with superuser privileges, a warning will be displayed if no
|
||||||
|
chroot is used, in order to encourage users to always use the mechanism. If
|
||||||
|
for any reason there is a compelling reason not to use chroot (e.g. access to
|
||||||
|
a server via a UNIX socket with an unconvenient path), it remains possible to
|
||||||
|
silence the warning by adding an explicit "chroot /", which has the benefit
|
||||||
|
of being visible in a configuration.
|
||||||
|
|
||||||
close-spread-time <time>
|
close-spread-time <time>
|
||||||
Define a time window during which idle connections and active connections
|
Define a time window during which idle connections and active connections
|
||||||
|
|
@ -3314,7 +3329,7 @@ setenv <name> <value>
|
||||||
the configuration file sees the new value. See also "presetenv", "resetenv",
|
the configuration file sees the new value. See also "presetenv", "resetenv",
|
||||||
and "unsetenv".
|
and "unsetenv".
|
||||||
|
|
||||||
shm-stats-file <name> [ EXPERIMENTAL ]
|
shm-stats-file <name>
|
||||||
When this directive is set, it enables the use of shared memory for storing
|
When this directive is set, it enables the use of shared memory for storing
|
||||||
stats counters. <name> is used as argument to shm_open() to open the shared
|
stats counters. <name> is used as argument to shm_open() to open the shared
|
||||||
memory at a unique location. It also means that the directive is only
|
memory at a unique location. It also means that the directive is only
|
||||||
|
|
@ -3330,7 +3345,7 @@ shm-stats-file <name> [ EXPERIMENTAL ]
|
||||||
|
|
||||||
See also "guid", "guid-prefix" and "shm-stats-file-max-objects"
|
See also "guid", "guid-prefix" and "shm-stats-file-max-objects"
|
||||||
|
|
||||||
shm-stats-file-max-objects <number> [ EXPERIMENTAL ]
|
shm-stats-file-max-objects <number>
|
||||||
This setting defines the maximum number of objects the shared memory used
|
This setting defines the maximum number of objects the shared memory used
|
||||||
for shared counters will be able to store per thread group. It is directly
|
for shared counters will be able to store per thread group. It is directly
|
||||||
related to the maximum memory size of the shm and is used to "premap" the
|
related to the maximum memory size of the shm and is used to "premap" the
|
||||||
|
|
@ -5306,17 +5321,26 @@ tune.quic.frontend.stream-data-ratio <0..100, in percent> (deprecated)
|
||||||
|
|
||||||
tune.quic.be.stream.max-concurrent <number>
|
tune.quic.be.stream.max-concurrent <number>
|
||||||
tune.quic.fe.stream.max-concurrent <number>
|
tune.quic.fe.stream.max-concurrent <number>
|
||||||
Sets the QUIC initial_max_streams_bidi transport parameter either on frontend
|
On frontend side, this is used as the value for the advertised
|
||||||
or backend side. This is the maximum number of bidirectional streams that the
|
initial_max_streams_bidi transport parameter. This is enforced as the maximum
|
||||||
remote peer will be authorized to open concurrently during the connection
|
number of bidirectional streams that the remote peer will be authorized to
|
||||||
lifetime. On frontend side, this limits the number of concurrent HTTP/3
|
open concurrently during the connection lifetime. This effectively limits the
|
||||||
client requests.
|
number of concurrent HTTP/3 client requests.
|
||||||
|
|
||||||
The default value is 100. Note that if you reduces it, it can restrict the
|
The default value is 100. Note that if you reduces it, it can restrict the
|
||||||
buffering capabilities of streams on receive, which would result in poor
|
buffering capabilities of streams on receive, which would result in poor
|
||||||
upload throughput. It can be corrected by increasing the QUIC stream rxbuf
|
upload throughput. It can be corrected by increasing the QUIC stream rxbuf
|
||||||
connection setting.
|
connection setting.
|
||||||
|
|
||||||
|
On backend side, this is enforced locally by haproxy to limit the number of
|
||||||
|
concurrent requests multiplexed over a single connection. This may be further
|
||||||
|
restricted by the peer flow control. It may be necessary to reduce the
|
||||||
|
default value of 100 to improve a site's responsiveness at the expense of a
|
||||||
|
higher number of opened backend connections. Similarly to the frontend side,
|
||||||
|
this setting also directly impacts the Rx buffering capability, this time
|
||||||
|
though limiting the HTTP download capacity. QUIC stream rxbuf setting can be
|
||||||
|
increased when dealing mostly with HTTP responses larger than "tune.bufsize".
|
||||||
|
|
||||||
See also: "tune.quic.be.stream.rxbuf", "tune.quic.fe.stream.rxbuf",
|
See also: "tune.quic.be.stream.rxbuf", "tune.quic.fe.stream.rxbuf",
|
||||||
"tune.quic.be.stream.data-ratio", "tune.quic.fe.stream.data-ratio"
|
"tune.quic.be.stream.data-ratio", "tune.quic.fe.stream.data-ratio"
|
||||||
|
|
||||||
|
|
@ -5719,6 +5743,16 @@ tune.streams-elasticity <number>
|
||||||
use lower values (120 to 200) to support 1.2 to 2 streams per connection on
|
use lower values (120 to 200) to support 1.2 to 2 streams per connection on
|
||||||
average at full load.
|
average at full load.
|
||||||
|
|
||||||
|
Contrary to HTTP/2, QUIC is capable to dynamically adjust the number of
|
||||||
|
concurrent streams during the connection lifetime. However, QUIC flow control
|
||||||
|
is stricter than HTTP/2, thus it is preferable when using it to specify
|
||||||
|
values big enough to prevent extra latency on the connection. There is also a
|
||||||
|
limitation for QUIC listeners with enabled 0-RTT. In this case, the initial
|
||||||
|
value advertised to the peer will ignore stream elasticity and instead rely
|
||||||
|
solely on the "tune.quic.fe.stream.max-concurrent" setting. However, the
|
||||||
|
stream elasticity principle will still be effective past this initial
|
||||||
|
annoucement during the connection lifetime.
|
||||||
|
|
||||||
Monitoring the total number of active streams on backends, including queues,
|
Monitoring the total number of active streams on backends, including queues,
|
||||||
provides a practical indicator of a sustainable target load and helps avoid
|
provides a practical indicator of a sustainable target load and helps avoid
|
||||||
over-provisioning.
|
over-provisioning.
|
||||||
|
|
@ -8260,7 +8294,10 @@ hash-type <method> <function> <modifier>
|
||||||
|
|
||||||
none don't hash the key, the key will be used as a hash, this can be
|
none don't hash the key, the key will be used as a hash, this can be
|
||||||
useful to manually hash the key using a converter for that purpose
|
useful to manually hash the key using a converter for that purpose
|
||||||
and let haproxy use the result directly.
|
and let haproxy use the result directly. The operation will
|
||||||
|
convert the key to a string if it is not already, and parse it as
|
||||||
|
an integer whose value will be used as the key. Some input key
|
||||||
|
types might not be relevant here (e.g. IP addresses).
|
||||||
|
|
||||||
<modifier> indicates an optional method applied after hashing the key :
|
<modifier> indicates an optional method applied after hashing the key :
|
||||||
|
|
||||||
|
|
@ -18847,6 +18884,21 @@ hash-key <key>
|
||||||
better only use values comprised between 1 and this value to
|
better only use values comprised between 1 and this value to
|
||||||
avoid overlap.
|
avoid overlap.
|
||||||
|
|
||||||
|
id32 The node keys will be derived from the server's numeric
|
||||||
|
identifier as set from "id" or which defaults to its position
|
||||||
|
in the server list, but the full 32 bits of the ID will be
|
||||||
|
used so that there is no collision. This one is not scaled
|
||||||
|
like "id" is, so it is recommended to either always use it
|
||||||
|
with a hash function (see "hash-key") or with explicitly
|
||||||
|
assigned ID values that are evenly distributed over the 32-bit
|
||||||
|
space.
|
||||||
|
|
||||||
|
guid The node keys will be derived from the server's guid, when
|
||||||
|
available, otherwise they will fall back on "id". The benefit
|
||||||
|
is that it does not depend on ordering at all, only on an
|
||||||
|
internal stable identifier that can be replicated across many
|
||||||
|
load balancers.
|
||||||
|
|
||||||
addr The node keys will be derived from the server's address, when
|
addr The node keys will be derived from the server's address, when
|
||||||
available, or else fall back on "id".
|
available, or else fall back on "id".
|
||||||
|
|
||||||
|
|
@ -18876,9 +18928,13 @@ healthcheck <name>
|
||||||
id <value>
|
id <value>
|
||||||
May be used in the following contexts: tcp, http, log
|
May be used in the following contexts: tcp, http, log
|
||||||
|
|
||||||
Set a persistent ID for the server. This ID must be positive and unique for
|
Set a persistent ID for the server. This ID must be a 32-bit positive number
|
||||||
the proxy. An unused ID will automatically be assigned if unset. The first
|
and unique for the proxy. An unused ID will automatically be assigned if
|
||||||
assigned value will be 1. This ID is currently only returned in statistics.
|
unset. The first assigned value will be 1. This ID is currently only returned
|
||||||
|
in statistics, and is used to place LB nodes when using consistent hash
|
||||||
|
algorithms when "hash-key" is set to "id" (the default). In this case, only
|
||||||
|
the 28 lowest bits of the value are used (i.e. (id % 268435356)), so better
|
||||||
|
only use values comprised between 1 and this value to avoid overlap.
|
||||||
|
|
||||||
idle-ping <delay>
|
idle-ping <delay>
|
||||||
May be used in the following contexts: tcp, http, log
|
May be used in the following contexts: tcp, http, log
|
||||||
|
|
@ -18972,7 +19028,7 @@ downinter <delay>
|
||||||
"inter" setting will have a very limited effect as it will not be able to
|
"inter" setting will have a very limited effect as it will not be able to
|
||||||
reduce the time spent in the queue.
|
reduce the time spent in the queue.
|
||||||
|
|
||||||
init-state { fully-up | up | down | fully-down }
|
init-state { fully-up | up | down | fully-down | none }
|
||||||
May be used in the following contexts: tcp, http
|
May be used in the following contexts: tcp, http
|
||||||
|
|
||||||
May be used in sections : defaults | frontend | listen | backend
|
May be used in sections : defaults | frontend | listen | backend
|
||||||
|
|
@ -18980,20 +19036,25 @@ init-state { fully-up | up | down | fully-down }
|
||||||
|
|
||||||
The "init-state" option sets the initial state of the server:
|
The "init-state" option sets the initial state of the server:
|
||||||
- when set to 'fully-up', the server is considered immediately available
|
- when set to 'fully-up', the server is considered immediately available
|
||||||
and can turn to the DOWN state when ALL health checks fail.
|
and, if health checks are enabled for this server, it will be turned to
|
||||||
- when set to 'up' (the default), the server is considered immediately
|
the DOWN state when ALL health checks fail.
|
||||||
available and will initiate a health check that can turn it to the DOWN
|
- when set to 'up', the server is considered immediately available and, if
|
||||||
state immediately if it fails.
|
health checks are enabled for this server, it will be turned to the DOWN
|
||||||
- when set to 'down', the server initially is considered unavailable and
|
state immediately if the next health check fails.
|
||||||
will initiate a health check that can turn it to the UP state immediately
|
- when set to 'down', the server initially is considered unavailable and,
|
||||||
if it succeeds.
|
if health checks are enabled for this server, it can be turned to the UP
|
||||||
|
state if the next health check succeeds.
|
||||||
- when set to 'fully-down', the server is initially considered unavailable
|
- when set to 'fully-down', the server is initially considered unavailable
|
||||||
and can turn to the UP state when ALL health checks succeed.
|
and, if health checks are enabled for this server, it will turned to the
|
||||||
|
UP state when ALL health checks succeed.
|
||||||
|
- when set to 'none' (the default value), init-state management is
|
||||||
|
disabled. It can be used to restore the default behavior when this
|
||||||
|
parameter was inherited from a 'default-server' directive.
|
||||||
|
|
||||||
The server's init-state is considered when the HAProxy instance is
|
The server's init-state is considered when the HAProxy instance is
|
||||||
(re)started, a new server is detected (for example via service discovery /
|
(re)started, a new server is detected (for example via service discovery /
|
||||||
DNS resolution), a dynamic server is inlived, a server exits maintenance,
|
DNS resolution), a dynamic server is inlived, a server exits maintenance,
|
||||||
etc.
|
etc. This directive cannot be used when the server is tracking another one.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
# pass client traffic ONLY to Redis "master" node
|
# pass client traffic ONLY to Redis "master" node
|
||||||
|
|
@ -20189,7 +20250,11 @@ a cache of previous answers, an answer will be considered obsolete after
|
||||||
|
|
||||||
|
|
||||||
resolvers <resolvers id>
|
resolvers <resolvers id>
|
||||||
Creates a new name server list labeled <resolvers id>
|
Creates a new name server list labeled <resolvers id>. As mentioned above,
|
||||||
|
the special name "default" always exists and will be automatically created if
|
||||||
|
not explicitly declared; this will be the one internal services such as
|
||||||
|
httpclient rely on. Declaring a "default" entry will affect how such services
|
||||||
|
perform their name resolution.
|
||||||
|
|
||||||
A resolvers section accept the following parameters:
|
A resolvers section accept the following parameters:
|
||||||
|
|
||||||
|
|
|
||||||
229
doc/internals/core-principles.txt
Normal file
229
doc/internals/core-principles.txt
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
HAPROXY CORE PRINCIPLES
|
||||||
|
|
||||||
|
0. RULE ZERO: EXCEPTIONS AND JUSTIFICATION
|
||||||
|
- These rules are mandatory; violations are bugs unless explicitly justified.
|
||||||
|
- A violation is acceptable if accompanied by a comment explaining WHY the
|
||||||
|
standard approach was insufficient (e.g., "Performance-critical bypass").
|
||||||
|
- Reviews should flag unjustified violations but accept commented ones.
|
||||||
|
|
||||||
|
1. PROJECT ORGANIZATION
|
||||||
|
- header files all under "include/", and split between haproxy/<file>-t.h for
|
||||||
|
type definitions (types, enums, structures), and haproxy/<file>.h for static
|
||||||
|
definitions and exported symbols. A few imported libs under include/import.
|
||||||
|
- C source files in src/.
|
||||||
|
- some API doc in doc/internals/api/ (not always up to date, check date or
|
||||||
|
version at the top).
|
||||||
|
|
||||||
|
2. ENVIRONMENT AND DATA TYPES
|
||||||
|
- The project targets 32/64-bit POSIX systems (little or big endian).
|
||||||
|
- Char is signed or unsigned 8-bit, short signed 16-bit, int signed 32-bit.
|
||||||
|
- Long and pointers always match the native word size. Long long is 64-bit.
|
||||||
|
- Aliases: uchar (unsigned char), uint (unsigned int), ulong (unsigned long),
|
||||||
|
ushort (unsigned short), ullong (unsigned long long), llong (long long),
|
||||||
|
schar (signed char).
|
||||||
|
- size_t always same size as long but often declared as uint on 32-bit and
|
||||||
|
ulong on 64-bit. Do not use in printf() without a cast (ulong with "%lu").
|
||||||
|
- Main platforms are x86_64 and aarch64 with high thread counts (>=64).
|
||||||
|
- Unaligned accesses are permitted for archs that support them; portable
|
||||||
|
wrappers in net_helper.h (read_u32(), write_u32() etc).
|
||||||
|
- signed integer wrapping well-defined via -fwrapv.
|
||||||
|
- arch-specific asm() statements OK as long as equivalent C-code exists for
|
||||||
|
generic archs.
|
||||||
|
- Pointer arithmetics used a lot via container_of(), offset_of(), and void*
|
||||||
|
casts.
|
||||||
|
- Floating point not used.
|
||||||
|
|
||||||
|
3. MEMORY MANAGEMENT AND POOLS
|
||||||
|
- Pools are used for runtime allocation; malloc/free are for boot code only.
|
||||||
|
- pool_alloc() semantics match malloc(); the return must always be tested.
|
||||||
|
- pool_alloc() and malloc() are not interchangeable / compatible.
|
||||||
|
- pool_free() semantics match free(); it is a no-op on NULL.
|
||||||
|
- pool_free() makes the pointer invalid immediately; it must not be touched
|
||||||
|
or passed to pool_free() again.
|
||||||
|
- Memory allocated from one pool must be released to the same pool.
|
||||||
|
- ha_free() calls free() and sets the pointer to NULL before returning.
|
||||||
|
- my_realloc2() frees the original pointer if the allocation fails.
|
||||||
|
- never leave dangling pointers in structs after free().
|
||||||
|
|
||||||
|
4. BUFFER INVARIANTS (struct buffer)
|
||||||
|
- Buffers are 4-word inline structs used for data in transit (wrapping,
|
||||||
|
sliding window).
|
||||||
|
- Members: area (storage), size (capacity), head (offset), data (count).
|
||||||
|
- The area pointer is allowed to be NULL when size is zero.
|
||||||
|
- always true: 0<=data<=size; always true when size>0: 0<=head<size.
|
||||||
|
- contents start at <head>, for <data> bytes, and may wrap at the end of the
|
||||||
|
storage area (area+size).
|
||||||
|
- API (b_*, in buf.h and dynbuf.h) supports empty or unallocated buffers.
|
||||||
|
- idempotent functions b_alloc() and b_free() use pools to manage the
|
||||||
|
storage area and check <size> to know if alloc/free still needed.
|
||||||
|
- a non-contiguous version exists (ncbuf, ncbmbuf), allowing holes anywhere
|
||||||
|
in data. The former mandates holes of at least 8 bytes. The second relies
|
||||||
|
on a bitmap of populated places.
|
||||||
|
- another string API exists, "ist", representing a pointer and a length in a
|
||||||
|
struct that is returned by inline functions and macros. It is described in
|
||||||
|
doc/internals/api/ist.txt
|
||||||
|
- buffers can switch to and from HTX, which is an internal representation of
|
||||||
|
HTTP elements, with an API supporting header addition/modification/removal,
|
||||||
|
start-line manipulation, data appending/consumption etc. HTX functions are
|
||||||
|
all prefixed with "htx_". Between htx_from_buf() and htx_to_buf(), only the
|
||||||
|
HTX API may be used, not the b_* API.
|
||||||
|
|
||||||
|
5. DATA MANIPULATION (CHUNKS, TRASH, LISTS, TREES)
|
||||||
|
- Chunks use the buffer API but are NOT allowed to wrap.
|
||||||
|
- Chunks are used for linear operations like chunk_printf().
|
||||||
|
- Trash is a thread-local temporary buffer; scope stays within the caller.
|
||||||
|
- trash always the same size as a buffer (global.tune.bufsize).
|
||||||
|
- get_trash_chunk() provides up to 3 rotating thread-local trash chunks (with
|
||||||
|
a scope spanning from the call to the next function call).
|
||||||
|
- For longer lived trash chunks, alloc_trash_chunk() is available but must be
|
||||||
|
released using free_trash_chunk() on leaving.
|
||||||
|
- standard doubly-linked lists (struct list) are provided via macros LIST_*.
|
||||||
|
- LIST_INIT() must be used on new heads and elements. LIST_DELETE() only
|
||||||
|
removes the element and does not reinitialize it, so the idempotent
|
||||||
|
LIST_DEL_INIT() is generally preferred. Iterators like list_for_each_* are
|
||||||
|
available, some safe against item removal. See doc/internals/api/list.txt
|
||||||
|
for details (grep -i "^list_" to list available macros).
|
||||||
|
- thread-safe doubly-linked lists (struct mt_list) are provided via macros
|
||||||
|
mt_list_*. They work like lists and use compatible storage, though they may
|
||||||
|
not be mixed. See doc/internals/api/mt_list.txt (grep -i "^mt_list_" to
|
||||||
|
list available operations).
|
||||||
|
- elastic binary trees (ebtree) are used for fast access (O(logN) operations,
|
||||||
|
O(1) deletion). Idempotent deletion. Main functions are lookup, insert,
|
||||||
|
delete, first, next, with type-based prefix eb{32,64,st,mb,pt}_*().
|
||||||
|
- compact elastic binary trees (cebtree) are used for read-mostly focusing on
|
||||||
|
space savings (O(logN) operations, but higher cost than ebtree). Same ops
|
||||||
|
as ebtree, with type-based prefix ceb{32,u32,64,u64,s,is}_*.
|
||||||
|
|
||||||
|
6. THREAD SYNCHRONIZATION
|
||||||
|
- Threads are started at boot (one per CPU) and persist for the process life,
|
||||||
|
arranged in thread groups (tg) by cache locality.
|
||||||
|
- Each thread has its own polling loop and scheduler. Total parallelism.
|
||||||
|
- thread_isolate()/thread_release() for total thread isolation (very heavy).
|
||||||
|
- "tid" always current thread number, "th_ctx" always current thread's context,
|
||||||
|
"ti" current thread info.
|
||||||
|
- "tgid" always current tg number, "tg_ctx" current tg context.
|
||||||
|
- HA_ATOMIC_* for atomic operations on integers and pointers (includes load
|
||||||
|
and store). DWCAS available on some platforms but requires an equivalent
|
||||||
|
for other ones.
|
||||||
|
- The _HA_ATOMIC_* version (leading underscore) do not use barriers so these
|
||||||
|
must be explicit (__ha_barrier_*).
|
||||||
|
- Atomic loops must use CPU relaxation or exponential back-off.
|
||||||
|
- For multiple changes at once, threads may use spinlocks (HA_SPIN_LOCK()/
|
||||||
|
HA_SPIN_UNLOCK/HA_SPIN_TRYLOCK), and upgradable RW locks (HA_RWLOCK_*) if
|
||||||
|
read accesses dominate.
|
||||||
|
- No sleeping locks (mutex etc), only spinning/rwlocks/atomic loops.
|
||||||
|
|
||||||
|
7. SCHEDULING AND LATENCY
|
||||||
|
- Latency is critical.
|
||||||
|
- No runtime filesystem access, no blocking calls, no long loops.
|
||||||
|
- Complex processing must be split into small steps; the task must yield.
|
||||||
|
- CPUs are not dedicated to haproxy, high risk of a thread being interrupted
|
||||||
|
by another process if it works too long, catastrophic if it happens with a
|
||||||
|
lock held.
|
||||||
|
- A watchdog kills the process if a task hogs a CPU for > few milliseconds.
|
||||||
|
- Tasks vs Tasklets: Tasks have tree storage (rq) and timers (wq); tasklets
|
||||||
|
use list elements instead of rq and are smaller (no wq). Only task.c/h may
|
||||||
|
distinguish rq vs list access.
|
||||||
|
- Tasks are aliased to tasklet while they are running (hence why some
|
||||||
|
functions cast task to tasklets and conversely to access certain fields).
|
||||||
|
- inter-thread task/tasklet wakeups always safe using the task_* API.
|
||||||
|
- task/tasklet->state field must always be accessed atomically.
|
||||||
|
|
||||||
|
8. ARCHITECTURAL LAYERS (MUX AND STREAMS)
|
||||||
|
- Naming: Lower layer (multiplexed), attached to the connection uses suffix
|
||||||
|
'c' (h1c, h2c, qcc, muxc); Upper layer (demultiplexed/application, often a
|
||||||
|
stream) uses suffix 's' (h1s, h2s, qcs, muxs).
|
||||||
|
- Application layer stream (struct stream) has two stream connectors (stconn):
|
||||||
|
front (scf) and back (scb). Responsible for processing requests/responses,
|
||||||
|
deciding which server to route it, finding a backend connection or creating
|
||||||
|
one, and exchanging data between the two sides.
|
||||||
|
- Stream connectors link to a muxs or applet via a stream endpoint descriptor
|
||||||
|
(sedesc/sd), and exchange data via buffers, which for an HTTP muxs are HTX
|
||||||
|
buffers containing HTX blocks.
|
||||||
|
- The sd carries the shared context between layers.
|
||||||
|
- When a stream detaches from a mux, a new sd is allocated for the stream and
|
||||||
|
the mux keeps its previous sd: stconn and muxs both always have a valid sd.
|
||||||
|
- Front connections/streams are tied to the creator thread forever.
|
||||||
|
- Idle back connections can be stolen via mux->takeover(), but become
|
||||||
|
thread-bound once a stream attaches. => all streams of a mux are on the
|
||||||
|
same thread.
|
||||||
|
- session vs connection vs stream: connection is transport; session lasts for
|
||||||
|
the client connection's life; stream are request/response pairs.
|
||||||
|
- applets carry a context specific to the service being executed or the CLI
|
||||||
|
command in appctx->svcctx, and this one is always zeroed before the handler
|
||||||
|
is first called.
|
||||||
|
|
||||||
|
9. FUNCTION RETURN CONVENTIONS
|
||||||
|
- Boolean style: Functions named as actions/sentences return 0 (failure) or
|
||||||
|
non-zero (success).
|
||||||
|
- Integer style: some syscall-like functions return <0 (error) or >=0 (success).
|
||||||
|
- Tri-state style, e.g. counts: <0 (error), 0 (no progress), >0 (success).
|
||||||
|
|
||||||
|
10. DIAGNOSTICS AND SAFETY
|
||||||
|
- When DEBUG_STRICT is set, ABORT_NOW() crashes the program immediately, and
|
||||||
|
BUG_ON(cond[,msg]) crashes the program if the condition is true.
|
||||||
|
- COUNT_IF() / CHECK_IF() only track if a condition occurs (non-fatal).
|
||||||
|
- Glitches are counters for uncommon events used to detect hostile behavior.
|
||||||
|
- strcpy(), strcat() and sprintf() are totally forbidden (the program will
|
||||||
|
not build).
|
||||||
|
|
||||||
|
11. BASIC CODING STYLE
|
||||||
|
- Linux Kernel-like, but uses tabs for indent, spaces for alignment. Function
|
||||||
|
definitions have their opening brace on a new line, never on the same line.
|
||||||
|
- All local variables must be declared at the beginning of the function
|
||||||
|
block, before any executable statements (gnu89-like).
|
||||||
|
- Avoid variable shadowing in code blocks.
|
||||||
|
- Beware of local static and global variables.
|
||||||
|
- Use const arguments whenever possible.
|
||||||
|
- Avoid static storage when persistence is not needed.
|
||||||
|
- Macros in uppercase unless they're used to wrap functions which then get a
|
||||||
|
leading underscore.
|
||||||
|
- Explicitly compare functions returning non-zero with 0 (e.g. strcmp) unless
|
||||||
|
they explicitly return a boolean (e.g. isalnum) or a pointer (e.g. strchr).
|
||||||
|
- Unsigned int comparisons to zero never use >0 but !=0 to avoid signedness
|
||||||
|
mistakes.
|
||||||
|
- turn non-zero integer to boolean using "!" or "!!".
|
||||||
|
|
||||||
|
12. BUILD AND TEST
|
||||||
|
- Preferred build command:
|
||||||
|
$ make -j$(nproc) TARGET=linux-glibc OPT_CFLAGS='-std=gnu89 -Os' \
|
||||||
|
USE_OPENSSL=1 USE_QUIC_OPENSSL_COMPAT=1 USE_QUIC=1 USE_LUA=1
|
||||||
|
- Individual files can be tested by passing src/file.o as a make argument.
|
||||||
|
- Compiler warnings are not permitted for new code.
|
||||||
|
|
||||||
|
13. COMMIT MESSAGES AND DOCUMENTATION
|
||||||
|
- Commit messages must follow the project's strict format below. Do not try
|
||||||
|
to learn better from previous commits, which might be wrong during reviews.
|
||||||
|
- Structure: <TAG>: <location>: <subject> (max ~70 chars), then blank line,
|
||||||
|
then description.
|
||||||
|
- Tags:
|
||||||
|
- CLEANUP: spelling fixes, refactoring, no new code nor functional change.
|
||||||
|
- MINOR: new feature or low-impact change, may be backported if needed.
|
||||||
|
- MEDIUM: new feature or change with moderate severity/impact/risk.
|
||||||
|
- MAJOR: new feature or change with important severity/impact/risk.
|
||||||
|
- OPTIM: Performance improvements, may always be reverted if it breaks.
|
||||||
|
- DOC: Documentation updates or fixes.
|
||||||
|
- BUG/<severity>: Fixes a bug. Specify if regression or long-standing.
|
||||||
|
Valid severities are MINOR (low impact), MEDIUM (perf/stability risk
|
||||||
|
in uncommon configs, MAJOR (most configs), CRITICAL (stability risk
|
||||||
|
without workaround).
|
||||||
|
- Regressions: Find original commit via `git blame`; designate using
|
||||||
|
`git log -1 --format='%h ("%s")'` and version via `git describe --tags`.
|
||||||
|
- Location: subsystem (stream, tasks, mux-h2, qpack etc).
|
||||||
|
- Description: Explain technical "WHY", "HOW", and technical impact. Explain
|
||||||
|
how to trigger the bug for developer testing.
|
||||||
|
- Backports: only for fixes, mention versions ("Must be backported to 3.0").
|
||||||
|
- Style: No generic messages like "fix(xxx): blah". Be technically precise.
|
||||||
|
- Do not mix spelling fixes in comments (not important) with other changes.
|
||||||
|
However it's preferred to have a single commit for many typo fixes at once.
|
||||||
|
- Spelling mistakes in user-visible parts (doc, logs, traces, error messages)
|
||||||
|
must be in their own commit (may need backport).
|
||||||
|
- One commit per bug.
|
||||||
|
- Example:
|
||||||
|
BUG/MEDIUM: sample: fix null pointer dereference in h1_parse_line
|
||||||
|
|
||||||
|
When parsing malformed headers, the line buffer was not initialized.
|
||||||
|
This caused a crash on certain edge cases. Let's fix this by always
|
||||||
|
initializing the line buffer when first calling the parser. This was
|
||||||
|
brought by commit 04c9e8f5 ("MINOR: add h1_parse_line") in latest -dev
|
||||||
|
so no backport is needed.
|
||||||
|
|
@ -179,6 +179,8 @@ enum {
|
||||||
/* below we have all handshake flags grouped into one */
|
/* below we have all handshake flags grouped into one */
|
||||||
CO_FL_HANDSHAKE = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
|
CO_FL_HANDSHAKE = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
|
||||||
CO_FL_WAIT_XPRT = CO_FL_WAIT_L4_CONN | CO_FL_HANDSHAKE | CO_FL_WAIT_L6_CONN,
|
CO_FL_WAIT_XPRT = CO_FL_WAIT_L4_CONN | CO_FL_HANDSHAKE | CO_FL_WAIT_L6_CONN,
|
||||||
|
/* handshake running on top of a layer6 */
|
||||||
|
CO_FL_WAIT_XPRT_L6 = CO_FL_QMUX_SEND | CO_FL_QMUX_RECV,
|
||||||
|
|
||||||
CO_FL_SSL_WAIT_HS = 0x08000000, /* wait for an SSL handshake to complete */
|
CO_FL_SSL_WAIT_HS = 0x08000000, /* wait for an SSL handshake to complete */
|
||||||
|
|
||||||
|
|
@ -673,11 +675,12 @@ struct connection {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mux_proto_list {
|
struct mux_proto_list {
|
||||||
const struct ist token; /* token name and length. Empty is catch-all */
|
const struct ist mux_proto; /* Mux protocol, to be used with the "proto" directive */
|
||||||
enum proto_proxy_mode mode;
|
enum proto_proxy_mode mode;
|
||||||
enum proto_proxy_side side;
|
enum proto_proxy_side side;
|
||||||
const struct mux_ops *mux;
|
const struct mux_ops *mux;
|
||||||
const char *alpn; /* Default alpn to set by default when the mux protocol is forced (optional, in binary form) */
|
const char *alpn; /* Default alpn to set by default when the mux protocol is forced (optional, in binary form) */
|
||||||
|
int init_xprt;
|
||||||
struct list list;
|
struct list list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,10 @@ int conn_create_mux(struct connection *conn, int *closed_connection);
|
||||||
int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake);
|
int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake);
|
||||||
int conn_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
|
int conn_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
|
||||||
struct ist mux_proto, int mode);
|
struct ist mux_proto, int mode);
|
||||||
|
const struct mux_proto_list *conn_select_mux_fe(const struct connection *conn);
|
||||||
int conn_install_mux_fe(struct connection *conn, void *ctx);
|
int conn_install_mux_fe(struct connection *conn, void *ctx);
|
||||||
|
|
||||||
|
const struct mux_proto_list *conn_select_mux_be(const struct connection *conn);
|
||||||
int conn_install_mux_be(struct connection *conn, void *ctx, struct session *sess,
|
int conn_install_mux_be(struct connection *conn, void *ctx, struct session *sess,
|
||||||
const struct mux_ops *force_mux_ops);
|
const struct mux_ops *force_mux_ops);
|
||||||
int conn_install_mux_chk(struct connection *conn, void *ctx, struct session *sess);
|
int conn_install_mux_chk(struct connection *conn, void *ctx, struct session *sess);
|
||||||
|
|
@ -111,6 +114,7 @@ int conn_reverse(struct connection *conn);
|
||||||
const char *conn_err_code_name(struct connection *c);
|
const char *conn_err_code_name(struct connection *c);
|
||||||
const char *conn_err_code_str(struct connection *c);
|
const char *conn_err_code_str(struct connection *c);
|
||||||
int xprt_add_hs(struct connection *conn);
|
int xprt_add_hs(struct connection *conn);
|
||||||
|
int xprt_add_l6hs(struct connection *conn, int xprt);
|
||||||
void register_mux_proto(struct mux_proto_list *list);
|
void register_mux_proto(struct mux_proto_list *list);
|
||||||
|
|
||||||
static inline void conn_report_term_evt(struct connection *conn, enum term_event_loc loc, unsigned char type);
|
static inline void conn_report_term_evt(struct connection *conn, enum term_event_loc loc, unsigned char type);
|
||||||
|
|
@ -649,7 +653,7 @@ static inline struct mux_proto_list *get_mux_proto(const struct ist proto)
|
||||||
struct mux_proto_list *item;
|
struct mux_proto_list *item;
|
||||||
|
|
||||||
list_for_each_entry(item, &mux_proto_list.list, list) {
|
list_for_each_entry(item, &mux_proto_list.list, list) {
|
||||||
if (isteq(proto, item->token))
|
if (isteq(proto, item->mux_proto))
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -668,6 +672,7 @@ void list_mux_proto(FILE *out);
|
||||||
*/
|
*/
|
||||||
static inline const struct mux_proto_list *conn_get_best_mux_entry(
|
static inline const struct mux_proto_list *conn_get_best_mux_entry(
|
||||||
const struct ist mux_proto,
|
const struct ist mux_proto,
|
||||||
|
const struct ist alpn,
|
||||||
int proto_side, int proto_is_quic, int proto_mode)
|
int proto_side, int proto_is_quic, int proto_mode)
|
||||||
{
|
{
|
||||||
struct mux_proto_list *item;
|
struct mux_proto_list *item;
|
||||||
|
|
@ -676,10 +681,14 @@ static inline const struct mux_proto_list *conn_get_best_mux_entry(
|
||||||
list_for_each_entry(item, &mux_proto_list.list, list) {
|
list_for_each_entry(item, &mux_proto_list.list, list) {
|
||||||
if (!(item->side & proto_side) || !(item->mode & proto_mode) || ((proto_is_quic != 0) != ((item->mux->flags & MX_FL_FRAMED) != 0)))
|
if (!(item->side & proto_side) || !(item->mode & proto_mode) || ((proto_is_quic != 0) != ((item->mux->flags & MX_FL_FRAMED) != 0)))
|
||||||
continue;
|
continue;
|
||||||
if (istlen(mux_proto) && isteq(mux_proto, item->token)) {
|
if (istlen(mux_proto) && isteq(mux_proto, item->mux_proto)) {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
else if (!istlen(item->token)) {
|
else if (istlen(alpn) && item->alpn &&
|
||||||
|
strlen(item->alpn) == istlen(alpn) + 1 &&
|
||||||
|
!memcmp(alpn.ptr, item->alpn + 1, istlen(alpn)))
|
||||||
|
return item;
|
||||||
|
else if (!istlen(item->mux_proto)) {
|
||||||
if (!fallback || (item->mode == proto_mode && fallback->mode != proto_mode))
|
if (!fallback || (item->mode == proto_mode && fallback->mode != proto_mode))
|
||||||
fallback = item;
|
fallback = item;
|
||||||
}
|
}
|
||||||
|
|
@ -696,11 +705,12 @@ static inline const struct mux_proto_list *conn_get_best_mux_entry(
|
||||||
*/
|
*/
|
||||||
static inline const struct mux_ops *conn_get_best_mux(struct connection *conn,
|
static inline const struct mux_ops *conn_get_best_mux(struct connection *conn,
|
||||||
const struct ist mux_proto,
|
const struct ist mux_proto,
|
||||||
|
const struct ist alpn,
|
||||||
int proto_side, int proto_mode)
|
int proto_side, int proto_mode)
|
||||||
{
|
{
|
||||||
const struct mux_proto_list *item;
|
const struct mux_proto_list *item;
|
||||||
|
|
||||||
item = conn_get_best_mux_entry(mux_proto, proto_side, proto_is_quic(conn->ctrl), proto_mode);
|
item = conn_get_best_mux_entry(mux_proto, alpn, proto_side, proto_is_quic(conn->ctrl), proto_mode);
|
||||||
|
|
||||||
return item ? item->mux : NULL;
|
return item ? item->mux : NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ enum h1m_state {
|
||||||
#define H1_MF_UPG_WEBSOCKET 0x00008000 // Set for a Websocket upgrade handshake
|
#define H1_MF_UPG_WEBSOCKET 0x00008000 // Set for a Websocket upgrade handshake
|
||||||
#define H1_MF_TE_CHUNKED 0x00010000 // T-E "chunked"
|
#define H1_MF_TE_CHUNKED 0x00010000 // T-E "chunked"
|
||||||
#define H1_MF_TE_OTHER 0x00020000 // T-E other than supported ones found (only "chunked" is supported for now)
|
#define H1_MF_TE_OTHER 0x00020000 // T-E other than supported ones found (only "chunked" is supported for now)
|
||||||
#define H1_MF_UPG_H2C 0x00040000 // "h2c" or "h2" used as upgrade token
|
/* unused: 0x00040000 */
|
||||||
#define H1_MF_NOT_HTTP 0x00080000 // Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted)
|
#define H1_MF_NOT_HTTP 0x00080000 // Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted)
|
||||||
/* Mask to use to reset H1M flags when we restart headers parsing.
|
/* Mask to use to reset H1M flags when we restart headers parsing.
|
||||||
*
|
*
|
||||||
|
|
@ -160,7 +160,7 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
||||||
|
|
||||||
int h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value);
|
int h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value);
|
||||||
void h1_parse_connection_header(struct h1m *h1m, struct ist *value);
|
void h1_parse_connection_header(struct h1m *h1m, struct ist *value);
|
||||||
void h1_parse_upgrade_header(struct h1m *h1m, struct ist value);
|
void h1_parse_upgrade_header(struct h1m *h1m, struct ist *value);
|
||||||
|
|
||||||
void h1_generate_random_ws_input_key(char key_out[25]);
|
void h1_generate_random_ws_input_key(char key_out[25]);
|
||||||
void h1_calculate_ws_output_key(const char *key, char *result);
|
void h1_calculate_ws_output_key(const char *key, char *result);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ struct httpclient {
|
||||||
struct buffer buf; /* input buffer, raw HTTP */
|
struct buffer buf; /* input buffer, raw HTTP */
|
||||||
} res;
|
} res;
|
||||||
struct {
|
struct {
|
||||||
/* callbacks used to send the request, */
|
/* callbacks used to send the request, */
|
||||||
void (*req_payload)(struct httpclient *hc); /* send a payload */
|
void (*req_payload)(struct httpclient *hc); /* send a payload */
|
||||||
|
|
||||||
/* callbacks used to receive the response, if not set, the IO
|
/* callbacks used to receive the response, if not set, the IO
|
||||||
|
|
@ -28,7 +28,7 @@ struct httpclient {
|
||||||
void (*res_end)(struct httpclient *hc); /* end of the response */
|
void (*res_end)(struct httpclient *hc); /* end of the response */
|
||||||
} ops;
|
} ops;
|
||||||
struct sockaddr_storage *dst; /* destination address */
|
struct sockaddr_storage *dst; /* destination address */
|
||||||
struct appctx *appctx; /* HTTPclient appctx */
|
struct appctx *appctx; /* HTTP client appctx */
|
||||||
int timeout_server; /* server timeout in ms */
|
int timeout_server; /* server timeout in ms */
|
||||||
void *caller; /* ptr of the caller */
|
void *caller; /* ptr of the caller */
|
||||||
unsigned int flags; /* other flags */
|
unsigned int flags; /* other flags */
|
||||||
|
|
@ -50,7 +50,7 @@ struct httpclient {
|
||||||
#define HTTPCLIENT_FS_ENDED 0x00020000 /* the httpclient is stopped */
|
#define HTTPCLIENT_FS_ENDED 0x00020000 /* the httpclient is stopped */
|
||||||
|
|
||||||
/* options */
|
/* options */
|
||||||
#define HTTPCLIENT_O_HTTPPROXY 0x00000001 /* the request must be use an absolute URI */
|
#define HTTPCLIENT_O_HTTPPROXY 0x00000001 /* the request must use an absolute URI */
|
||||||
#define HTTPCLIENT_O_RES_HTX 0x00000002 /* response is stored in HTX */
|
#define HTTPCLIENT_O_RES_HTX 0x00000002 /* response is stored in HTX */
|
||||||
|
|
||||||
/* States of the HTTP Client Appctx */
|
/* States of the HTTP Client Appctx */
|
||||||
|
|
@ -65,4 +65,4 @@ enum {
|
||||||
|
|
||||||
#define HTTPCLIENT_USERAGENT "HAProxy"
|
#define HTTPCLIENT_USERAGENT "HAProxy"
|
||||||
|
|
||||||
#endif /* ! _HAPROXY_HTTCLIENT__T_H */
|
#endif /* !_HAPROXY_HTTPCLIENT_T_H */
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,4 @@ static inline int httpclient_started(struct httpclient *hc)
|
||||||
return !!(hc->flags & HTTPCLIENT_FS_STARTED);
|
return !!(hc->flags & HTTPCLIENT_FS_STARTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* ! _HAPROXY_HTTCLIENT_H */
|
#endif /* !_HAPROXY_HTTPCLIENT_H */
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,9 @@ struct buffer *htx_copy_to_large_buffer(struct buffer *dst, struct buffer *src);
|
||||||
#define HTX_XFER_DEFAULT 0x00000000 /* Default XFER: no partial xfer / remove blocks from source */
|
#define HTX_XFER_DEFAULT 0x00000000 /* Default XFER: no partial xfer / remove blocks from source */
|
||||||
#define HTX_XFER_KEEP_SRC_BLKS 0x00000001 /* Don't remove xfer blocks from source messages during xfer */
|
#define HTX_XFER_KEEP_SRC_BLKS 0x00000001 /* Don't remove xfer blocks from source messages during xfer */
|
||||||
#define HTX_XFER_PARTIAL_HDRS_COPY 0x00000002 /* Allow partial copy of headers and trailers part */
|
#define HTX_XFER_PARTIAL_HDRS_COPY 0x00000002 /* Allow partial copy of headers and trailers part */
|
||||||
#define HTX_XFER_HDRS_ONLY 0x00000003 /* Only Transfer header blocks (start-line, header and EOH) */
|
#define HTX_XFER_HDRS_ONLY 0x00000004 /* Only Transfer header blocks (start-line, header and EOH) */
|
||||||
|
#define HTX_XFER_NO_METADATA 0x00000008 /* <count> don't include meta-data, only payload */
|
||||||
|
|
||||||
size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int flags);
|
size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int flags);
|
||||||
|
|
||||||
/* Functions and macros to get parts of the start-line or length of these
|
/* Functions and macros to get parts of the start-line or length of these
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,56 @@ static inline unsigned int div64_32(unsigned long long o1, unsigned int o2)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* returns non-zero if a*b would overflow an unsigned long, otherwise sets the
|
||||||
|
* result into res and returns 0.
|
||||||
|
*/
|
||||||
|
static inline int mulul_overflow(unsigned long a, unsigned long b, unsigned long *res)
|
||||||
|
{
|
||||||
|
/* __builtin_mul_overflow() is gcc >= 5 or clang >= 3.4 */
|
||||||
|
#if (defined(__GNUC__) && __GNUC__ >= 5) || \
|
||||||
|
(defined(__clang__) && ((__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ >= 4)))
|
||||||
|
return __builtin_mul_overflow(a, b, res);
|
||||||
|
#else
|
||||||
|
/* portable method involving a division */
|
||||||
|
if (a && b && a > (~(ulong)0) / b)
|
||||||
|
return 1;
|
||||||
|
*res = a * b;
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns non-zero if a*b would overflow a size_t, otherwise sets the
|
||||||
|
* result into res and returns 0.
|
||||||
|
*/
|
||||||
|
static inline int mulsz_overflow(size_t a, size_t b, size_t *res)
|
||||||
|
{
|
||||||
|
/* __builtin_mul_overflow() is gcc >= 5 or clang >= 3.4 */
|
||||||
|
#if (defined(__GNUC__) && __GNUC__ >= 5) || \
|
||||||
|
(defined(__clang__) && ((__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ >= 4)))
|
||||||
|
return __builtin_mul_overflow(a, b, res);
|
||||||
|
#else
|
||||||
|
/* portable method involving a division */
|
||||||
|
if (a && b && a > (~(size_t)0) / b)
|
||||||
|
return 1;
|
||||||
|
*res = a * b;
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Computes the size of an array of m*n bytes, taking overflows into account.
|
||||||
|
* If the multiply would overflow, returns the largest possible size_t so that
|
||||||
|
* any call to malloc() or equivalent would fail. Otherwise returns the size.
|
||||||
|
* Note that this implies that even 1*max would not be permitted either.
|
||||||
|
*/
|
||||||
|
static inline size_t array_size_or_fail(size_t m, size_t n)
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
if (unlikely(mulsz_overflow(m, n, &size)))
|
||||||
|
return DISGUISE(~(size_t)0);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
/* rotate left a 64-bit integer by <bits:[0-5]> bits */
|
/* rotate left a 64-bit integer by <bits:[0-5]> bits */
|
||||||
static inline uint64_t rotl64(uint64_t v, uint8_t bits)
|
static inline uint64_t rotl64(uint64_t v, uint8_t bits)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ struct qcc {
|
||||||
struct list frms; /* prepared frames related to flow-control */
|
struct list frms; /* prepared frames related to flow-control */
|
||||||
|
|
||||||
uint64_t ms_bidi_init; /* max initial sub-ID of bidi stream allowed for the peer */
|
uint64_t ms_bidi_init; /* max initial sub-ID of bidi stream allowed for the peer */
|
||||||
|
uint64_t ms_bidi_rel; /* max relative sub-ID of bidi stream allowed for the peer */
|
||||||
uint64_t ms_bidi; /* max sub-ID of bidi stream allowed for the peer */
|
uint64_t ms_bidi; /* max sub-ID of bidi stream allowed for the peer */
|
||||||
uint64_t cl_bidi_r; /* total count of closed remote bidi stream since last MAX_STREAMS emission */
|
uint64_t cl_bidi_r; /* total count of closed remote bidi stream since last MAX_STREAMS emission */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <haproxy/connection.h>
|
#include <haproxy/connection.h>
|
||||||
#include <haproxy/list.h>
|
#include <haproxy/list.h>
|
||||||
#include <haproxy/mux_quic-t.h>
|
#include <haproxy/mux_quic-t.h>
|
||||||
|
#include <haproxy/quic_tune.h>
|
||||||
#include <haproxy/stconn.h>
|
#include <haproxy/stconn.h>
|
||||||
|
|
||||||
#include <haproxy/h3.h>
|
#include <haproxy/h3.h>
|
||||||
|
|
@ -128,6 +129,9 @@ static inline void qcs_wait_http_req(struct qcs *qcs)
|
||||||
BUG_ON_HOT(qcs->flags & QC_SF_HREQ_RECV);
|
BUG_ON_HOT(qcs->flags & QC_SF_HREQ_RECV);
|
||||||
qcs->flags |= QC_SF_HREQ_RECV;
|
qcs->flags |= QC_SF_HREQ_RECV;
|
||||||
++qcc->nb_hreq;
|
++qcc->nb_hreq;
|
||||||
|
|
||||||
|
/* On BE side avail_streams cb should prevent opening of too many concurrent streams. */
|
||||||
|
BUG_ON(conn_is_back(qcc->conn) && qcc->nb_hreq > quic_tune.be.stream_max_concurrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qcc_show_quic(struct qcc *qcc);
|
void qcc_show_quic(struct qcc *qcc);
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ void proxy_capture_error(struct proxy *proxy, int is_back,
|
||||||
void (*show)(struct buffer *, const struct error_snapshot *));
|
void (*show)(struct buffer *, const struct error_snapshot *));
|
||||||
void proxy_adjust_all_maxconn(void);
|
void proxy_adjust_all_maxconn(void);
|
||||||
struct proxy *cli_find_frontend(struct appctx *appctx, const char *arg);
|
struct proxy *cli_find_frontend(struct appctx *appctx, const char *arg);
|
||||||
struct proxy *cli_find_frontend(struct appctx *appctx, const char *arg);
|
struct proxy *cli_find_backend(struct appctx *appctx, const char *arg);
|
||||||
int resolve_stick_rule(struct proxy *curproxy, struct sticking_rule *mrule);
|
int resolve_stick_rule(struct proxy *curproxy, struct sticking_rule *mrule);
|
||||||
void free_stick_rules(struct list *rules);
|
void free_stick_rules(struct list *rules);
|
||||||
void free_server_rules(struct list *srules);
|
void free_server_rules(struct list *srules);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* include/haproxy/dns-t.h
|
* include/haproxy/resolvers-t.h
|
||||||
* This file provides structures and types for DNS.
|
* This file provides structures and types for DNS.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
|
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
|
||||||
|
|
@ -114,7 +114,7 @@ struct resolv_answer_item {
|
||||||
char name[DNS_MAX_NAME_SIZE+1]; /* answer name */
|
char name[DNS_MAX_NAME_SIZE+1]; /* answer name */
|
||||||
int16_t type; /* question type */
|
int16_t type; /* question type */
|
||||||
int16_t class; /* query class */
|
int16_t class; /* query class */
|
||||||
int32_t ttl; /* response TTL */
|
uint32_t ttl; /* response TTL */
|
||||||
int16_t priority; /* SRV type priority */
|
int16_t priority; /* SRV type priority */
|
||||||
uint16_t weight; /* SRV type weight */
|
uint16_t weight; /* SRV type weight */
|
||||||
uint16_t port; /* SRV type port */
|
uint16_t port; /* SRV type port */
|
||||||
|
|
@ -281,7 +281,7 @@ enum {
|
||||||
* matching preference was found.
|
* matching preference was found.
|
||||||
*/
|
*/
|
||||||
RSLV_UPD_SRVIP_NOT_FOUND, /* provided IP not found
|
RSLV_UPD_SRVIP_NOT_FOUND, /* provided IP not found
|
||||||
* OR provided IP found and preference is not match and an IP
|
* OR provided IP found and preference is not matched and an IP
|
||||||
* matching preference was found.
|
* matching preference was found.
|
||||||
*/
|
*/
|
||||||
RSLV_UPD_NO_IP_FOUND, /* no IP could be found in the response */
|
RSLV_UPD_NO_IP_FOUND, /* no IP could be found in the response */
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* include/haproxy/dns.h
|
* include/haproxy/resolvers.h
|
||||||
* This file provides functions related to DNS protocol
|
* This file provides functions related to DNS protocol
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
|
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@ enum srv_initaddr {
|
||||||
* at start up time.
|
* at start up time.
|
||||||
*/
|
*/
|
||||||
enum srv_init_state {
|
enum srv_init_state {
|
||||||
SRV_INIT_STATE_FULLY_DOWN = 0, /* the server should initially be considered DOWN until it passes all health checks. Please keep set to zero. */
|
SRV_INIT_STATE_NONE = 0,
|
||||||
|
SRV_INIT_STATE_FULLY_DOWN, /* the server should initially be considered DOWN until it passes all health checks. Please keep set to zero. */
|
||||||
SRV_INIT_STATE_DOWN, /* the server should initially be considered DOWN until it passes one health check. */
|
SRV_INIT_STATE_DOWN, /* the server should initially be considered DOWN until it passes one health check. */
|
||||||
SRV_INIT_STATE_UP, /* the server should initially be considered UP, but will go DOWN if it fails one health check. */
|
SRV_INIT_STATE_UP, /* the server should initially be considered UP, but will go DOWN if it fails one health check. */
|
||||||
SRV_INIT_STATE_FULLY_UP, /* the server should initially be considered UP, but will go DOWN if it fails all health checks. */
|
SRV_INIT_STATE_FULLY_UP, /* the server should initially be considered UP, but will go DOWN if it fails all health checks. */
|
||||||
|
|
@ -248,7 +249,9 @@ struct pid_list {
|
||||||
|
|
||||||
/* srv methods of computing chash keys */
|
/* srv methods of computing chash keys */
|
||||||
enum srv_hash_key {
|
enum srv_hash_key {
|
||||||
SRV_HASH_KEY_ID = 0, /* derived from server puid */
|
SRV_HASH_KEY_ID = 0, /* derived from server puid, 28 LSB used */
|
||||||
|
SRV_HASH_KEY_ID32, /* derived from server puid, 32 bits used */
|
||||||
|
SRV_HASH_KEY_GUID, /* derived from server guid */
|
||||||
SRV_HASH_KEY_ADDR, /* derived from server address */
|
SRV_HASH_KEY_ADDR, /* derived from server address */
|
||||||
SRV_HASH_KEY_ADDR_PORT /* derived from server address and port */
|
SRV_HASH_KEY_ADDR_PORT /* derived from server address and port */
|
||||||
};
|
};
|
||||||
|
|
@ -326,6 +329,7 @@ enum renegotiate_mode {
|
||||||
struct path_parameters {
|
struct path_parameters {
|
||||||
__decl_thread(HA_RWLOCK_T param_lock);
|
__decl_thread(HA_RWLOCK_T param_lock);
|
||||||
char nego_alpn[MAX_ALPN_SIZE];
|
char nego_alpn[MAX_ALPN_SIZE];
|
||||||
|
int64_t srv_hash;
|
||||||
#ifdef USE_QUIC
|
#ifdef USE_QUIC
|
||||||
struct quic_early_transport_params tps;
|
struct quic_early_transport_params tps;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -278,6 +278,35 @@ static inline void srv_adm_set_ready(struct server *s)
|
||||||
srv_clr_admin_flag(s, SRV_ADMF_FMAINT);
|
srv_clr_admin_flag(s, SRV_ADMF_FMAINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void srv_set_init_state(struct server *srv)
|
||||||
|
{
|
||||||
|
/* no init-state configured or the server is already disabled: don't eval init-state */
|
||||||
|
if (srv->init_state == SRV_INIT_STATE_NONE ||
|
||||||
|
srv->next_admin & (SRV_ADMF_CMAINT | SRV_ADMF_FMAINT))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (srv->init_state == SRV_INIT_STATE_FULLY_UP) {
|
||||||
|
/* initially UP, when all checks fail to bring server DOWN */
|
||||||
|
srv->next_state = SRV_ST_RUNNING;
|
||||||
|
srv->check.health = srv->check.rise + srv->check.fall - 1;
|
||||||
|
}
|
||||||
|
else if (srv->init_state == SRV_INIT_STATE_UP) {
|
||||||
|
/* initially UP, when one check fails check brings server DOWN */
|
||||||
|
srv->next_state = SRV_ST_RUNNING;
|
||||||
|
srv->check.health = srv->check.rise;
|
||||||
|
}
|
||||||
|
else if (srv->init_state == SRV_INIT_STATE_DOWN) {
|
||||||
|
/* initially DOWN, when one check is successful bring server UP */
|
||||||
|
srv->next_state = SRV_ST_STOPPED;
|
||||||
|
srv->check.health = srv->check.rise - 1;
|
||||||
|
}
|
||||||
|
else if (srv->init_state == SRV_INIT_STATE_FULLY_DOWN) {
|
||||||
|
/* initially DOWN, when all checks are successful bring server UP */
|
||||||
|
srv->next_state = SRV_ST_STOPPED;
|
||||||
|
srv->check.health = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* appends an initaddr method to the existing list. Returns 0 on failure. */
|
/* appends an initaddr method to the existing list. Returns 0 on failure. */
|
||||||
static inline int srv_append_initaddr(unsigned int *list, enum srv_initaddr addr)
|
static inline int srv_append_initaddr(unsigned int *list, enum srv_initaddr addr)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ struct certificate_ocsp {
|
||||||
int refcount_store; /* Number of ckch_store that reference this certificate_ocsp */
|
int refcount_store; /* Number of ckch_store that reference this certificate_ocsp */
|
||||||
int refcount; /* Number of actual references to this certificate_ocsp (SSL_CTXs mostly) */
|
int refcount; /* Number of actual references to this certificate_ocsp (SSL_CTXs mostly) */
|
||||||
struct buffer response;
|
struct buffer response;
|
||||||
long expire;
|
unsigned long expire;
|
||||||
X509 *issuer;
|
X509 *issuer;
|
||||||
STACK_OF(X509) *chain;
|
STACK_OF(X509) *chain;
|
||||||
struct eb64_node next_update; /* Key of items inserted in ocsp_update_tree (sorted by absolute date) */
|
struct eb64_node next_update; /* Key of items inserted in ocsp_update_tree (sorted by absolute date) */
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@
|
||||||
#define _TRC_LOC(f,l) __TRC_LOC(f, ":", l)
|
#define _TRC_LOC(f,l) __TRC_LOC(f, ":", l)
|
||||||
#define __TRC_LOC(f,c,l) f c #l
|
#define __TRC_LOC(f,c,l) f c #l
|
||||||
|
|
||||||
|
#if defined(USE_TRACE)
|
||||||
|
|
||||||
/* truncate a macro arg list to exactly 5 args and replace missing ones with NULL.
|
/* truncate a macro arg list to exactly 5 args and replace missing ones with NULL.
|
||||||
* The first one (a0) is always ignored.
|
* The first one (a0) is always ignored.
|
||||||
*/
|
*/
|
||||||
|
|
@ -139,8 +141,23 @@
|
||||||
&trace_no_cb, ist2(_msg, _msg_len)); \
|
&trace_no_cb, ist2(_msg, _msg_len)); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
#else
|
||||||
|
# define TRACE_ENABLED(level, mask, args...) 0
|
||||||
|
# define TRACE(msg, mask, args...) do { /* do nothing */ } while(0)
|
||||||
|
# define TRACE_ERROR(msg, mask, args...) do { /* do nothing */ } while(0)
|
||||||
|
# define TRACE_USER(msg, mask, args...) do { /* do nothing */ } while(0)
|
||||||
|
# define TRACE_DATA(msg, mask, args...) do { /* do nothing */ } while(0)
|
||||||
|
# define TRACE_PROTO(msg, mask, args...) do { /* do nothing */ } while(0)
|
||||||
|
# define TRACE_STATE(msg, mask, args...) do { /* do nothing */ } while(0)
|
||||||
|
# define TRACE_DEVEL(msg, mask, args...) do { /* do nothing */ } while(0)
|
||||||
|
# define TRACE_ENTER(mask, args...) do { /* do nothing */ } while(0)
|
||||||
|
# define TRACE_LEAVE(mask, args...) do { /* do nothing */ } while(0)
|
||||||
|
# define TRACE_POINT(mask, args...) do { /* do nothing */ } while(0)
|
||||||
|
# define TRACE_PRINTF(level, args...) do { /* do nothing */ } while(0)
|
||||||
|
# define TRACE_PRINTF_LOC(level, args...) do { /* do nothing */ } while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(DEBUG_DEV) || defined(DEBUG_FULL)
|
#if defined (USE_TRACE) && (defined(DEBUG_DEV) || defined(DEBUG_FULL))
|
||||||
# define DBG_TRACE(msg, mask, args...) TRACE(msg, mask, ##args)
|
# define DBG_TRACE(msg, mask, args...) TRACE(msg, mask, ##args)
|
||||||
# define DBG_TRACE_ERROR(msg, mask, args...) TRACE_ERROR(msg, mask, ##args)
|
# define DBG_TRACE_ERROR(msg, mask, args...) TRACE_ERROR(msg, mask, ##args)
|
||||||
# define DBG_TRACE_USER(msg, mask, args...) TRACE_USER(msg, mask, ##args)
|
# define DBG_TRACE_USER(msg, mask, args...) TRACE_USER(msg, mask, ##args)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ syslog S3 -level notice {
|
||||||
|
|
||||||
syslog S4 -level notice {
|
syslog S4 -level notice {
|
||||||
recv
|
recv
|
||||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv4 failed.+reason: Layer4 connection problem.+info: \"Connection refused\".+check duration: [[:digit:]]+ms.+status: 0/1 DOWN."
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be4/srv4 failed.+reason: Layer4 connection problem.+info: \"ECONNREFUSED returned by OS.*\".+check duration: [[:digit:]]+ms.+status: 0/1 DOWN."
|
||||||
} -start
|
} -start
|
||||||
|
|
||||||
server s1 {
|
server s1 {
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ feature ignore_unknown_macro
|
||||||
|
|
||||||
syslog S1 -level notice {
|
syslog S1 -level notice {
|
||||||
recv
|
recv
|
||||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv1 failed.*Connection refused at step 2 of tcp-check.*connect port 1"
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv1 failed.*ECONNREFUSED returned by OS.* at step 2 of tcp-check.*connect port 1"
|
||||||
} -start
|
} -start
|
||||||
|
|
||||||
syslog S2 -level notice {
|
syslog S2 -level notice {
|
||||||
recv
|
recv
|
||||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv1 failed.*Connection refused at step 1 of tcp-check.*connect port 1"
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv1 failed.*ECONNREFUSED returned by OS.* at step 1 of tcp-check.*connect port 1"
|
||||||
} -start
|
} -start
|
||||||
|
|
||||||
server s1 {
|
server s1 {
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ client c1 -connect ${hap_fe1_sock} {
|
||||||
} -run
|
} -run
|
||||||
|
|
||||||
# missing websocket key
|
# missing websocket key
|
||||||
client c2 -connect ${hap_fe1_sock} {
|
client c2_1 -connect ${hap_fe1_sock} {
|
||||||
txreq \
|
txreq \
|
||||||
-req "GET" \
|
-req "GET" \
|
||||||
-url "/" \
|
-url "/" \
|
||||||
|
|
@ -158,6 +158,19 @@ client c2 -connect ${hap_fe1_sock} {
|
||||||
expect resp.status == 400
|
expect resp.status == 400
|
||||||
} -run
|
} -run
|
||||||
|
|
||||||
|
client c2_2 -connect ${hap_fe1_sock} {
|
||||||
|
txreq \
|
||||||
|
-req "GET" \
|
||||||
|
-url "/" \
|
||||||
|
-hdr "host: 127.0.0.1" \
|
||||||
|
-hdr "connection: upgrade" \
|
||||||
|
-hdr "upgrade: proto1, websocket, proto2" \
|
||||||
|
-hdr "upgrade: proto3"
|
||||||
|
|
||||||
|
rxresp
|
||||||
|
expect resp.status == 400
|
||||||
|
} -run
|
||||||
|
|
||||||
# missing key on server side
|
# missing key on server side
|
||||||
client c3 -connect ${hap_fe2_sock} {
|
client c3 -connect ${hap_fe2_sock} {
|
||||||
txreq \
|
txreq \
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,10 @@ client c1 -connect ${h1_feS_sock} {
|
||||||
|
|
||||||
haproxy h1 -cli {
|
haproxy h1 -cli {
|
||||||
# non existent backend
|
# non existent backend
|
||||||
send "experimental-mode on; add backend be from def"
|
send "add backend be from def"
|
||||||
expect ~ "Mode is required"
|
expect ~ "Mode is required"
|
||||||
|
|
||||||
send "experimental-mode on; add backend be from def_http"
|
send "add backend be from def_http"
|
||||||
expect ~ "New backend registered."
|
expect ~ "New backend registered."
|
||||||
|
|
||||||
send "add server be/srv ${hsrv_fe_addr}:${hsrv_fe_port}"
|
send "add server be/srv ${hsrv_fe_addr}:${hsrv_fe_port}"
|
||||||
|
|
|
||||||
|
|
@ -40,29 +40,29 @@ haproxy h1 -conf {
|
||||||
} -start
|
} -start
|
||||||
|
|
||||||
haproxy h1 -cli {
|
haproxy h1 -cli {
|
||||||
send "experimental-mode on; del backend other"
|
send "del backend other"
|
||||||
expect ~ "No such backend."
|
expect ~ "No such backend."
|
||||||
|
|
||||||
send "experimental-mode on; del backend li"
|
send "del backend li"
|
||||||
expect ~ "Cannot delete a listen section."
|
expect ~ "Cannot delete a listen section."
|
||||||
|
|
||||||
send "experimental-mode on; del backend be_ref"
|
send "del backend be_ref"
|
||||||
expect ~ "This proxy cannot be removed at runtime due to other configuration elements pointing to it."
|
expect ~ "This proxy cannot be removed at runtime due to other configuration elements pointing to it."
|
||||||
|
|
||||||
send "show stat be 2 -1"
|
send "show stat be 2 -1"
|
||||||
expect ~ "be,BACKEND,"
|
expect ~ "be,BACKEND,"
|
||||||
|
|
||||||
send "experimental-mode on; del backend be"
|
send "del backend be"
|
||||||
expect ~ "Backend must be unpublished prior to its deletion."
|
expect ~ "Backend must be unpublished prior to its deletion."
|
||||||
|
|
||||||
send "unpublish backend be;"
|
send "unpublish backend be;"
|
||||||
expect ~ ".*"
|
expect ~ ".*"
|
||||||
send "experimental-mode on; del backend be"
|
send "del backend be"
|
||||||
expect ~ "Only a backend without server can be deleted."
|
expect ~ "Only a backend without server can be deleted."
|
||||||
|
|
||||||
send "del server be/s1"
|
send "del server be/s1"
|
||||||
expect ~ ".*"
|
expect ~ ".*"
|
||||||
send "experimental-mode on; del backend be"
|
send "del backend be"
|
||||||
expect ~ "Backend deleted."
|
expect ~ "Backend deleted."
|
||||||
|
|
||||||
send "show stat be 2 -1"
|
send "show stat be 2 -1"
|
||||||
|
|
@ -75,7 +75,7 @@ haproxy h1 -cli {
|
||||||
|
|
||||||
send "unpublish backend be_unnamed_def_ref;"
|
send "unpublish backend be_unnamed_def_ref;"
|
||||||
expect ~ ".*"
|
expect ~ ".*"
|
||||||
send "experimental-mode on; del backend be_unnamed_def_ref"
|
send "del backend be_unnamed_def_ref"
|
||||||
expect ~ "Backend deleted."
|
expect ~ "Backend deleted."
|
||||||
|
|
||||||
send "show stat be_unnamed_def_ref 2 -1"
|
send "show stat be_unnamed_def_ref 2 -1"
|
||||||
|
|
@ -83,6 +83,6 @@ haproxy h1 -cli {
|
||||||
|
|
||||||
send "unpublish backend be_unnamed_def_ref2;"
|
send "unpublish backend be_unnamed_def_ref2;"
|
||||||
expect ~ ".*"
|
expect ~ ".*"
|
||||||
send "experimental-mode on; del backend be_unnamed_def_ref2"
|
send "del backend be_unnamed_def_ref2"
|
||||||
expect ~ "Backend deleted."
|
expect ~ "Backend deleted."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
#REGTEST_TYPE=bug
|
#REGTEST_TYPE=bug
|
||||||
varnishtest "Test for ECDSA/RSA selection and crt-list filters"
|
varnishtest "Test for ECDSA/RSA selection and crt-list filters"
|
||||||
feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.8)'"
|
feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.8)'"
|
||||||
feature cmd "$HAPROXY_PROGRAM -cc 'feature(QUIC)'"
|
# QUIC backend are not supported with USE_QUIC_OPENSSL_COMPAT
|
||||||
|
feature cmd "$HAPROXY_PROGRAM -cc 'feature(QUIC) && !feature(QUIC_OPENSSL_COMPAT) && !feature(OPENSSL_WOLFSSL)'"
|
||||||
# Note that USE_OPENSSL is always set if USE_QUIC is set
|
# Note that USE_OPENSSL is always set if USE_QUIC is set
|
||||||
# Same conditions as for ssl/tls13_ssl_crt-list_filters.vtc about TLS library versions
|
# Same conditions as for ssl/tls13_ssl_crt-list_filters.vtc about TLS library versions
|
||||||
feature cmd "$HAPROXY_PROGRAM -cc 'ssllib_name_startswith(OpenSSL) && openssl_version_atleast(1.1.1) || feature(OPENSSL_AWSLC)'"
|
feature cmd "$HAPROXY_PROGRAM -cc 'ssllib_name_startswith(OpenSSL) && openssl_version_atleast(1.1.1) || feature(OPENSSL_AWSLC)'"
|
||||||
|
|
||||||
# This test checks if the multiple certificate types works correctly with the
|
# This test checks if the multiple certificate types works correctly with the
|
||||||
# SNI, and that the negative filters are correctly excluded
|
# SNI, and that the negative filters are correctly excluded
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,10 @@ haproxy h1 -conf {
|
||||||
thread-groups 1
|
thread-groups 1
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
|
.if feature(QUIC_OPENSSL_COMPAT)
|
||||||
|
limited-quic
|
||||||
|
.endif
|
||||||
|
|
||||||
stats socket "${tmpdir}/h1/stats" level admin
|
stats socket "${tmpdir}/h1/stats" level admin
|
||||||
issuers-chain-path "${testdir}/certs/issuers-chain-path/ca/"
|
issuers-chain-path "${testdir}/certs/issuers-chain-path/ca/"
|
||||||
crt-base "${testdir}/certs/issuers-chain-path"
|
crt-base "${testdir}/certs/issuers-chain-path"
|
||||||
|
|
|
||||||
10
src/acme.c
10
src/acme.c
|
|
@ -1562,6 +1562,16 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||||
key = ctx->store->data->key;
|
key = ctx->store->data->key;
|
||||||
ctx->store->data->key = NULL;
|
ctx->store->data->key = NULL;
|
||||||
|
|
||||||
|
/* OpenSSL's BIO_new_mem_buf() expects a NUL-terminated string when
|
||||||
|
* passed -1. The httpclient buffer lacks this, so manually terminate
|
||||||
|
* it here to prevent an out-of-bounds heap read during PEM parsing.
|
||||||
|
*/
|
||||||
|
if (b_room(&hc->res.buf) < 1) {
|
||||||
|
memprintf(errmsg, "ACME certificate response has no room for NUL terminator");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
hc->res.buf.area[hc->res.buf.data] = '\0';
|
||||||
|
|
||||||
/* XXX: might need a function dedicated to this, which does not read a private key */
|
/* XXX: might need a function dedicated to this, which does not read a private key */
|
||||||
if (ssl_sock_load_pem_into_ckch(ctx->store->path, hc->res.buf.area, ctx->store->data , errmsg) != 0)
|
if (ssl_sock_load_pem_into_ckch(ctx->store->path, hc->res.buf.area, ctx->store->data , errmsg) != 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
|
||||||
|
|
@ -539,9 +539,6 @@ size_t appctx_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, unsig
|
||||||
if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC))
|
if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC))
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
if (!count)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
if (!appctx_get_buf(appctx, &appctx->outbuf)) {
|
if (!appctx_get_buf(appctx, &appctx->outbuf)) {
|
||||||
TRACE_STATE("waiting for appctx outbuf allocation", APPLET_EV_RECV|APPLET_EV_BLK, appctx);
|
TRACE_STATE("waiting for appctx outbuf allocation", APPLET_EV_RECV|APPLET_EV_BLK, appctx);
|
||||||
goto end;
|
goto end;
|
||||||
|
|
@ -550,7 +547,8 @@ size_t appctx_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, unsig
|
||||||
if (flags & CO_RFL_BUF_FLUSH)
|
if (flags & CO_RFL_BUF_FLUSH)
|
||||||
applet_fl_set(appctx, APPCTX_FL_FASTFWD);
|
applet_fl_set(appctx, APPCTX_FL_FASTFWD);
|
||||||
|
|
||||||
ret = CALL_APPLET_WITH_RET(appctx->applet, rcv_buf(appctx, buf, count, flags));
|
if (count)
|
||||||
|
ret = CALL_APPLET_WITH_RET(appctx->applet, rcv_buf(appctx, buf, count, flags));
|
||||||
if (ret)
|
if (ret)
|
||||||
applet_fl_clr(appctx, APPCTX_FL_OUTBLK_FULL);
|
applet_fl_clr(appctx, APPCTX_FL_OUTBLK_FULL);
|
||||||
|
|
||||||
|
|
@ -608,7 +606,7 @@ size_t appctx_htx_snd_buf(struct appctx *appctx, struct buffer *buf, size_t coun
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
htx_xfer(appctx_htx, buf_htx, count, HTX_XFER_DEFAULT);
|
htx_xfer(appctx_htx, buf_htx, count, HTX_XFER_NO_METADATA);
|
||||||
if (htx_is_empty(buf_htx)) {
|
if (htx_is_empty(buf_htx)) {
|
||||||
appctx_htx->flags |= (buf_htx->flags & HTX_FL_EOM);
|
appctx_htx->flags |= (buf_htx->flags & HTX_FL_EOM);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ unsigned int gen_hash(const struct proxy* px, const char* key, unsigned long len
|
||||||
hash = hash_crc32(key, len);
|
hash = hash_crc32(key, len);
|
||||||
break;
|
break;
|
||||||
case BE_LB_HFCN_NONE:
|
case BE_LB_HFCN_NONE:
|
||||||
/* use key as a hash */
|
/* use key as a hash. It MUST be in string format */
|
||||||
{
|
{
|
||||||
const char *_key = key;
|
const char *_key = key;
|
||||||
|
|
||||||
|
|
@ -370,11 +370,11 @@ struct server *get_server_ph_post(struct stream *s, const struct server *avoid)
|
||||||
len -= plen + 1;
|
len -= plen + 1;
|
||||||
|
|
||||||
while (len && *end != '&') {
|
while (len && *end != '&') {
|
||||||
if (unlikely(!HTTP_IS_TOKEN(*p))) {
|
if (unlikely(!HTTP_IS_TOKEN(*end))) {
|
||||||
/* if in a POST, body must be URI encoded or it's not a URI.
|
/* if in a POST, body must be URI encoded or it's not a URI.
|
||||||
* Do not interpret any possible binary data as a parameter.
|
* Do not interpret any possible binary data as a parameter.
|
||||||
*/
|
*/
|
||||||
if (likely(HTTP_IS_LWS(*p))) /* eol, uncertain uri len */
|
if (likely(HTTP_IS_LWS(*end))) /* eol, uncertain uri len */
|
||||||
break;
|
break;
|
||||||
return NULL; /* oh, no; this is not uri-encoded.
|
return NULL; /* oh, no; this is not uri-encoded.
|
||||||
* This body does not contain parameters.
|
* This body does not contain parameters.
|
||||||
|
|
@ -545,7 +545,14 @@ struct server *get_server_expr(struct stream *s, const struct server *avoid)
|
||||||
if (px->lbprm.tot_used == 1)
|
if (px->lbprm.tot_used == 1)
|
||||||
goto hash_done;
|
goto hash_done;
|
||||||
|
|
||||||
smp = sample_fetch_as_type(px, s->sess, s, SMP_OPT_DIR_REQ | SMP_OPT_FINAL, px->lbprm.expr, SMP_T_BIN);
|
/* Note that if the hash-type doesn't hash the key, we must provide it
|
||||||
|
* as a string representing a number as it will be parsed by read_int64().
|
||||||
|
* Otherwise it's binary. The difference happens on samples returing
|
||||||
|
* ints (e.g. rand()) as well as IP addresses, which, when turned to
|
||||||
|
* binary, are just binary-encoded and cannot be parsed.
|
||||||
|
*/
|
||||||
|
smp = sample_fetch_as_type(px, s->sess, s, SMP_OPT_DIR_REQ | SMP_OPT_FINAL, px->lbprm.expr,
|
||||||
|
((px->lbprm.algo & BE_LB_HASH_FUNC) == BE_LB_HFCN_NONE) ? SMP_T_STR : SMP_T_BIN);
|
||||||
if (!smp)
|
if (!smp)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
|
@ -1811,7 +1818,10 @@ int connect_server(struct stream *s)
|
||||||
{
|
{
|
||||||
struct connection *cli_conn = objt_conn(strm_orig(s));
|
struct connection *cli_conn = objt_conn(strm_orig(s));
|
||||||
struct connection *srv_conn = NULL;
|
struct connection *srv_conn = NULL;
|
||||||
|
const struct mux_proto_list *mux_proto = NULL;
|
||||||
struct server *srv;
|
struct server *srv;
|
||||||
|
struct ist name = IST_NULL;
|
||||||
|
struct sample *name_smp;
|
||||||
int reuse_mode;
|
int reuse_mode;
|
||||||
int reuse __maybe_unused = 0;
|
int reuse __maybe_unused = 0;
|
||||||
int may_use_early_data __maybe_unused = 1; // are we allowed to use early data ?
|
int may_use_early_data __maybe_unused = 1; // are we allowed to use early data ?
|
||||||
|
|
@ -1833,6 +1843,17 @@ int connect_server(struct stream *s)
|
||||||
if (err != SRV_STATUS_OK)
|
if (err != SRV_STATUS_OK)
|
||||||
return SF_ERR_INTERNAL;
|
return SF_ERR_INTERNAL;
|
||||||
|
|
||||||
|
if (srv && srv->pool_conn_name_expr) {
|
||||||
|
name_smp = sample_fetch_as_type(s->be, s->sess, s,
|
||||||
|
SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
|
||||||
|
srv->pool_conn_name_expr, SMP_T_STR);
|
||||||
|
if (name_smp) {
|
||||||
|
name = ist2(name_smp->data.u.str.area,
|
||||||
|
name_smp->data.u.str.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hash = be_calculate_conn_hash(srv, s, s->sess, bind_addr, s->scb->dst, name);
|
||||||
|
|
||||||
if (!be_supports_conn_reuse(s->be))
|
if (!be_supports_conn_reuse(s->be))
|
||||||
goto skip_reuse;
|
goto skip_reuse;
|
||||||
|
|
||||||
|
|
@ -1844,20 +1865,7 @@ int connect_server(struct stream *s)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const int not_first_req = s->txn.http && s->txn.http->flags & TX_NOT_FIRST;
|
const int not_first_req = s->txn.http && s->txn.http->flags & TX_NOT_FIRST;
|
||||||
struct ist name = IST_NULL;
|
|
||||||
struct sample *name_smp;
|
|
||||||
|
|
||||||
if (srv && srv->pool_conn_name_expr) {
|
|
||||||
name_smp = sample_fetch_as_type(s->be, s->sess, s,
|
|
||||||
SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
|
|
||||||
srv->pool_conn_name_expr, SMP_T_STR);
|
|
||||||
if (name_smp) {
|
|
||||||
name = ist2(name_smp->data.u.str.area,
|
|
||||||
name_smp->data.u.str.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hash = be_calculate_conn_hash(srv, s, s->sess, bind_addr, s->scb->dst, name);
|
|
||||||
err = be_reuse_connection(hash, s->sess, s->be, srv, s->scb,
|
err = be_reuse_connection(hash, s->sess, s->be, srv, s->scb,
|
||||||
s->target, not_first_req);
|
s->target, not_first_req);
|
||||||
if (err == SF_ERR_INTERNAL)
|
if (err == SF_ERR_INTERNAL)
|
||||||
|
|
@ -2079,7 +2087,7 @@ int connect_server(struct stream *s)
|
||||||
if (IS_HTX_STRM(s) && srv->use_ssl &&
|
if (IS_HTX_STRM(s) && srv->use_ssl &&
|
||||||
(srv->ssl_ctx.alpn_str || srv->ssl_ctx.npn_str)) {
|
(srv->ssl_ctx.alpn_str || srv->ssl_ctx.npn_str)) {
|
||||||
HA_RWLOCK_RDLOCK(SERVER_LOCK, &srv->path_params.param_lock);
|
HA_RWLOCK_RDLOCK(SERVER_LOCK, &srv->path_params.param_lock);
|
||||||
if (srv->path_params.nego_alpn[0] == 0)
|
if (srv->path_params.srv_hash != hash || srv->path_params.nego_alpn[0] == 0)
|
||||||
may_start_mux_now = 0;
|
may_start_mux_now = 0;
|
||||||
HA_RWLOCK_RDUNLOCK(SERVER_LOCK, &srv->path_params.param_lock);
|
HA_RWLOCK_RDUNLOCK(SERVER_LOCK, &srv->path_params.param_lock);
|
||||||
}
|
}
|
||||||
|
|
@ -2130,9 +2138,11 @@ int connect_server(struct stream *s)
|
||||||
srv_conn->flags |= CO_FL_SOCKS4;
|
srv_conn->flags |= CO_FL_SOCKS4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (srv && srv->mux_proto && isteq(srv->mux_proto->token, ist("qmux"))) {
|
if (may_start_mux_now) {
|
||||||
srv_conn->flags |= (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
|
/* Delay MUX init if an XPRT handshake is required prior. */
|
||||||
may_start_mux_now = 0;
|
mux_proto = conn_select_mux_be(srv_conn);
|
||||||
|
if (mux_proto && mux_proto->init_xprt)
|
||||||
|
may_start_mux_now = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
||||||
|
|
@ -2242,6 +2252,13 @@ int connect_server(struct stream *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (mux_proto && mux_proto->init_xprt) {
|
||||||
|
/* Add handshake layer prior to MUX init if required. Does nothing if SSL layer is active though. */
|
||||||
|
if (xprt_add_l6hs(srv_conn, mux_proto->init_xprt)) {
|
||||||
|
conn_full_close(srv_conn);
|
||||||
|
return SF_ERR_INTERNAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now that the mux may have been created, we can start the xprt.
|
* Now that the mux may have been created, we can start the xprt.
|
||||||
|
|
|
||||||
12
src/cache.c
12
src/cache.c
|
|
@ -2180,7 +2180,17 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p
|
||||||
sec_entry = get_secondary_entry(cache_tree, res,
|
sec_entry = get_secondary_entry(cache_tree, res,
|
||||||
s->txn.http->cache_secondary_hash,
|
s->txn.http->cache_secondary_hash,
|
||||||
0);
|
0);
|
||||||
if (sec_entry && sec_entry != res) {
|
if (!sec_entry) {
|
||||||
|
/* Secondary key miss: release the retained primary entry
|
||||||
|
* and reattach the detached row before returning.
|
||||||
|
*/
|
||||||
|
release_entry(cache_tree, res, 0);
|
||||||
|
shctx_wrlock(shctx);
|
||||||
|
if (detached)
|
||||||
|
shctx_row_reattach(shctx, entry_block);
|
||||||
|
shctx_wrunlock(shctx);
|
||||||
|
}
|
||||||
|
else if (sec_entry != res) {
|
||||||
/* The wrong row was added to the hot list. */
|
/* The wrong row was added to the hot list. */
|
||||||
release_entry(cache_tree, res, 0);
|
release_entry(cache_tree, res, 0);
|
||||||
retain_entry(sec_entry);
|
retain_entry(sec_entry);
|
||||||
|
|
|
||||||
|
|
@ -1629,11 +1629,6 @@ static int cfg_parse_global_shm_stats_file(char **args, int section_type,
|
||||||
struct proxy *curpx, const struct proxy *defpx,
|
struct proxy *curpx, const struct proxy *defpx,
|
||||||
const char *file, int line, char **err)
|
const char *file, int line, char **err)
|
||||||
{
|
{
|
||||||
if (!experimental_directives_allowed) {
|
|
||||||
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'", args[0]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (global.shm_stats_file != NULL) {
|
if (global.shm_stats_file != NULL) {
|
||||||
memprintf(err, "'%s' already specified.\n", args[0]);
|
memprintf(err, "'%s' already specified.\n", args[0]);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1644,7 +1639,6 @@ static int cfg_parse_global_shm_stats_file(char **args, int section_type,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
|
||||||
global.shm_stats_file = strdup(args[1]);
|
global.shm_stats_file = strdup(args[1]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1653,11 +1647,6 @@ static int cfg_parse_global_shm_stats_file_max_objects(char **args, int section_
|
||||||
struct proxy *curpx, const struct proxy *defpx,
|
struct proxy *curpx, const struct proxy *defpx,
|
||||||
const char *file, int line, char **err)
|
const char *file, int line, char **err)
|
||||||
{
|
{
|
||||||
if (!experimental_directives_allowed) {
|
|
||||||
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'", args[0]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shm_stats_file_max_objects != -1) {
|
if (shm_stats_file_max_objects != -1) {
|
||||||
memprintf(err, "'%s' already specified.\n", args[0]);
|
memprintf(err, "'%s' already specified.\n", args[0]);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1668,7 +1657,6 @@ static int cfg_parse_global_shm_stats_file_max_objects(char **args, int section_
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
|
||||||
shm_stats_file_max_objects = atoi(args[1]);
|
shm_stats_file_max_objects = atoi(args[1]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1495,7 +1495,7 @@ static int bind_parse_tls_ticket_keys(char **args, int cur_arg, struct proxy *px
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
keys_ref->tlskeys = malloc(TLS_TICKETS_NO * sizeof(union tls_sess_key));
|
keys_ref->tlskeys = malloc(array_size_or_fail(TLS_TICKETS_NO, sizeof(union tls_sess_key)));
|
||||||
if (!keys_ref->tlskeys) {
|
if (!keys_ref->tlskeys) {
|
||||||
memprintf(err, "'%s' : allocation error", args[cur_arg+1]);
|
memprintf(err, "'%s' : allocation error", args[cur_arg+1]);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
|
||||||
|
|
@ -1394,7 +1394,7 @@ int parse_cfg(const struct cfgfile *cfg)
|
||||||
global.cfg_curr_line = 0;
|
global.cfg_curr_line = 0;
|
||||||
global.cfg_curr_file = file;
|
global.cfg_curr_file = file;
|
||||||
|
|
||||||
if ((thisline = malloc(sizeof(*thisline) * linesize)) == NULL) {
|
if ((thisline = malloc(array_size_or_fail(sizeof(*thisline), linesize))) == NULL) {
|
||||||
ha_alert("Out of memory trying to allocate a buffer for a configuration line.\n");
|
ha_alert("Out of memory trying to allocate a buffer for a configuration line.\n");
|
||||||
err_code = -1;
|
err_code = -1;
|
||||||
goto err;
|
goto err;
|
||||||
|
|
@ -1442,7 +1442,7 @@ next_line:
|
||||||
char *newline;
|
char *newline;
|
||||||
int newlinesize = linesize * 2;
|
int newlinesize = linesize * 2;
|
||||||
|
|
||||||
newline = realloc(thisline, sizeof(*thisline) * newlinesize);
|
newline = realloc(thisline, array_size_or_fail(sizeof(*thisline), newlinesize));
|
||||||
if (newline == NULL) {
|
if (newline == NULL) {
|
||||||
ha_alert("parsing [%s:%d]: line too long, cannot allocate memory.\n",
|
ha_alert("parsing [%s:%d]: line too long, cannot allocate memory.\n",
|
||||||
file, linenum);
|
file, linenum);
|
||||||
|
|
|
||||||
|
|
@ -812,7 +812,7 @@ void chk_report_conn_err(struct check *check, int errno_bck, int expired)
|
||||||
}
|
}
|
||||||
|
|
||||||
errno = unclean_errno(errno_bck);
|
errno = unclean_errno(errno_bck);
|
||||||
if (conn && errno)
|
if (conn && !errno)
|
||||||
retrieve_errno_from_socket(conn);
|
retrieve_errno_from_socket(conn);
|
||||||
|
|
||||||
TRACE_ENTER(CHK_EV_HCHK_END|CHK_EV_HCHK_ERR, check, 0, 0, (size_t[]){expired});
|
TRACE_ENTER(CHK_EV_HCHK_END|CHK_EV_HCHK_ERR, check, 0, 0, (size_t[]){expired});
|
||||||
|
|
|
||||||
11
src/cli.c
11
src/cli.c
|
|
@ -1151,8 +1151,13 @@ int cli_parse_cmdline(struct appctx *appctx)
|
||||||
*/
|
*/
|
||||||
if (len-1 == strlen(appctx->cli_ctx.payload_pat)) {
|
if (len-1 == strlen(appctx->cli_ctx.payload_pat)) {
|
||||||
if (strncmp(str, appctx->cli_ctx.payload_pat, len-1) == 0) {
|
if (strncmp(str, appctx->cli_ctx.payload_pat, len-1) == 0) {
|
||||||
/* end of payload was reached, rewind before the previous \n and replace it by a \0 */
|
/* end of payload was reached, rewind before the previous \n, if any, and replace it by a \0
|
||||||
b_sub(buf, strlen(appctx->cli_ctx.payload_pat) + 2);
|
* Otherwise, the payload is empty, just
|
||||||
|
*/
|
||||||
|
if (b_data(buf) > len)
|
||||||
|
b_sub(buf, len+1);
|
||||||
|
else
|
||||||
|
b_sub(buf, len);
|
||||||
*b_tail(buf) = '\0';
|
*b_tail(buf) = '\0';
|
||||||
appctx->st1 &= ~APPCTX_CLI_ST1_PAYLOAD;
|
appctx->st1 &= ~APPCTX_CLI_ST1_PAYLOAD;
|
||||||
}
|
}
|
||||||
|
|
@ -2543,7 +2548,7 @@ static int _getsocks(char **args, char *payload, struct appctx *appctx, void *pr
|
||||||
/* We will send sockets MAX_SEND_FD per MAX_SEND_FD, allocate a
|
/* We will send sockets MAX_SEND_FD per MAX_SEND_FD, allocate a
|
||||||
* buffer big enough to store the socket information.
|
* buffer big enough to store the socket information.
|
||||||
*/
|
*/
|
||||||
tmpbuf = malloc(MAX_SEND_FD * (1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof(int)));
|
tmpbuf = malloc(array_size_or_fail(MAX_SEND_FD, (1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof(int))));
|
||||||
if (tmpbuf == NULL) {
|
if (tmpbuf == NULL) {
|
||||||
ha_warning("Failed to allocate memory to transfer socket information\n");
|
ha_warning("Failed to allocate memory to transfer socket information\n");
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
||||||
149
src/connection.c
149
src/connection.c
|
|
@ -196,7 +196,7 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake)
|
||||||
* information to create one, typically from the ALPN. If we're
|
* information to create one, typically from the ALPN. If we're
|
||||||
* done with the handshake, attempt to create one.
|
* done with the handshake, attempt to create one.
|
||||||
*/
|
*/
|
||||||
if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_QMUX_RECV|CO_FL_QMUX_SEND))) {
|
if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_WAIT_XPRT_L6))) {
|
||||||
ret = conn_create_mux(conn, NULL);
|
ret = conn_create_mux(conn, NULL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -282,6 +282,7 @@ int conn_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
|
||||||
struct ist mux_proto, int mode)
|
struct ist mux_proto, int mode)
|
||||||
{
|
{
|
||||||
struct bind_conf *bind_conf = __objt_listener(conn->target)->bind_conf;
|
struct bind_conf *bind_conf = __objt_listener(conn->target)->bind_conf;
|
||||||
|
struct ist alpn = IST_NULL;
|
||||||
const struct mux_ops *old_mux, *new_mux;
|
const struct mux_ops *old_mux, *new_mux;
|
||||||
void *old_mux_ctx;
|
void *old_mux_ctx;
|
||||||
const char *alpn_str = NULL;
|
const char *alpn_str = NULL;
|
||||||
|
|
@ -289,9 +290,9 @@ int conn_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
|
||||||
|
|
||||||
if (!mux_proto.len) {
|
if (!mux_proto.len) {
|
||||||
conn_get_alpn(conn, &alpn_str, &alpn_len);
|
conn_get_alpn(conn, &alpn_str, &alpn_len);
|
||||||
mux_proto = ist2(alpn_str, alpn_len);
|
alpn = ist2(alpn_str, alpn_len);
|
||||||
}
|
}
|
||||||
new_mux = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_FE, mode);
|
new_mux = conn_get_best_mux(conn, mux_proto, alpn, PROTO_SIDE_FE, mode);
|
||||||
old_mux = conn->mux;
|
old_mux = conn->mux;
|
||||||
|
|
||||||
/* No mux found */
|
/* No mux found */
|
||||||
|
|
@ -318,6 +319,29 @@ int conn_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns the mux_proto_list entry compatible with <conn> frontend connection
|
||||||
|
* or NULL if nothing eligible.
|
||||||
|
* TODO duplicate code to merge with conn_install_mux_fe().
|
||||||
|
*/
|
||||||
|
const struct mux_proto_list *conn_select_mux_fe(const struct connection *conn)
|
||||||
|
{
|
||||||
|
struct bind_conf *bind_conf;
|
||||||
|
const char *alpn_str = NULL;
|
||||||
|
struct ist alpn;
|
||||||
|
int alpn_len = 0, mode;
|
||||||
|
|
||||||
|
bind_conf = __objt_listener(conn->target)->bind_conf;
|
||||||
|
|
||||||
|
if (bind_conf->mux_proto)
|
||||||
|
return bind_conf->mux_proto;
|
||||||
|
|
||||||
|
mode = conn_pr_mode_to_proto_mode(bind_conf->frontend->mode);
|
||||||
|
conn_get_alpn(conn, &alpn_str, &alpn_len);
|
||||||
|
alpn = ist2(alpn_str, alpn_len);
|
||||||
|
return conn_get_best_mux_entry(IST_NULL, alpn, PROTO_SIDE_FE,
|
||||||
|
proto_is_quic(conn->ctrl), mode);
|
||||||
|
}
|
||||||
|
|
||||||
/* installs the best mux for incoming connection <conn> using the upper context
|
/* installs the best mux for incoming connection <conn> using the upper context
|
||||||
* <ctx>. If the mux protocol is forced, we use it to find the best
|
* <ctx>. If the mux protocol is forced, we use it to find the best
|
||||||
* mux. Otherwise we use the ALPN name, if any. Returns < 0 on error.
|
* mux. Otherwise we use the ALPN name, if any. Returns < 0 on error.
|
||||||
|
|
@ -330,14 +354,14 @@ int conn_install_mux_fe(struct connection *conn, void *ctx)
|
||||||
if (bind_conf->mux_proto)
|
if (bind_conf->mux_proto)
|
||||||
mux_ops = bind_conf->mux_proto->mux;
|
mux_ops = bind_conf->mux_proto->mux;
|
||||||
else {
|
else {
|
||||||
struct ist mux_proto;
|
struct ist alpn;
|
||||||
const char *alpn_str = NULL;
|
const char *alpn_str = NULL;
|
||||||
int alpn_len = 0;
|
int alpn_len = 0;
|
||||||
int mode = conn_pr_mode_to_proto_mode(bind_conf->frontend->mode);
|
int mode = conn_pr_mode_to_proto_mode(bind_conf->frontend->mode);
|
||||||
|
|
||||||
conn_get_alpn(conn, &alpn_str, &alpn_len);
|
conn_get_alpn(conn, &alpn_str, &alpn_len);
|
||||||
mux_proto = ist2(alpn_str, alpn_len);
|
alpn = ist2(alpn_str, alpn_len);
|
||||||
mux_ops = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_FE, mode);
|
mux_ops = conn_get_best_mux(conn, IST_NULL, alpn, PROTO_SIDE_FE, mode);
|
||||||
if (!mux_ops)
|
if (!mux_ops)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -353,6 +377,66 @@ int conn_install_mux_fe(struct connection *conn, void *ctx)
|
||||||
return conn_install_mux(conn, mux_ops, ctx, bind_conf->frontend, conn->owner);
|
return conn_install_mux(conn, mux_ops, ctx, bind_conf->frontend, conn->owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns the mux_proto_list entry compatible with <conn> backend connection
|
||||||
|
* or NULL if nothing eligible.
|
||||||
|
* TODO duplicate code to merge with conn_install_mux_be/chk().
|
||||||
|
*/
|
||||||
|
const struct mux_proto_list *conn_select_mux_be(const struct connection *conn)
|
||||||
|
{
|
||||||
|
struct session *sess;
|
||||||
|
struct server *srv;
|
||||||
|
struct proxy *prx;
|
||||||
|
struct check *check;
|
||||||
|
struct ist alpn;
|
||||||
|
const char *alpn_str = NULL;
|
||||||
|
int alpn_len = 0, mode;
|
||||||
|
|
||||||
|
sess = conn->owner;
|
||||||
|
if (sess && obj_type(sess->origin) == OBJ_TYPE_CHECK) {
|
||||||
|
check = __objt_check(sess->origin);
|
||||||
|
if (check->mux_proto)
|
||||||
|
return check->mux_proto;
|
||||||
|
|
||||||
|
mode = tcpchk_rules_type_to_proto_mode(check->tcpcheck->rs->flags);
|
||||||
|
|
||||||
|
conn_get_alpn(conn, &alpn_str, &alpn_len);
|
||||||
|
alpn = ist2(alpn_str, alpn_len);
|
||||||
|
|
||||||
|
return conn_get_best_mux_entry(IST_NULL, alpn, PROTO_SIDE_BE,
|
||||||
|
proto_is_quic(conn->ctrl), mode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
srv = objt_server(conn->target);
|
||||||
|
prx = objt_proxy(conn->target);
|
||||||
|
if (srv)
|
||||||
|
prx = srv->proxy;
|
||||||
|
|
||||||
|
if (!prx) {
|
||||||
|
/* Target should either be a server or a proxy.
|
||||||
|
* USE a full a BUG_ON() once considered definitive.
|
||||||
|
*/
|
||||||
|
BUG_ON_HOT(1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode = conn_pr_mode_to_proto_mode(prx->mode);
|
||||||
|
|
||||||
|
if (srv && srv->mux_proto)
|
||||||
|
return srv->mux_proto;
|
||||||
|
|
||||||
|
if (!conn_get_alpn(conn, &alpn_str, &alpn_len)) {
|
||||||
|
if (srv && srv->path_params.nego_alpn[0]) {
|
||||||
|
alpn_str = srv->path_params.nego_alpn;
|
||||||
|
alpn_len = strlen(alpn_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alpn = ist2(alpn_str, alpn_len);
|
||||||
|
|
||||||
|
return conn_get_best_mux_entry(IST_NULL, alpn, PROTO_SIDE_BE,
|
||||||
|
proto_is_quic(conn->ctrl), mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* installs the best mux for outgoing connection <conn> using the upper context
|
/* installs the best mux for outgoing connection <conn> using the upper context
|
||||||
* <ctx>. If the server mux protocol is forced, we use it to find the best mux.
|
* <ctx>. If the server mux protocol is forced, we use it to find the best mux.
|
||||||
* It's also possible to specify an alternative mux protocol <force_mux_ops>,
|
* It's also possible to specify an alternative mux protocol <force_mux_ops>,
|
||||||
|
|
@ -380,20 +464,20 @@ int conn_install_mux_be(struct connection *conn, void *ctx, struct session *sess
|
||||||
mux_ops = force_mux_ops;
|
mux_ops = force_mux_ops;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
struct ist mux_proto;
|
struct ist alpn;
|
||||||
const char *alpn_str = NULL;
|
const char *alpn_str = NULL;
|
||||||
int alpn_len = 0;
|
int alpn_len = 0;
|
||||||
int mode = conn_pr_mode_to_proto_mode(prx->mode);
|
int mode = conn_pr_mode_to_proto_mode(prx->mode);
|
||||||
|
|
||||||
if (!conn_get_alpn(conn, &alpn_str, &alpn_len)) {
|
if (!conn_get_alpn(conn, &alpn_str, &alpn_len)) {
|
||||||
if (srv && srv->path_params.nego_alpn[0]) {
|
if (srv && srv->path_params.srv_hash == conn->hash_node.key && srv->path_params.nego_alpn[0]) {
|
||||||
alpn_str = srv->path_params.nego_alpn;
|
alpn_str = srv->path_params.nego_alpn;
|
||||||
alpn_len = strlen(alpn_str);
|
alpn_len = strlen(alpn_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mux_proto = ist2(alpn_str, alpn_len);
|
alpn = ist2(alpn_str, alpn_len);
|
||||||
|
|
||||||
mux_ops = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_BE, mode);
|
mux_ops = conn_get_best_mux(conn, IST_NULL, alpn, PROTO_SIDE_BE, mode);
|
||||||
if (!mux_ops)
|
if (!mux_ops)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -434,15 +518,15 @@ int conn_install_mux_chk(struct connection *conn, void *ctx, struct session *ses
|
||||||
if (check->mux_proto)
|
if (check->mux_proto)
|
||||||
mux_ops = check->mux_proto->mux;
|
mux_ops = check->mux_proto->mux;
|
||||||
else {
|
else {
|
||||||
struct ist mux_proto;
|
struct ist alpn;
|
||||||
const char *alpn_str = NULL;
|
const char *alpn_str = NULL;
|
||||||
int alpn_len = 0;
|
int alpn_len = 0;
|
||||||
int mode = tcpchk_rules_type_to_proto_mode(check->tcpcheck->rs->flags);
|
int mode = tcpchk_rules_type_to_proto_mode(check->tcpcheck->rs->flags);
|
||||||
|
|
||||||
conn_get_alpn(conn, &alpn_str, &alpn_len);
|
conn_get_alpn(conn, &alpn_str, &alpn_len);
|
||||||
mux_proto = ist2(alpn_str, alpn_len);
|
alpn = ist2(alpn_str, alpn_len);
|
||||||
|
|
||||||
mux_ops = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_BE, mode);
|
mux_ops = conn_get_best_mux(conn, IST_NULL, alpn, PROTO_SIDE_BE, mode);
|
||||||
if (!mux_ops)
|
if (!mux_ops)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -763,6 +847,43 @@ int xprt_add_hs(struct connection *conn)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Activates an <xprt> layer on top of <conn> connection. This handshake layer
|
||||||
|
* should be designed to work on top of the layer 6. If SSL is active and its
|
||||||
|
* handshake still in progress, this function does nothing.
|
||||||
|
*
|
||||||
|
* Returns 0 on success else a negative error code.
|
||||||
|
*/
|
||||||
|
int xprt_add_l6hs(struct connection *conn, int xprt)
|
||||||
|
{
|
||||||
|
const struct xprt_ops *ops = xprt_get(xprt);
|
||||||
|
void *ops_ctx = NULL;
|
||||||
|
|
||||||
|
/* Only QMux is supported as handshake on top of layer6 for now. */
|
||||||
|
BUG_ON(xprt != XPRT_QMUX);
|
||||||
|
|
||||||
|
if (conn->flags & CO_FL_ERROR)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Do nothing if SSL is in used but handshake still in progress. In
|
||||||
|
* this case, xprt layer will be added on handshake completion.
|
||||||
|
*/
|
||||||
|
if (conn->xprt == xprt_get(XPRT_SSL) &&
|
||||||
|
(conn->flags & CO_FL_WAIT_L6_CONN)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ops->init(conn, &ops_ctx))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ops->add_xprt(conn, ops_ctx, conn->xprt_ctx, conn->xprt, NULL, NULL);
|
||||||
|
conn->xprt = ops;
|
||||||
|
conn->xprt_ctx = ops_ctx;
|
||||||
|
/* Reset XPRT READY flag before the next conn_xprt_start(). */
|
||||||
|
conn->flags &= ~CO_FL_XPRT_READY;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* returns a short name for an error, typically the same as the enum name
|
/* returns a short name for an error, typically the same as the enum name
|
||||||
* without the "CO_ER_" prefix, or an empty string for no error (better eye
|
* without the "CO_ER_" prefix, or an empty string for no error (better eye
|
||||||
* catching in logs). This is more compact for some debug cases.
|
* catching in logs). This is more compact for some debug cases.
|
||||||
|
|
@ -1991,7 +2112,7 @@ void list_mux_proto(FILE *out)
|
||||||
fprintf(out, "Available multiplexer protocols :\n"
|
fprintf(out, "Available multiplexer protocols :\n"
|
||||||
"(protocols marked as <default> cannot be specified using 'proto' keyword)\n");
|
"(protocols marked as <default> cannot be specified using 'proto' keyword)\n");
|
||||||
list_for_each_entry(item, &mux_proto_list.list, list) {
|
list_for_each_entry(item, &mux_proto_list.list, list) {
|
||||||
proto = item->token;
|
proto = item->mux_proto;
|
||||||
|
|
||||||
if (item->mode == PROTO_MODE_ANY)
|
if (item->mode == PROTO_MODE_ANY)
|
||||||
mode = "TCP|HTTP";
|
mode = "TCP|HTTP";
|
||||||
|
|
|
||||||
24
src/dict.c
24
src/dict.c
|
|
@ -79,7 +79,7 @@ static struct dict_entry *__dict_lookup(struct dict *d, const char *s)
|
||||||
*/
|
*/
|
||||||
struct dict_entry *dict_insert(struct dict *d, char *s)
|
struct dict_entry *dict_insert(struct dict *d, char *s)
|
||||||
{
|
{
|
||||||
struct dict_entry *de;
|
struct dict_entry *de, *tree_de;
|
||||||
struct ebpt_node *n;
|
struct ebpt_node *n;
|
||||||
|
|
||||||
HA_RWLOCK_RDLOCK(DICT_LOCK, &d->rwlock);
|
HA_RWLOCK_RDLOCK(DICT_LOCK, &d->rwlock);
|
||||||
|
|
@ -97,13 +97,18 @@ struct dict_entry *dict_insert(struct dict *d, char *s)
|
||||||
|
|
||||||
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
||||||
n = ebis_insert(&d->values, &de->value);
|
n = ebis_insert(&d->values, &de->value);
|
||||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
tree_de = container_of(n, struct dict_entry, value);
|
||||||
if (n != &de->value) {
|
if (tree_de == de)
|
||||||
|
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||||
|
else {
|
||||||
|
/* another entry was already there, we'll return it, kill
|
||||||
|
* ours and bump the other's refcount before returning it.
|
||||||
|
*/
|
||||||
|
HA_ATOMIC_INC(&tree_de->refcount);
|
||||||
|
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||||
free_dict_entry(de);
|
free_dict_entry(de);
|
||||||
de = container_of(n, struct dict_entry, value);
|
|
||||||
}
|
}
|
||||||
|
return tree_de;
|
||||||
return de;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -117,10 +122,11 @@ void dict_entry_unref(struct dict *d, struct dict_entry *de)
|
||||||
if (!de)
|
if (!de)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
||||||
|
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0) {
|
||||||
|
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
ebpt_delete(&de->value);
|
ebpt_delete(&de->value);
|
||||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||||
|
|
||||||
|
|
|
||||||
19
src/dns.c
19
src/dns.c
|
|
@ -194,7 +194,7 @@ int dns_send_nameserver(struct dns_nameserver *ns, void *buf, size_t len)
|
||||||
struct ist myist;
|
struct ist myist;
|
||||||
|
|
||||||
myist = ist2(buf, len);
|
myist = ist2(buf, len);
|
||||||
ret = dns_ring_write(ns->stream->ring_req, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
|
ret = dns_ring_write(ns->stream->ring_req, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ns->counters->snd_error++;
|
ns->counters->snd_error++;
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -215,7 +215,7 @@ void dns_session_free(struct dns_session *);
|
||||||
*/
|
*/
|
||||||
ssize_t dns_recv_nameserver(struct dns_nameserver *ns, void *data, size_t size)
|
ssize_t dns_recv_nameserver(struct dns_nameserver *ns, void *data, size_t size)
|
||||||
{
|
{
|
||||||
ssize_t ret = -1;
|
ssize_t ret = -1;
|
||||||
|
|
||||||
if (ns->dgram) {
|
if (ns->dgram) {
|
||||||
struct dgram_conn *dgram = &ns->dgram->conn;
|
struct dgram_conn *dgram = &ns->dgram->conn;
|
||||||
|
|
@ -475,7 +475,6 @@ int dns_dgram_init(struct dns_nameserver *ns, struct sockaddr_storage *sk)
|
||||||
dgram->conn.t.sock.fd = -1;
|
dgram->conn.t.sock.fd = -1;
|
||||||
dgram->conn.addr.to = *sk;
|
dgram->conn.addr.to = *sk;
|
||||||
HA_SPIN_INIT(&dgram->conn.lock);
|
HA_SPIN_INIT(&dgram->conn.lock);
|
||||||
ns->dgram = dgram;
|
|
||||||
|
|
||||||
dgram->ofs_req = ~0; /* init ring offset */
|
dgram->ofs_req = ~0; /* init ring offset */
|
||||||
dgram->ring_req = dns_ring_new(2*DNS_TCP_MSG_RING_MAX_SIZE);
|
dgram->ring_req = dns_ring_new(2*DNS_TCP_MSG_RING_MAX_SIZE);
|
||||||
|
|
@ -490,6 +489,7 @@ int dns_dgram_init(struct dns_nameserver *ns, struct sockaddr_storage *sk)
|
||||||
ha_alert("nameserver sets too many watchers > 255 on ring. This is a bug and should not happen.\n");
|
ha_alert("nameserver sets too many watchers > 255 on ring. This is a bug and should not happen.\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
ns->dgram = dgram;
|
||||||
return 0;
|
return 0;
|
||||||
out:
|
out:
|
||||||
dns_ring_free(dgram->ring_req);
|
dns_ring_free(dgram->ring_req);
|
||||||
|
|
@ -913,6 +913,7 @@ static int dns_session_init(struct appctx *appctx)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
sockaddr_free(&addr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1345,8 +1346,8 @@ int dns_stream_init(struct dns_nameserver *ns, struct server *srv)
|
||||||
{
|
{
|
||||||
struct dns_stream_server *dss = NULL;
|
struct dns_stream_server *dss = NULL;
|
||||||
|
|
||||||
dss = calloc(1, sizeof(*dss));
|
dss = calloc(1, sizeof(*dss));
|
||||||
if (!dss) {
|
if (!dss) {
|
||||||
ha_alert("memory allocation error initializing dns tcp server '%s'.\n", srv->id);
|
ha_alert("memory allocation error initializing dns tcp server '%s'.\n", srv->id);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -1362,7 +1363,7 @@ int dns_stream_init(struct dns_nameserver *ns, struct server *srv)
|
||||||
}
|
}
|
||||||
/* Create the task associated to the resolver target handling conns */
|
/* Create the task associated to the resolver target handling conns */
|
||||||
if ((dss->task_req = task_new_anywhere()) == NULL) {
|
if ((dss->task_req = task_new_anywhere()) == NULL) {
|
||||||
ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
|
ha_alert("memory allocation error initializing req task for dns tcp server '%s'.\n", srv->id);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1379,7 +1380,7 @@ int dns_stream_init(struct dns_nameserver *ns, struct server *srv)
|
||||||
|
|
||||||
/* Create the task associated to the resolver target handling conns */
|
/* Create the task associated to the resolver target handling conns */
|
||||||
if ((dss->task_rsp = task_new_anywhere()) == NULL) {
|
if ((dss->task_rsp = task_new_anywhere()) == NULL) {
|
||||||
ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
|
ha_alert("memory allocation error initializing rsp task for dns tcp server '%s'.\n", srv->id);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1389,7 +1390,7 @@ int dns_stream_init(struct dns_nameserver *ns, struct server *srv)
|
||||||
|
|
||||||
/* Create the task associated to the resolver target handling conns */
|
/* Create the task associated to the resolver target handling conns */
|
||||||
if ((dss->task_idle = task_new_anywhere()) == NULL) {
|
if ((dss->task_idle = task_new_anywhere()) == NULL) {
|
||||||
ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
|
ha_alert("memory allocation error initializing idle task for dns tcp server '%s'.\n", srv->id);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1425,7 +1426,7 @@ int init_dns_buffers()
|
||||||
if (!dns_msg_trash)
|
if (!dns_msg_trash)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void deinit_dns_buffers()
|
void deinit_dns_buffers()
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ static void usermsgs_put(const struct ist *msg)
|
||||||
{
|
{
|
||||||
/* Allocate the buffer if not already done. */
|
/* Allocate the buffer if not already done. */
|
||||||
if (unlikely(b_is_null(&usermsgs_buf))) {
|
if (unlikely(b_is_null(&usermsgs_buf))) {
|
||||||
usermsgs_buf.area = malloc(USER_MESSAGES_BUFSIZE * sizeof(char));
|
usermsgs_buf.area = malloc(array_size_or_fail(USER_MESSAGES_BUFSIZE, sizeof(char)));
|
||||||
if (usermsgs_buf.area)
|
if (usermsgs_buf.area)
|
||||||
usermsgs_buf.size = USER_MESSAGES_BUFSIZE;
|
usermsgs_buf.size = USER_MESSAGES_BUFSIZE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@ int prepare_external_check(struct check *check)
|
||||||
case PR_MODE_CLI: svmode = "cli"; break;
|
case PR_MODE_CLI: svmode = "cli"; break;
|
||||||
case PR_MODE_SYSLOG: svmode = "syslog"; break;
|
case PR_MODE_SYSLOG: svmode = "syslog"; break;
|
||||||
case PR_MODE_PEERS: svmode = "peers"; break;
|
case PR_MODE_PEERS: svmode = "peers"; break;
|
||||||
case PR_MODE_HTTP: svmode = (s->mux_proto) ? s->mux_proto->token.ptr : "h1"; break;
|
case PR_MODE_HTTP: svmode = (s->mux_proto) ? s->mux_proto->mux_proto.ptr : "h1"; break;
|
||||||
case PR_MODE_TCP: svmode = "tcp"; break;
|
case PR_MODE_TCP: svmode = "tcp"; break;
|
||||||
case PR_MODE_SPOP: svmode = "spop"; break;
|
case PR_MODE_SPOP: svmode = "spop"; break;
|
||||||
/* all valid cases must be enumerated above, below is to avoid a warning */
|
/* all valid cases must be enumerated above, below is to avoid a warning */
|
||||||
|
|
|
||||||
|
|
@ -644,7 +644,7 @@ static int cfg_fcgi_apps_postparser()
|
||||||
px->options2 |= PR_O2_RSTRICT_REQ_HDR_NAMES_DEL;
|
px->options2 |= PR_O2_RSTRICT_REQ_HDR_NAMES_DEL;
|
||||||
|
|
||||||
for (srv = px->srv; srv; srv = srv->next) {
|
for (srv = px->srv; srv; srv = srv->next) {
|
||||||
if (srv->mux_proto && isteq(srv->mux_proto->token, ist("fcgi"))) {
|
if (srv->mux_proto && isteq(srv->mux_proto->mux_proto, ist("fcgi"))) {
|
||||||
nb_fcgi_srv++;
|
nb_fcgi_srv++;
|
||||||
if (fcgi_conf)
|
if (fcgi_conf)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
2
src/fd.c
2
src/fd.c
|
|
@ -1166,7 +1166,7 @@ int init_pollers()
|
||||||
struct poller *bp;
|
struct poller *bp;
|
||||||
|
|
||||||
/* always provide an aligned fdtab */
|
/* always provide an aligned fdtab */
|
||||||
if ((fdtab = ha_aligned_zalloc(64, global.maxsock * sizeof(*fdtab))) == NULL) {
|
if ((fdtab = ha_aligned_zalloc(64, array_size_or_fail(global.maxsock, sizeof(*fdtab)))) == NULL) {
|
||||||
ha_alert("Not enough memory to allocate %d entries for fdtab!\n", global.maxsock);
|
ha_alert("Not enough memory to allocate %d entries for fdtab!\n", global.maxsock);
|
||||||
goto fail_tab;
|
goto fail_tab;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
35
src/h1.c
35
src/h1.c
|
|
@ -274,17 +274,17 @@ void h1_parse_connection_header(struct h1m *h1m, struct ist *value)
|
||||||
|
|
||||||
/* Parse the Upgrade: header of an HTTP/1 request.
|
/* Parse the Upgrade: header of an HTTP/1 request.
|
||||||
* If "websocket" is found, set H1_MF_UPG_WEBSOCKET flag
|
* If "websocket" is found, set H1_MF_UPG_WEBSOCKET flag
|
||||||
* If "h2c" or "h2" found, set H1_MF_UPG_H2C flag.
|
* If "h2c" or "h2" found, the value is skipped.
|
||||||
*/
|
*/
|
||||||
void h1_parse_upgrade_header(struct h1m *h1m, struct ist value)
|
void h1_parse_upgrade_header(struct h1m *h1m, struct ist *value)
|
||||||
{
|
{
|
||||||
char *e, *n;
|
char *e, *n, *p;
|
||||||
struct ist word;
|
struct ist word;
|
||||||
|
|
||||||
h1m->flags &= ~(H1_MF_UPG_WEBSOCKET|H1_MF_UPG_H2C);
|
word.ptr = value->ptr - 1; // -1 for next loop's pre-increment
|
||||||
|
p = value->ptr;
|
||||||
word.ptr = value.ptr - 1; // -1 for next loop's pre-increment
|
e = value->ptr + value->len;
|
||||||
e = istend(value);
|
value->len = 0;
|
||||||
|
|
||||||
while (++word.ptr < e) {
|
while (++word.ptr < e) {
|
||||||
/* skip leading delimiter and blanks */
|
/* skip leading delimiter and blanks */
|
||||||
|
|
@ -301,9 +301,20 @@ void h1_parse_upgrade_header(struct h1m *h1m, struct ist value)
|
||||||
if (isteqi(word, ist("websocket")))
|
if (isteqi(word, ist("websocket")))
|
||||||
h1m->flags |= H1_MF_UPG_WEBSOCKET;
|
h1m->flags |= H1_MF_UPG_WEBSOCKET;
|
||||||
else if (isteqi(word, ist("h2c")) || isteqi(word, ist("h2")))
|
else if (isteqi(word, ist("h2c")) || isteqi(word, ist("h2")))
|
||||||
h1m->flags |= H1_MF_UPG_H2C;
|
goto skip_val;
|
||||||
|
|
||||||
word.ptr = n;
|
if (value->ptr + value->len == p) {
|
||||||
|
/* no rewrite done till now */
|
||||||
|
value->len = n - value->ptr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (value->len)
|
||||||
|
value->ptr[value->len++] = ',';
|
||||||
|
istcat(value, word, e - value->ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_val:
|
||||||
|
word.ptr = p = n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -983,7 +994,11 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isteqi(n, ist("upgrade"))) {
|
else if (isteqi(n, ist("upgrade"))) {
|
||||||
h1_parse_upgrade_header(h1m, v);
|
h1_parse_upgrade_header(h1m, &v);
|
||||||
|
if (!v.len) {
|
||||||
|
/* skip it */
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!(h1m->flags & H1_MF_RESP) && isteqi(n, ist("host"))) {
|
else if (!(h1m->flags & H1_MF_RESP) && isteqi(n, ist("host"))) {
|
||||||
if (host_idx == -1) {
|
if (host_idx == -1) {
|
||||||
|
|
|
||||||
14
src/h1_htx.c
14
src/h1_htx.c
|
|
@ -215,20 +215,6 @@ static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx
|
||||||
|
|
||||||
flags |= h1m_htx_sl_flags(h1m);
|
flags |= h1m_htx_sl_flags(h1m);
|
||||||
|
|
||||||
/* Remove Upgrade header in problematic cases :
|
|
||||||
* - "h2c" or "h2" token specified as token
|
|
||||||
*/
|
|
||||||
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_H2C)) == (H1_MF_CONN_UPG|H1_MF_UPG_H2C)) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; hdrs[i].n.len; i++) {
|
|
||||||
if (isteqi(hdrs[i].n, ist("upgrade")))
|
|
||||||
hdrs[i].v = IST_NULL;
|
|
||||||
}
|
|
||||||
h1m->flags &=~ H1_MF_CONN_UPG;
|
|
||||||
flags &= ~HTX_SL_F_CONN_UPG;
|
|
||||||
}
|
|
||||||
|
|
||||||
sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);
|
sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);
|
||||||
if (!sl || !htx_add_all_headers(htx, hdrs))
|
if (!sl || !htx_add_all_headers(htx, hdrs))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
|
||||||
176
src/haproxy.c
176
src/haproxy.c
|
|
@ -1713,7 +1713,7 @@ void haproxy_init_args(int argc, char **argv)
|
||||||
oldpids_sig = SIGTERM; /* terminate immediately */
|
oldpids_sig = SIGTERM; /* terminate immediately */
|
||||||
while (argc > 1 && argv[1][0] != '-') {
|
while (argc > 1 && argv[1][0] != '-') {
|
||||||
char * endptr = NULL;
|
char * endptr = NULL;
|
||||||
oldpids = realloc(oldpids, (nb_oldpids + 1) * sizeof(int));
|
oldpids = realloc(oldpids, array_size_or_fail(nb_oldpids + 1, sizeof(int)));
|
||||||
if (!oldpids) {
|
if (!oldpids) {
|
||||||
ha_alert("Cannot allocate old pid : out of memory.\n");
|
ha_alert("Cannot allocate old pid : out of memory.\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
@ -1926,20 +1926,37 @@ static void dump_registered_keywords(void)
|
||||||
|
|
||||||
/* Generate a random cluster-secret in case the setting is not provided in the
|
/* Generate a random cluster-secret in case the setting is not provided in the
|
||||||
* configuration. This allows to use features which rely on it albeit with some
|
* configuration. This allows to use features which rely on it albeit with some
|
||||||
* limitations.
|
* limitations. The function doesn't (solely) use ha_random64() because this
|
||||||
|
* secret is permanent, and ha_random64() can easily be leaked at various
|
||||||
|
* places.
|
||||||
*/
|
*/
|
||||||
static void generate_random_cluster_secret()
|
static void generate_random_cluster_secret()
|
||||||
{
|
{
|
||||||
/* used as a default random cluster-secret if none defined. */
|
/* used as a default random cluster-secret if none defined. */
|
||||||
uint64_t rand;
|
union {
|
||||||
|
uint64_t by64[2];
|
||||||
|
uint32_t by32[4];
|
||||||
|
uchar by8[16];
|
||||||
|
} rand;
|
||||||
|
|
||||||
/* The caller must not overwrite an already defined secret. */
|
/* The caller must not overwrite an already defined secret. */
|
||||||
BUG_ON(cluster_secret_isset);
|
BUG_ON(cluster_secret_isset);
|
||||||
|
BUG_ON(sizeof(global.cluster_secret) != sizeof(rand));
|
||||||
|
|
||||||
|
#ifdef USE_OPENSSL
|
||||||
|
if (RAND_bytes(rand.by8, sizeof(rand.by8)) != 1)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/* no SSL or not working, fall back to other sources */
|
||||||
|
rand.by64[0] = ha_random64();
|
||||||
|
rand.by64[1] = ha_random64();
|
||||||
|
rand.by32[0] ^= ((random() & 0x00ffff00) << 8) | ((random() & 0x00ffff00) >> 8);
|
||||||
|
rand.by32[1] ^= ((random() & 0x00ffff00) << 8) | ((random() & 0x00ffff00) >> 8);
|
||||||
|
rand.by32[2] ^= ((random() & 0x00ffff00) << 8) | ((random() & 0x00ffff00) >> 8);
|
||||||
|
rand.by32[3] ^= ((random() & 0x00ffff00) << 8) | ((random() & 0x00ffff00) >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
rand = ha_random64();
|
|
||||||
memcpy(global.cluster_secret, &rand, sizeof(rand));
|
memcpy(global.cluster_secret, &rand, sizeof(rand));
|
||||||
rand = ha_random64();
|
|
||||||
memcpy(global.cluster_secret + sizeof(rand), &rand, sizeof(rand));
|
|
||||||
cluster_secret_isset = 1;
|
cluster_secret_isset = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3266,6 +3283,105 @@ static void set_identity(const char *program_name)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(CLONE_NEWUSER)
|
||||||
|
/* Setup the user namespace after a successful unshare(CLONE_NEWUSER). We do not
|
||||||
|
* return a value because this is best-effort; it is only useful in very rare
|
||||||
|
* situations (see below), and if it fails, we let subsequent setuid() and/or
|
||||||
|
* setgid() calls fail later.
|
||||||
|
*/
|
||||||
|
static void setup_user_ns(uid_t euid, gid_t egid)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
int n, ret, fd;
|
||||||
|
|
||||||
|
/* Creating uid_map and gid_map files is required for some specific
|
||||||
|
* situations where we attempt to setuid()/setgid() to the user/group
|
||||||
|
* we are already running as after a successful unshare(CLONE_NEWUSER).
|
||||||
|
* While these directives would effectively be no-ops, we still support
|
||||||
|
* them because it is possible that such setups exist in the wild. For
|
||||||
|
* instance, if haproxy is run through a systemd file containing
|
||||||
|
* "User=someuser" while the configuration file has "user someuser", we
|
||||||
|
* would be in this situation, and a user enabling "chroot auto" in this
|
||||||
|
* case would end up with seemingly unrelated setuid() failures.
|
||||||
|
*
|
||||||
|
* See user_namespaces(7) for more information.
|
||||||
|
*/
|
||||||
|
if (global.uid > 0) {
|
||||||
|
n = snprintf(buf, sizeof(buf), "%u %u 1\n", euid, euid);
|
||||||
|
fd = open("/proc/self/uid_map", O_WRONLY);
|
||||||
|
if (fd == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret = write(fd, buf, n);
|
||||||
|
close(fd);
|
||||||
|
if (ret != n)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global.gid > 0) {
|
||||||
|
/* In order to write to the gid_map file, we first need to write
|
||||||
|
* "deny" to the setgroups file. We allow for failure because
|
||||||
|
* older kernels do not support the setgroups file.
|
||||||
|
*/
|
||||||
|
fd = open("/proc/self/setgroups", O_WRONLY);
|
||||||
|
if (fd != -1) {
|
||||||
|
ret = write(fd, "deny", 4);
|
||||||
|
close(fd);
|
||||||
|
if (ret != 4)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = snprintf(buf, sizeof(buf), "%u %u 1\n", egid, egid);
|
||||||
|
fd = open("/proc/self/gid_map", O_WRONLY);
|
||||||
|
if (fd == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret = write(fd, buf, n);
|
||||||
|
close(fd);
|
||||||
|
if (ret != n)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int do_chroot(const char *prog, const char *path)
|
||||||
|
{
|
||||||
|
const char *chroot_dir = path;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
if (strcmp(path, "auto") == 0) {
|
||||||
|
/* When "chroot auto" is used, we attempt to chroot to an
|
||||||
|
* anonymous and read-only directory.
|
||||||
|
*/
|
||||||
|
char tmpdir[] = "/tmp/haproxy.XXXXXX";
|
||||||
|
chroot_dir = mkdtemp(tmpdir);
|
||||||
|
if (chroot_dir == NULL) {
|
||||||
|
ha_alert("[%s.main()] Cannot create(%s) for chroot auto.\n",
|
||||||
|
prog, tmpdir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
error = chdir(tmpdir);
|
||||||
|
/* We can call rmdir() here; we hold a reference to the
|
||||||
|
* directory since it is our CWD (and if chdir() failed we still
|
||||||
|
* want to remove the directory).
|
||||||
|
*/
|
||||||
|
DISGUISE(rmdir(tmpdir));
|
||||||
|
if (!error)
|
||||||
|
error = chroot(".");
|
||||||
|
} else if (strcmp(path, "/") != 0) {
|
||||||
|
error = chroot(path);
|
||||||
|
}
|
||||||
|
if (!error)
|
||||||
|
error = chdir("/");
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
ha_alert("[%s.main()] Cannot chroot(%s).\n", prog, chroot_dir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct rlimit limit;
|
struct rlimit limit;
|
||||||
|
|
@ -3582,16 +3698,48 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* privileged users should use chroot whenever possible; use chroot /
|
||||||
|
* if really not wanted.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!global.chroot) {
|
||||||
|
int chroot_permitted = geteuid() == 0;
|
||||||
|
|
||||||
|
#if defined(USE_PRCTL) && defined(PR_CAPBSET_READ) && defined(CAP_SYS_CHROOT)
|
||||||
|
chroot_permitted &= (prctl(PR_CAPBSET_READ, CAP_SYS_CHROOT, 0, 0, 0) == 1);
|
||||||
|
#endif
|
||||||
|
if (chroot_permitted) {
|
||||||
|
ha_warning("[%s.main()] HAProxy was started as root without any 'chroot' "
|
||||||
|
"directive. A chroot limits filesystem access of an intruder "
|
||||||
|
"to a single, preferably empty, directory. It is strongly recommended "
|
||||||
|
"to enable this feature whenever possible (it's always possible when "
|
||||||
|
"starting as root), via 'chroot auto' in the global section. If you "
|
||||||
|
"think you have good reasons for running outside a chroot, explicitly "
|
||||||
|
"configure 'chroot /' to silence this warning.\n", argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CLONE_NEWUSER
|
||||||
|
/* When we aren't root and intend to chroot, we try the Linux-only
|
||||||
|
* unshare(CLONE_NEWUSER) mechanism if available to allow chroot as an
|
||||||
|
* unprivileged user. If that doesn't work, we just let the subsequent
|
||||||
|
* chroot() fail as it would have previously.
|
||||||
|
*/
|
||||||
|
if (geteuid() != 0 && global.chroot != NULL) {
|
||||||
|
uid_t euid = geteuid();
|
||||||
|
gid_t egid = getegid();
|
||||||
|
if (unshare(CLONE_NEWUSER) == 0)
|
||||||
|
setup_user_ns(euid, egid);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Must chroot and setgid/setuid in the children */
|
/* Must chroot and setgid/setuid in the children */
|
||||||
/* chroot if needed */
|
/* chroot if needed */
|
||||||
if (global.chroot != NULL) {
|
if (global.chroot != NULL && do_chroot(argv[0], global.chroot) != 0) {
|
||||||
if (chroot(global.chroot) == -1 || chdir("/") == -1) {
|
if (nb_oldpids)
|
||||||
ha_alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot);
|
tell_old_pids(SIGTTIN);
|
||||||
if (nb_oldpids)
|
protocol_unbind_all();
|
||||||
tell_old_pids(SIGTTIN);
|
exit(1);
|
||||||
protocol_unbind_all();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ha_free(&global.chroot);
|
ha_free(&global.chroot);
|
||||||
|
|
|
||||||
17
src/haterm.c
17
src/haterm.c
|
|
@ -12,7 +12,7 @@
|
||||||
#include <haproxy/istbuf.h>
|
#include <haproxy/istbuf.h>
|
||||||
#include <haproxy/pipe.h>
|
#include <haproxy/pipe.h>
|
||||||
#include <haproxy/pool.h>
|
#include <haproxy/pool.h>
|
||||||
#include <haproxy/proxy-t.h>
|
#include <haproxy/proxy.h>
|
||||||
#include <haproxy/sc_strm.h>
|
#include <haproxy/sc_strm.h>
|
||||||
#include <haproxy/stconn-t.h>
|
#include <haproxy/stconn-t.h>
|
||||||
#include <haproxy/stream.h>
|
#include <haproxy/stream.h>
|
||||||
|
|
@ -788,7 +788,7 @@ static void hstream_parse_uri(struct ist uri, struct hstream *hs)
|
||||||
} while (*next);
|
} while (*next);
|
||||||
|
|
||||||
if (use_rand)
|
if (use_rand)
|
||||||
result = ((long long)ha_random64() * result) / ((long long)RAND_MAX + 1);
|
result = ((long long)statistical_prng() * result) / 0xFFFFFFFFU;
|
||||||
|
|
||||||
switch (*arg) {
|
switch (*arg) {
|
||||||
case 's':
|
case 's':
|
||||||
|
|
@ -1223,11 +1223,22 @@ static int hstream_build_responses(void)
|
||||||
#if defined(USE_LINUX_SPLICE)
|
#if defined(USE_LINUX_SPLICE)
|
||||||
static void hstream_init_splicing(void)
|
static void hstream_init_splicing(void)
|
||||||
{
|
{
|
||||||
|
struct proxy *px;
|
||||||
unsigned int pipesize = 65536;
|
unsigned int pipesize = 65536;
|
||||||
|
int haterm_used = 0;
|
||||||
|
|
||||||
if (!(global.tune.options & GTUNE_USE_SPLICE) || !global.maxpipes)
|
if (!(global.tune.options & GTUNE_USE_SPLICE) || !global.maxpipes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
for (px = proxies_list; px; px = px->next) {
|
||||||
|
if ((px->cap & PR_CAP_FE) && !(px->flags & PR_FL_DISABLED) && px->stream_new_from_sc == hstream_new) {
|
||||||
|
haterm_used = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!haterm_used)
|
||||||
|
return;
|
||||||
|
|
||||||
if (global.tune.pipesize)
|
if (global.tune.pipesize)
|
||||||
pipesize = global.tune.pipesize;
|
pipesize = global.tune.pipesize;
|
||||||
|
|
||||||
|
|
@ -1257,7 +1268,7 @@ static void hstream_init_splicing(void)
|
||||||
master_pipesize = 0;
|
master_pipesize = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ha_warning("Splicing in haterm is limited to %lu bytes (too old kernel)\n", (ulong)master_pipesize);
|
ha_warning("Splicing in haterm is limited to %lu bytes\n", (ulong)master_pipesize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -2949,20 +2949,20 @@ __LJMP static int hlua_socket_receive_yield(struct lua_State *L, int status, lua
|
||||||
|
|
||||||
/* remove final \r\n. */
|
/* remove final \r\n. */
|
||||||
if (nblk == 1) {
|
if (nblk == 1) {
|
||||||
if (blk1[len1-1] == '\n') {
|
if (len1 && blk1[len1-1] == '\n') {
|
||||||
len1--;
|
len1--;
|
||||||
skip_at_end++;
|
skip_at_end++;
|
||||||
if (blk1[len1-1] == '\r') {
|
if (len1 && blk1[len1-1] == '\r') {
|
||||||
len1--;
|
len1--;
|
||||||
skip_at_end++;
|
skip_at_end++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (blk2[len2-1] == '\n') {
|
if (len2 && blk2[len2-1] == '\n') {
|
||||||
len2--;
|
len2--;
|
||||||
skip_at_end++;
|
skip_at_end++;
|
||||||
if (blk2[len2-1] == '\r') {
|
if (len2 && blk2[len2-1] == '\r') {
|
||||||
len2--;
|
len2--;
|
||||||
skip_at_end++;
|
skip_at_end++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,7 @@ out:
|
||||||
* This function tries to destroy the httpclient if it wasn't running.
|
* This function tries to destroy the httpclient if it wasn't running.
|
||||||
* If it was running, stop the client and ask it to autodestroy itself.
|
* If it was running, stop the client and ask it to autodestroy itself.
|
||||||
*
|
*
|
||||||
* Once this function is used, all pointer sto the client must be removed
|
* Once this function is used, all pointers to the client must be removed
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void httpclient_stop_and_destroy(struct httpclient *hc)
|
void httpclient_stop_and_destroy(struct httpclient *hc)
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ void hc_cli_res_end_cb(struct httpclient *hc)
|
||||||
static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
|
static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
{
|
{
|
||||||
struct hcli_svc_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct hcli_svc_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
struct httpclient *hc;
|
struct httpclient *hc = NULL;
|
||||||
char *err = NULL;
|
char *err = NULL;
|
||||||
enum http_meth_t meth;
|
enum http_meth_t meth;
|
||||||
char *meth_str;
|
char *meth_str;
|
||||||
|
|
@ -159,6 +159,7 @@ static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void
|
||||||
|
|
||||||
err:
|
err:
|
||||||
memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
|
memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
|
||||||
|
httpclient_destroy(hc);
|
||||||
return cli_err(appctx, err);
|
return cli_err(appctx, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
22
src/htx.c
22
src/htx.c
|
|
@ -681,7 +681,7 @@ struct htx_blk *htx_replace_blk_value(struct htx *htx, struct htx_blk *blk,
|
||||||
}
|
}
|
||||||
else { /* Do a defrag first (it is always an expansion) */
|
else { /* Do a defrag first (it is always an expansion) */
|
||||||
struct htx_blk tmpblk;
|
struct htx_blk tmpblk;
|
||||||
struct buffer *chunk = alloc_trash_chunk();
|
struct buffer *chunk = alloc_trash_chunk_sz(n.len + v.len + delta);
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
||||||
if (!chunk)
|
if (!chunk)
|
||||||
|
|
@ -730,17 +730,20 @@ struct htx_blk *htx_replace_blk_value(struct htx *htx, struct htx_blk *blk,
|
||||||
* - HTX_XFER_KEEP_SRC_BLKS: source blocks are not removed
|
* - HTX_XFER_KEEP_SRC_BLKS: source blocks are not removed
|
||||||
* - HTX_XFER_PARTIAL_HDRS_COPY: partial headers and trailers part can be xferred
|
* - HTX_XFER_PARTIAL_HDRS_COPY: partial headers and trailers part can be xferred
|
||||||
* - HTX_XFER_HDRS_ONLY: Only the headers part is xferred
|
* - HTX_XFER_HDRS_ONLY: Only the headers part is xferred
|
||||||
|
* - HTX_XFER_NO_METADATA: <count> don't include meta-data, only payload
|
||||||
*/
|
*/
|
||||||
size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int flags)
|
size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct htx_blk *blk, *last_dstblk;
|
struct htx_blk *blk, *last_dstblk;
|
||||||
size_t ret = 0;
|
size_t ret = 0;
|
||||||
|
size_t meta_sz = (flags & HTX_XFER_NO_METADATA) ? 0 : sizeof(*blk);
|
||||||
uint32_t max, last_dstblk_sz;
|
uint32_t max, last_dstblk_sz;
|
||||||
int dst_full = 0;
|
int dst_full = 0;
|
||||||
|
|
||||||
|
|
||||||
last_dstblk = NULL;
|
last_dstblk = NULL;
|
||||||
last_dstblk_sz = 0;
|
last_dstblk_sz = 0;
|
||||||
for (blk = htx_get_head_blk(src); blk && count; blk = htx_get_next_blk(src, blk)) {
|
for (blk = htx_get_head_blk(src); blk && count > meta_sz; blk = htx_get_next_blk(src, blk)) {
|
||||||
struct ist v;
|
struct ist v;
|
||||||
enum htx_blk_type type;
|
enum htx_blk_type type;
|
||||||
uint32_t sz;
|
uint32_t sz;
|
||||||
|
|
@ -755,7 +758,10 @@ size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int fla
|
||||||
type != HTX_BLK_HDR && type != HTX_BLK_EOH)
|
type != HTX_BLK_HDR && type != HTX_BLK_EOH)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
max = htx_get_max_blksz(dst, count);
|
max = htx_free_data_space(dst);
|
||||||
|
if (max > count - meta_sz)
|
||||||
|
max = count - meta_sz;
|
||||||
|
|
||||||
if (!max)
|
if (!max)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -771,8 +777,8 @@ size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int fla
|
||||||
}
|
}
|
||||||
last_dstblk = htx_get_tail_blk(dst);
|
last_dstblk = htx_get_tail_blk(dst);
|
||||||
last_dstblk_sz = v.len;
|
last_dstblk_sz = v.len;
|
||||||
count -= sizeof(*blk) + v.len;
|
count -= meta_sz + v.len;
|
||||||
ret += sizeof(*blk) + v.len;
|
ret += meta_sz + v.len;
|
||||||
if (v.len != sz) {
|
if (v.len != sz) {
|
||||||
dst_full = 1;
|
dst_full = 1;
|
||||||
goto stop;
|
goto stop;
|
||||||
|
|
@ -793,8 +799,8 @@ size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int fla
|
||||||
last_dstblk->info = blk->info;
|
last_dstblk->info = blk->info;
|
||||||
htx_memcpy(htx_get_blk_ptr(dst, last_dstblk), htx_get_blk_ptr(src, blk), sz);
|
htx_memcpy(htx_get_blk_ptr(dst, last_dstblk), htx_get_blk_ptr(src, blk), sz);
|
||||||
last_dstblk_sz = sz;
|
last_dstblk_sz = sz;
|
||||||
count -= sizeof(*blk) + sz;
|
count -= meta_sz + sz;
|
||||||
ret += sizeof(*blk) + sz;
|
ret += meta_sz + sz;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -826,7 +832,7 @@ size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int fla
|
||||||
/* Remove partial headers/trailers from <dst> and rollback on <src> to not remove them later */
|
/* Remove partial headers/trailers from <dst> and rollback on <src> to not remove them later */
|
||||||
while (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_HDR || type == HTX_BLK_TLR) {
|
while (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_HDR || type == HTX_BLK_TLR) {
|
||||||
BUG_ON(type != htx_get_blk_type(blk));
|
BUG_ON(type != htx_get_blk_type(blk));
|
||||||
ret -= sizeof(*blk) + htx_get_blksz(blk);
|
ret -= meta_sz + htx_get_blksz(blk);
|
||||||
htx_remove_blk(dst, dstblk);
|
htx_remove_blk(dst, dstblk);
|
||||||
dstblk = htx_get_tail_blk(dst);
|
dstblk = htx_get_tail_blk(dst);
|
||||||
blk = htx_get_prev_blk(src, blk);
|
blk = htx_get_prev_blk(src, blk);
|
||||||
|
|
|
||||||
|
|
@ -448,7 +448,7 @@ static int build_and_check_tag(jwe_enc enc, struct jwt_item items[JWE_ELT_MAX],
|
||||||
int retval = 1;
|
int retval = 1;
|
||||||
const EVP_MD *hash = NULL;
|
const EVP_MD *hash = NULL;
|
||||||
int mac_key_len = 0;
|
int mac_key_len = 0;
|
||||||
uint64_t aad_len = my_htonll(items[JWE_ELT_JOSE].length << 3);
|
uint64_t aad_len = my_htonll((uint64_t)items[JWE_ELT_JOSE].length << 3);
|
||||||
|
|
||||||
struct buffer *tag_data = alloc_trash_chunk();
|
struct buffer *tag_data = alloc_trash_chunk();
|
||||||
struct buffer *hmac = alloc_trash_chunk();
|
struct buffer *hmac = alloc_trash_chunk();
|
||||||
|
|
|
||||||
12
src/jws.c
12
src/jws.c
|
|
@ -54,13 +54,15 @@ static size_t EVP_PKEY_EC_to_pub_jwk(EVP_PKEY *pkey, char *dst, size_t dsize)
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
const char *crv = NULL;
|
const char *crv = NULL;
|
||||||
|
|
||||||
#if HA_OPENSSL_VERSION_NUMBER > 0x30000000L
|
#if HA_OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||||
char curve[32] = {};
|
char curve[32] = {};
|
||||||
size_t curvelen;
|
size_t curvelen;
|
||||||
int nid;
|
int nid;
|
||||||
|
|
||||||
EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &x);
|
if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &x) == 0)
|
||||||
EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &y);
|
goto out;
|
||||||
|
if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &y) == 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curve, sizeof(curve), &curvelen) == 0)
|
if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curve, sizeof(curve), &curvelen) == 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -144,7 +146,7 @@ static size_t EVP_PKEY_RSA_to_pub_jwk(EVP_PKEY *pkey, char *dst, size_t dsize)
|
||||||
struct buffer *str_n = NULL, *str_e = NULL;
|
struct buffer *str_n = NULL, *str_e = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
#if HA_OPENSSL_VERSION_NUMBER > 0x30000000L
|
#if HA_OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||||
|
|
||||||
if ((EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &n)) == 0)
|
if ((EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &n)) == 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -292,7 +294,7 @@ enum jwt_alg EVP_PKEY_to_jws_alg(EVP_PKEY *pkey)
|
||||||
enum jwt_alg alg = JWS_ALG_NONE;
|
enum jwt_alg alg = JWS_ALG_NONE;
|
||||||
|
|
||||||
if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) {
|
if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) {
|
||||||
#if HA_OPENSSL_VERSION_NUMBER > 0x30000000L
|
#if HA_OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||||
char curve[32] = {};
|
char curve[32] = {};
|
||||||
size_t curvelen;
|
size_t curvelen;
|
||||||
int nid;
|
int nid;
|
||||||
|
|
|
||||||
|
|
@ -324,6 +324,8 @@ static int convert_ecdsa_sig(const struct jwt_ctx *ctx, struct buffer *signature
|
||||||
ec_S = BN_bin2bn((unsigned char *)(b_orig(signature) + bignum_len), bignum_len, NULL);
|
ec_S = BN_bin2bn((unsigned char *)(b_orig(signature) + bignum_len), bignum_len, NULL);
|
||||||
|
|
||||||
if (!ec_R || !ec_S) {
|
if (!ec_R || !ec_S) {
|
||||||
|
BN_free(ec_R);
|
||||||
|
BN_free(ec_S);
|
||||||
retval = JWT_VRFY_INVALID_TOKEN;
|
retval = JWT_VRFY_INVALID_TOKEN;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#include <haproxy/api.h>
|
#include <haproxy/api.h>
|
||||||
#include <haproxy/backend.h>
|
#include <haproxy/backend.h>
|
||||||
#include <haproxy/errors.h>
|
#include <haproxy/errors.h>
|
||||||
|
#include <haproxy/guid.h>
|
||||||
#include <haproxy/queue.h>
|
#include <haproxy/queue.h>
|
||||||
#include <haproxy/server.h>
|
#include <haproxy/server.h>
|
||||||
#include <haproxy/tools.h>
|
#include <haproxy/tools.h>
|
||||||
|
|
@ -82,6 +83,7 @@ static inline u32 chash_compute_server_key(struct server *s)
|
||||||
{
|
{
|
||||||
enum srv_hash_key hash_key = s->hash_key;
|
enum srv_hash_key hash_key = s->hash_key;
|
||||||
struct server_inetaddr srv_addr;
|
struct server_inetaddr srv_addr;
|
||||||
|
const char *guid_key = NULL;
|
||||||
u32 key;
|
u32 key;
|
||||||
|
|
||||||
/* If hash-key is addr or addr-port then we need the address, but if we
|
/* If hash-key is addr or addr-port then we need the address, but if we
|
||||||
|
|
@ -96,6 +98,11 @@ static inline u32 chash_compute_server_key(struct server *s)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SRV_HASH_KEY_GUID:
|
||||||
|
guid_key = guid_get(&s->guid);
|
||||||
|
if (!guid_key)
|
||||||
|
hash_key = SRV_HASH_KEY_ID;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -121,6 +128,14 @@ static inline u32 chash_compute_server_key(struct server *s)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SRV_HASH_KEY_GUID:
|
||||||
|
key = XXH32(guid_key, strlen(guid_key), 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SRV_HASH_KEY_ID32:
|
||||||
|
key = full_hash(htonl(s->puid));
|
||||||
|
break;
|
||||||
|
|
||||||
case SRV_HASH_KEY_ID:
|
case SRV_HASH_KEY_ID:
|
||||||
default:
|
default:
|
||||||
key = s->puid * SRV_EWGHT_RANGE;
|
key = s->puid * SRV_EWGHT_RANGE;
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ static int compute_ideal_maxconn()
|
||||||
{
|
{
|
||||||
int ssl_sides = !!global.ssl_used_frontend + !!global.ssl_used_backend;
|
int ssl_sides = !!global.ssl_used_frontend + !!global.ssl_used_backend;
|
||||||
int engine_fds = global.ssl_used_async_engines * ssl_sides;
|
int engine_fds = global.ssl_used_async_engines * ssl_sides;
|
||||||
int pipes = compute_ideal_maxpipes();
|
int pipes = global.maxpipes ? global.maxpipes : compute_ideal_maxpipes();
|
||||||
int remain = MAX(rlim_fd_cur_at_boot, rlim_fd_max_at_boot);
|
int remain = MAX(rlim_fd_cur_at_boot, rlim_fd_max_at_boot);
|
||||||
int maxconn;
|
int maxconn;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1608,7 +1608,7 @@ struct logger *dup_logger(struct logger *def)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (def->lb.smp_rgs) {
|
if (def->lb.smp_rgs) {
|
||||||
cpy->lb.smp_rgs = malloc(sizeof(*cpy->lb.smp_rgs) * def->lb.smp_rgs_sz);
|
cpy->lb.smp_rgs = malloc(array_size_or_fail(sizeof(*cpy->lb.smp_rgs), def->lb.smp_rgs_sz));
|
||||||
if (!cpy->lb.smp_rgs)
|
if (!cpy->lb.smp_rgs)
|
||||||
goto error;
|
goto error;
|
||||||
memcpy(cpy->lb.smp_rgs, def->lb.smp_rgs,
|
memcpy(cpy->lb.smp_rgs, def->lb.smp_rgs,
|
||||||
|
|
@ -3319,7 +3319,7 @@ struct ist *build_log_header(struct log_header hdr, size_t *nbelem)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
|
else if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
|
||||||
int month;
|
uint month;
|
||||||
char *timestamp = metadata[LOG_META_TIME].ptr;
|
char *timestamp = metadata[LOG_META_TIME].ptr;
|
||||||
|
|
||||||
/* iso time always begins like this: '1970-01-01T00:00:00' */
|
/* iso time always begins like this: '1970-01-01T00:00:00' */
|
||||||
|
|
@ -5499,7 +5499,7 @@ void parse_log_message(char *buf, size_t buflen, int *level, int *facility,
|
||||||
return;
|
return;
|
||||||
fac_level = 10*fac_level + (*p - '0');
|
fac_level = 10*fac_level + (*p - '0');
|
||||||
p++;
|
p++;
|
||||||
if ((p - buf) > buflen)
|
if ((p - buf) >= buflen)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6743,6 +6743,7 @@ int cfg_parse_log_profile(const char *file, int linenum, char **args, int kwm)
|
||||||
SMP_VAL_FE_LOG_END, &errmsg)) {
|
SMP_VAL_FE_LOG_END, &errmsg)) {
|
||||||
ha_alert("Parsing [%s:%d]: failed to parse logformat: %s.\n",
|
ha_alert("Parsing [%s:%d]: failed to parse logformat: %s.\n",
|
||||||
file, linenum, errmsg);
|
file, linenum, errmsg);
|
||||||
|
lf_expr_deinit(target_lf);
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -981,7 +981,7 @@ static inline void fcgi_strm_propagate_term_flags(struct fcgi_conn *fconn, struc
|
||||||
*/
|
*/
|
||||||
static void fcgi_strm_destroy(struct fcgi_strm *fstrm)
|
static void fcgi_strm_destroy(struct fcgi_strm *fstrm)
|
||||||
{
|
{
|
||||||
struct connection *conn = fstrm->fconn->conn;
|
struct connection __maybe_unused *conn = fstrm->fconn->conn;
|
||||||
|
|
||||||
TRACE_ENTER(FCGI_EV_FSTRM_END, conn, fstrm);
|
TRACE_ENTER(FCGI_EV_FSTRM_END, conn, fstrm);
|
||||||
|
|
||||||
|
|
@ -3540,7 +3540,7 @@ static size_t fcgi_strm_parse_trailers(struct fcgi_strm *fstrm, struct h1m *h1m,
|
||||||
|
|
||||||
static size_t fcgi_strm_parse_response(struct fcgi_strm *fstrm, struct buffer *buf, size_t count)
|
static size_t fcgi_strm_parse_response(struct fcgi_strm *fstrm, struct buffer *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct fcgi_conn *fconn = fstrm->fconn;
|
struct fcgi_conn __maybe_unused *fconn = fstrm->fconn;
|
||||||
struct htx *htx;
|
struct htx *htx;
|
||||||
struct h1m *h1m = &fstrm->h1m;
|
struct h1m *h1m = &fstrm->h1m;
|
||||||
size_t ret, data, total = 0;
|
size_t ret, data, total = 0;
|
||||||
|
|
@ -4031,7 +4031,7 @@ static int fcgi_subscribe(struct stconn *sc, int event_type, struct wait_event *
|
||||||
static int fcgi_unsubscribe(struct stconn *sc, int event_type, struct wait_event *es)
|
static int fcgi_unsubscribe(struct stconn *sc, int event_type, struct wait_event *es)
|
||||||
{
|
{
|
||||||
struct fcgi_strm *fstrm = __sc_mux_strm(sc);
|
struct fcgi_strm *fstrm = __sc_mux_strm(sc);
|
||||||
struct fcgi_conn *fconn = fstrm->fconn;
|
struct fcgi_conn __maybe_unused *fconn = fstrm->fconn;
|
||||||
|
|
||||||
BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV));
|
BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV));
|
||||||
BUG_ON(fstrm->subs && fstrm->subs != es);
|
BUG_ON(fstrm->subs && fstrm->subs != es);
|
||||||
|
|
@ -4551,7 +4551,7 @@ static const struct mux_ops mux_fcgi_ops = {
|
||||||
|
|
||||||
/* this mux registers FCGI proto */
|
/* this mux registers FCGI proto */
|
||||||
static struct mux_proto_list mux_proto_fcgi =
|
static struct mux_proto_list mux_proto_fcgi =
|
||||||
{ .token = IST("fcgi"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BE, .mux = &mux_fcgi_ops };
|
{ .mux_proto = IST("fcgi"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BE, .mux = &mux_fcgi_ops };
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_fcgi);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_fcgi);
|
||||||
|
|
||||||
|
|
|
||||||
10
src/mux_h1.c
10
src/mux_h1.c
|
|
@ -1897,7 +1897,7 @@ static void h1_append_chunk_crlf(struct buffer *buf)
|
||||||
*/
|
*/
|
||||||
static void h1_set_tunnel_mode(struct h1s *h1s)
|
static void h1_set_tunnel_mode(struct h1s *h1s)
|
||||||
{
|
{
|
||||||
struct h1c *h1c = h1s->h1c;
|
struct h1c __maybe_unused *h1c = h1s->h1c;
|
||||||
|
|
||||||
h1s->req.state = H1_MSG_TUNNEL;
|
h1s->req.state = H1_MSG_TUNNEL;
|
||||||
h1s->req.flags &= ~(H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK);
|
h1s->req.flags &= ~(H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK);
|
||||||
|
|
@ -2713,7 +2713,9 @@ static size_t h1_make_headers(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
|
||||||
goto nextblk;
|
goto nextblk;
|
||||||
}
|
}
|
||||||
else if (isteq(n, ist("upgrade"))) {
|
else if (isteq(n, ist("upgrade"))) {
|
||||||
h1_parse_upgrade_header(h1m, v);
|
h1_parse_upgrade_header(h1m, &v);
|
||||||
|
if (!v.len)
|
||||||
|
goto nextblk;
|
||||||
}
|
}
|
||||||
else if ((isteq(n, ist("sec-websocket-accept")) && h1m->flags & H1_MF_RESP) ||
|
else if ((isteq(n, ist("sec-websocket-accept")) && h1m->flags & H1_MF_RESP) ||
|
||||||
(isteq(n, ist("sec-websocket-key")) && !(h1m->flags & H1_MF_RESP))) {
|
(isteq(n, ist("sec-websocket-key")) && !(h1m->flags & H1_MF_RESP))) {
|
||||||
|
|
@ -6123,9 +6125,9 @@ static const struct mux_ops mux_h1_ops = {
|
||||||
|
|
||||||
/* this mux registers default HTX proto but also h1 proto (to be referenced in the conf */
|
/* this mux registers default HTX proto but also h1 proto (to be referenced in the conf */
|
||||||
static struct mux_proto_list mux_proto_h1 =
|
static struct mux_proto_list mux_proto_h1 =
|
||||||
{ .token = IST("h1"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &mux_h1_ops };
|
{ .mux_proto = IST("h1"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &mux_h1_ops };
|
||||||
static struct mux_proto_list mux_proto_http =
|
static struct mux_proto_list mux_proto_http =
|
||||||
{ .token = IST(""), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &mux_http_ops, .alpn = "\010http/1.1" };
|
{ .mux_proto = IST(""), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &mux_http_ops, .alpn = "\010http/1.1" };
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_h1);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_h1);
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_http);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_http);
|
||||||
|
|
|
||||||
35
src/mux_h2.c
35
src/mux_h2.c
|
|
@ -1281,7 +1281,7 @@ void _h2_trace_header(const struct ist hn, const struct ist hv,
|
||||||
const struct h2c *h2c, const struct h2s *h2s)
|
const struct h2c *h2c, const struct h2s *h2s)
|
||||||
{
|
{
|
||||||
struct ist n_ist, v_ist;
|
struct ist n_ist, v_ist;
|
||||||
const char *c_str, *s_str;
|
const char __maybe_unused *c_str, *s_str;
|
||||||
|
|
||||||
chunk_reset(&trash);
|
chunk_reset(&trash);
|
||||||
c_str = chunk_newstr(&trash);
|
c_str = chunk_newstr(&trash);
|
||||||
|
|
@ -1456,7 +1456,7 @@ static int h2_init(struct connection *conn, struct proxy *prx, struct session *s
|
||||||
|
|
||||||
if (max_strm && h2c->streams_hard_limit > max_strm)
|
if (max_strm && h2c->streams_hard_limit > max_strm)
|
||||||
h2c->streams_limit = h2c->streams_hard_limit = max_strm;
|
h2c->streams_limit = h2c->streams_hard_limit = max_strm;
|
||||||
if (h2c->streams_hard_limit > 1)
|
if (global.tune.streams_elasticity && h2c->streams_hard_limit > 1)
|
||||||
_HA_ATOMIC_ADD(&tg_ctx->committed_extra_streams, h2c->streams_hard_limit - 1);
|
_HA_ATOMIC_ADD(&tg_ctx->committed_extra_streams, h2c->streams_hard_limit - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1527,7 +1527,10 @@ static int h2_init(struct connection *conn, struct proxy *prx, struct session *s
|
||||||
TRACE_LEAVE(H2_EV_H2C_NEW, conn);
|
TRACE_LEAVE(H2_EV_H2C_NEW, conn);
|
||||||
return 0;
|
return 0;
|
||||||
fail_stream:
|
fail_stream:
|
||||||
if (!(h2c->flags & H2_CF_IS_BACK) && h2c->streams_hard_limit > 1)
|
/* Unnecessary code for now as fail_stream only occurs with BE conns.
|
||||||
|
* Still better though to keep it to prevent future mistakes.
|
||||||
|
*/
|
||||||
|
if (!(h2c->flags & H2_CF_IS_BACK) && global.tune.streams_elasticity && h2c->streams_hard_limit > 1)
|
||||||
_HA_ATOMIC_SUB(&tg_ctx->committed_extra_streams, h2c->streams_hard_limit - 1);
|
_HA_ATOMIC_SUB(&tg_ctx->committed_extra_streams, h2c->streams_hard_limit - 1);
|
||||||
hpack_dht_free(h2c->ddht);
|
hpack_dht_free(h2c->ddht);
|
||||||
fail:
|
fail:
|
||||||
|
|
@ -1607,7 +1610,7 @@ static void h2_release(struct h2c *h2c)
|
||||||
if (!conn || !conn_is_reverse(conn))
|
if (!conn || !conn_is_reverse(conn))
|
||||||
HA_ATOMIC_DEC(&h2c->px_counters->open_conns);
|
HA_ATOMIC_DEC(&h2c->px_counters->open_conns);
|
||||||
|
|
||||||
if (!(h2c->flags & H2_CF_IS_BACK) && h2c->streams_hard_limit > 1)
|
if (!(h2c->flags & H2_CF_IS_BACK) && global.tune.streams_elasticity && h2c->streams_hard_limit > 1)
|
||||||
_HA_ATOMIC_SUB(&tg_ctx->committed_extra_streams, h2c->streams_hard_limit - 1);
|
_HA_ATOMIC_SUB(&tg_ctx->committed_extra_streams, h2c->streams_hard_limit - 1);
|
||||||
|
|
||||||
pool_free(pool_head_h2_rx_bufs, h2c->shared_rx_bufs);
|
pool_free(pool_head_h2_rx_bufs, h2c->shared_rx_bufs);
|
||||||
|
|
@ -2021,7 +2024,7 @@ static inline void h2s_propagate_term_flags(struct h2c *h2c, struct h2s *h2s)
|
||||||
*/
|
*/
|
||||||
static void h2s_destroy(struct h2s *h2s)
|
static void h2s_destroy(struct h2s *h2s)
|
||||||
{
|
{
|
||||||
struct connection *conn = h2s->h2c->conn;
|
struct connection __maybe_unused *conn = h2s->h2c->conn;
|
||||||
int freed = 0;
|
int freed = 0;
|
||||||
|
|
||||||
TRACE_ENTER(H2_EV_H2S_END, conn, h2s);
|
TRACE_ENTER(H2_EV_H2S_END, conn, h2s);
|
||||||
|
|
@ -4197,7 +4200,7 @@ static int h2_conn_reverse(struct h2c *h2c)
|
||||||
/* the connection was accounted as frontend streams before
|
/* the connection was accounted as frontend streams before
|
||||||
* reversal, we must undo that accounting now.
|
* reversal, we must undo that accounting now.
|
||||||
*/
|
*/
|
||||||
if (h2c->streams_hard_limit > 1)
|
if (h2c->streams_hard_limit > 1 && global.tune.streams_elasticity)
|
||||||
_HA_ATOMIC_SUB(&tg_ctx->committed_extra_streams, h2c->streams_hard_limit - 1);
|
_HA_ATOMIC_SUB(&tg_ctx->committed_extra_streams, h2c->streams_hard_limit - 1);
|
||||||
|
|
||||||
h2c->flags |= H2_CF_IS_BACK;
|
h2c->flags |= H2_CF_IS_BACK;
|
||||||
|
|
@ -4219,7 +4222,7 @@ static int h2_conn_reverse(struct h2c *h2c)
|
||||||
struct proxy *prx = l->bind_conf->frontend;
|
struct proxy *prx = l->bind_conf->frontend;
|
||||||
|
|
||||||
/* backend connections becoming frontend need accounting. */
|
/* backend connections becoming frontend need accounting. */
|
||||||
if (h2c->streams_hard_limit > 1)
|
if (h2c->streams_hard_limit > 1 && global.tune.streams_elasticity)
|
||||||
_HA_ATOMIC_ADD(&tg_ctx->committed_extra_streams, h2c->streams_hard_limit - 1);
|
_HA_ATOMIC_ADD(&tg_ctx->committed_extra_streams, h2c->streams_hard_limit - 1);
|
||||||
|
|
||||||
h2c->flags &= ~H2_CF_IS_BACK;
|
h2c->flags &= ~H2_CF_IS_BACK;
|
||||||
|
|
@ -6233,6 +6236,13 @@ next_frame:
|
||||||
|
|
||||||
/* Skip StreamDep and weight for now (we don't support PRIORITY) */
|
/* Skip StreamDep and weight for now (we don't support PRIORITY) */
|
||||||
if (h2c->dff & H2_F_HEADERS_PRIORITY) {
|
if (h2c->dff & H2_F_HEADERS_PRIORITY) {
|
||||||
|
if (flen < 5) {
|
||||||
|
h2c_report_glitch(h2c, 1, "too short PRIORITY frame");
|
||||||
|
TRACE_STATE("too short PRIORITY frame", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
|
||||||
|
h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (read_n32(hdrs) == h2c->dsi) {
|
if (read_n32(hdrs) == h2c->dsi) {
|
||||||
/* RFC7540#5.3.1 : stream dep may not depend on itself */
|
/* RFC7540#5.3.1 : stream dep may not depend on itself */
|
||||||
h2c_report_glitch(h2c, 1, "PRIORITY frame referencing itself");
|
h2c_report_glitch(h2c, 1, "PRIORITY frame referencing itself");
|
||||||
|
|
@ -6242,13 +6252,6 @@ next_frame:
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flen < 5) {
|
|
||||||
h2c_report_glitch(h2c, 1, "too short PRIORITY frame");
|
|
||||||
TRACE_STATE("too short PRIORITY frame", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
|
|
||||||
h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
hdrs += 5; // stream dep = 4, weight = 1
|
hdrs += 5; // stream dep = 4, weight = 1
|
||||||
flen -= 5;
|
flen -= 5;
|
||||||
}
|
}
|
||||||
|
|
@ -7610,7 +7613,7 @@ static size_t h2s_make_data(struct h2s *h2s, struct buffer *buf, size_t count)
|
||||||
*/
|
*/
|
||||||
static size_t h2s_skip_data(struct h2s *h2s, struct buffer *buf, size_t count)
|
static size_t h2s_skip_data(struct h2s *h2s, struct buffer *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct h2c *h2c = h2s->h2c;
|
struct h2c __maybe_unused *h2c = h2s->h2c;
|
||||||
struct htx *htx;
|
struct htx *htx;
|
||||||
int bsize; /* htx block size */
|
int bsize; /* htx block size */
|
||||||
int fsize; /* h2 frame size */
|
int fsize; /* h2 frame size */
|
||||||
|
|
@ -9010,7 +9013,7 @@ static const struct mux_ops h2_ops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct mux_proto_list mux_proto_h2 =
|
static struct mux_proto_list mux_proto_h2 =
|
||||||
{ .token = IST("h2"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &h2_ops, .alpn = "\002h2" };
|
{ .mux_proto = IST("h2"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &h2_ops, .alpn = "\002h2" };
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_h2);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_h2);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -936,9 +936,9 @@ const struct mux_ops mux_pt_ops = {
|
||||||
|
|
||||||
/* PROT selection : default mux has empty name */
|
/* PROT selection : default mux has empty name */
|
||||||
static struct mux_proto_list mux_proto_none =
|
static struct mux_proto_list mux_proto_none =
|
||||||
{ .token = IST("none"), .mode = PROTO_MODE_TCP, .side = PROTO_SIDE_BOTH, .mux = &mux_pt_ops };
|
{ .mux_proto = IST("none"), .mode = PROTO_MODE_TCP, .side = PROTO_SIDE_BOTH, .mux = &mux_pt_ops };
|
||||||
static struct mux_proto_list mux_proto_tcp =
|
static struct mux_proto_list mux_proto_tcp =
|
||||||
{ .token = IST(""), .mode = PROTO_MODE_TCP, .side = PROTO_SIDE_BOTH, .mux = &mux_tcp_ops };
|
{ .mux_proto = IST(""), .mode = PROTO_MODE_TCP, .side = PROTO_SIDE_BOTH, .mux = &mux_tcp_ops };
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_none);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_none);
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_tcp);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_tcp);
|
||||||
|
|
|
||||||
|
|
@ -2467,6 +2467,7 @@ int qcc_recv_stop_sending(struct qcc *qcc, uint64_t id, uint64_t err)
|
||||||
static int qcc_release_remote_stream(struct qcc *qcc, uint64_t id)
|
static int qcc_release_remote_stream(struct qcc *qcc, uint64_t id)
|
||||||
{
|
{
|
||||||
struct quic_frame *frm;
|
struct quic_frame *frm;
|
||||||
|
uint64_t conn_max, rem, non_extra, inc;
|
||||||
|
|
||||||
TRACE_ENTER(QMUX_EV_QCS_END, qcc->conn);
|
TRACE_ENTER(QMUX_EV_QCS_END, qcc->conn);
|
||||||
|
|
||||||
|
|
@ -2483,8 +2484,42 @@ static int qcc_release_remote_stream(struct qcc *qcc, uint64_t id)
|
||||||
/* MAX_STREAMS needed if closed streams value more than twice
|
/* MAX_STREAMS needed if closed streams value more than twice
|
||||||
* the initial window or reaching the stream ID limit.
|
* the initial window or reaching the stream ID limit.
|
||||||
*/
|
*/
|
||||||
if (qcc->lfctl.cl_bidi_r > qcc->lfctl.ms_bidi_init / 2 ||
|
if (qcc->lfctl.cl_bidi_r > qcc->lfctl.ms_bidi_rel / 2 ||
|
||||||
qcc->lfctl.cl_bidi_r + qcc->lfctl.ms_bidi == max) {
|
qcc->lfctl.cl_bidi_r + qcc->lfctl.ms_bidi == max) {
|
||||||
|
|
||||||
|
BUG_ON(qcc->lfctl.ms_bidi_rel < qcc->lfctl.cl_bidi_r);
|
||||||
|
rem = qcc->lfctl.ms_bidi_rel - qcc->lfctl.cl_bidi_r;
|
||||||
|
/* if every streams are closed, decrement extra stream accounting by 1 */
|
||||||
|
non_extra = !rem ? 1 : 0;
|
||||||
|
|
||||||
|
if (!(qcc->flags & QC_CF_IS_BACK) && global.tune.streams_elasticity) {
|
||||||
|
/* If stream elasticity is active, first decrement closed from extra streams. */
|
||||||
|
if (qcc->lfctl.ms_bidi_rel > 1) {
|
||||||
|
_HA_ATOMIC_SUB(&tg_ctx->committed_extra_streams,
|
||||||
|
qcc->lfctl.cl_bidi_r - non_extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now calculate the available streams. */
|
||||||
|
conn_max = conn_calc_max_streams(qcc->lfctl.ms_bidi_init);
|
||||||
|
if (conn_max <= rem) {
|
||||||
|
/* More streams already consumed than currently allowed,
|
||||||
|
* keep the current flow control limit.
|
||||||
|
*/
|
||||||
|
qcc->lfctl.ms_bidi_rel = rem;
|
||||||
|
qcc->lfctl.cl_bidi_r = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update flow control limit up to the allowed elasticity limit. */
|
||||||
|
inc = conn_max - rem;
|
||||||
|
_HA_ATOMIC_ADD(&tg_ctx->committed_extra_streams, inc - non_extra);
|
||||||
|
qcc->lfctl.ms_bidi_rel = rem + inc;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Stream elasticity not active, flow control increase remains static. */
|
||||||
|
inc = qcc->lfctl.cl_bidi_r;
|
||||||
|
}
|
||||||
|
|
||||||
TRACE_DATA("increase max stream limit with MAX_STREAMS_BIDI", QMUX_EV_QCC_SEND, qcc->conn);
|
TRACE_DATA("increase max stream limit with MAX_STREAMS_BIDI", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
frm = qc_frm_alloc(QUIC_FT_MAX_STREAMS_BIDI);
|
frm = qc_frm_alloc(QUIC_FT_MAX_STREAMS_BIDI);
|
||||||
if (!frm) {
|
if (!frm) {
|
||||||
|
|
@ -2493,12 +2528,11 @@ static int qcc_release_remote_stream(struct qcc *qcc, uint64_t id)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
frm->max_streams_bidi.max_streams = qcc->lfctl.ms_bidi +
|
frm->max_streams_bidi.max_streams = qcc->lfctl.ms_bidi + inc;
|
||||||
qcc->lfctl.cl_bidi_r;
|
|
||||||
LIST_APPEND(&qcc->lfctl.frms, &frm->list);
|
LIST_APPEND(&qcc->lfctl.frms, &frm->list);
|
||||||
tasklet_wakeup(qcc->wait_event.tasklet);
|
tasklet_wakeup(qcc->wait_event.tasklet);
|
||||||
|
|
||||||
qcc->lfctl.ms_bidi += qcc->lfctl.cl_bidi_r;
|
qcc->lfctl.ms_bidi += inc;
|
||||||
qcc->lfctl.cl_bidi_r = 0;
|
qcc->lfctl.cl_bidi_r = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3307,6 +3341,10 @@ static int qcm_avail_streams(struct connection *conn)
|
||||||
|
|
||||||
ret = qcc_fctl_avail_streams(qcc, 1);
|
ret = qcc_fctl_avail_streams(qcc, 1);
|
||||||
|
|
||||||
|
/* Enforce stream_max_concurrent limit even if peer allows more streams. */
|
||||||
|
if (ret > quic_tune.be.stream_max_concurrent - qcc->nb_hreq)
|
||||||
|
ret = quic_tune.be.stream_max_concurrent - qcc->nb_hreq;
|
||||||
|
|
||||||
/* Now cap return value if reaching max-reuse server or maximum stream
|
/* Now cap return value if reaching max-reuse server or maximum stream
|
||||||
* ID. qcc_be_is_reusable() already detected if one of these has been
|
* ID. qcc_be_is_reusable() already detected if one of these has been
|
||||||
* exceeded.
|
* exceeded.
|
||||||
|
|
@ -3586,6 +3624,12 @@ static void qcc_release(struct qcc *qcc)
|
||||||
}
|
}
|
||||||
TRACE_PROTO("application layer released", QMUX_EV_QCC_END, conn);
|
TRACE_PROTO("application layer released", QMUX_EV_QCC_END, conn);
|
||||||
|
|
||||||
|
if (!(qcc->flags & QC_CF_IS_BACK) && global.tune.streams_elasticity &&
|
||||||
|
qcc->lfctl.ms_bidi_rel > 1) {
|
||||||
|
_HA_ATOMIC_SUB(&tg_ctx->committed_extra_streams,
|
||||||
|
qcc->lfctl.ms_bidi_rel - 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (conn && !conn_is_quic(conn)) {
|
if (conn && !conn_is_quic(conn)) {
|
||||||
b_free(&qcc->rx.qmux_buf);
|
b_free(&qcc->rx.qmux_buf);
|
||||||
b_free(&qcc->tx.qmux_buf);
|
b_free(&qcc->tx.qmux_buf);
|
||||||
|
|
@ -3849,7 +3893,7 @@ static int qcm_init(struct connection *conn, struct proxy *prx,
|
||||||
/* Server parameters, params used for RX flow control. */
|
/* Server parameters, params used for RX flow control. */
|
||||||
lparams = &conn->handle.qc->rx.params;
|
lparams = &conn->handle.qc->rx.params;
|
||||||
|
|
||||||
qcc->lfctl.ms_bidi = qcc->lfctl.ms_bidi_init = lparams->initial_max_streams_bidi;
|
qcc->lfctl.ms_bidi = qcc->lfctl.ms_bidi_init = qcc->lfctl.ms_bidi_rel = lparams->initial_max_streams_bidi;
|
||||||
qcc->lfctl.ms_uni = lparams->initial_max_streams_uni;
|
qcc->lfctl.ms_uni = lparams->initial_max_streams_uni;
|
||||||
qcc->lfctl.msd_bidi_l = lparams->initial_max_stream_data_bidi_local;
|
qcc->lfctl.msd_bidi_l = lparams->initial_max_stream_data_bidi_local;
|
||||||
qcc->lfctl.msd_bidi_r = lparams->initial_max_stream_data_bidi_remote;
|
qcc->lfctl.msd_bidi_r = lparams->initial_max_stream_data_bidi_remote;
|
||||||
|
|
@ -3878,7 +3922,7 @@ static int qcm_init(struct connection *conn, struct proxy *prx,
|
||||||
qcc->rfctl.msd_uni_l = rparams->initial_max_stream_data_uni;
|
qcc->rfctl.msd_uni_l = rparams->initial_max_stream_data_uni;
|
||||||
|
|
||||||
lparams = xprt_qmux_lparams(conn->xprt_ctx);
|
lparams = xprt_qmux_lparams(conn->xprt_ctx);
|
||||||
qcc->lfctl.ms_bidi = qcc->lfctl.ms_bidi_init = lparams->initial_max_streams_bidi;
|
qcc->lfctl.ms_bidi = qcc->lfctl.ms_bidi_init = qcc->lfctl.ms_bidi_rel= lparams->initial_max_streams_bidi;
|
||||||
qcc->lfctl.ms_uni = lparams->initial_max_streams_uni;
|
qcc->lfctl.ms_uni = lparams->initial_max_streams_uni;
|
||||||
qcc->lfctl.msd_bidi_l = lparams->initial_max_stream_data_bidi_local;
|
qcc->lfctl.msd_bidi_l = lparams->initial_max_stream_data_bidi_local;
|
||||||
qcc->lfctl.msd_bidi_r = lparams->initial_max_stream_data_bidi_remote;
|
qcc->lfctl.msd_bidi_r = lparams->initial_max_stream_data_bidi_remote;
|
||||||
|
|
@ -3928,6 +3972,11 @@ static int qcm_init(struct connection *conn, struct proxy *prx,
|
||||||
qcc->next_bidi_l = 0x01;
|
qcc->next_bidi_l = 0x01;
|
||||||
qcc->largest_uni_r = 0x02;
|
qcc->largest_uni_r = 0x02;
|
||||||
qcc->next_uni_l = 0x03;
|
qcc->next_uni_l = 0x03;
|
||||||
|
|
||||||
|
if (global.tune.streams_elasticity && qcc->lfctl.ms_bidi_init > 1) {
|
||||||
|
_HA_ATOMIC_ADD(&tg_ctx->committed_extra_streams,
|
||||||
|
qcc->lfctl.ms_bidi_init - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qcc->wait_event.tasklet = tasklet_new();
|
qcc->wait_event.tasklet = tasklet_new();
|
||||||
|
|
@ -4154,6 +4203,10 @@ static void qcm_strm_detach(struct sedesc *sd)
|
||||||
qcs->flags |= QC_SF_DETACH;
|
qcs->flags |= QC_SF_DETACH;
|
||||||
qcc_refresh_timeout(qcc);
|
qcc_refresh_timeout(qcc);
|
||||||
|
|
||||||
|
/* TODO on backend side if a QCS is detached, the connection may
|
||||||
|
* not be reinserted in the correct server pool (idle or avail).
|
||||||
|
*/
|
||||||
|
|
||||||
TRACE_LEAVE(QMUX_EV_STRM_END, qcc->conn, qcs);
|
TRACE_LEAVE(QMUX_EV_STRM_END, qcc->conn, qcs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -4637,7 +4690,7 @@ static int qcm_ctl(struct connection *conn, enum mux_ctl_type mux_ctl, void *out
|
||||||
return qcc->nb_hreq;
|
return qcc->nb_hreq;
|
||||||
|
|
||||||
case MUX_CTL_GET_MAXSTRM:
|
case MUX_CTL_GET_MAXSTRM:
|
||||||
return qcc->lfctl.ms_bidi_init;
|
return qcc->lfctl.ms_bidi_rel;
|
||||||
|
|
||||||
case MUX_CTL_TEVTS:
|
case MUX_CTL_TEVTS:
|
||||||
return qcc->term_evts_log;
|
return qcc->term_evts_log;
|
||||||
|
|
@ -4784,7 +4837,7 @@ void qcc_show_quic(struct qcc *qcc)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct mux_proto_list mux_proto_quic =
|
static struct mux_proto_list mux_proto_quic =
|
||||||
{ .token = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &quic_ops };
|
{ .mux_proto = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &quic_ops };
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_quic);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_quic);
|
||||||
|
|
||||||
|
|
@ -4813,6 +4866,7 @@ static const struct mux_ops qmux_ops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct mux_proto_list mux_proto_qmux =
|
static struct mux_proto_list mux_proto_qmux =
|
||||||
{ .token = IST("qmux"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &qmux_ops };
|
{ .mux_proto = IST("qmux"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &qmux_ops,
|
||||||
|
.alpn = "\002h3", .init_xprt = XPRT_QMUX };
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_qmux);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_qmux);
|
||||||
|
|
|
||||||
|
|
@ -1154,7 +1154,7 @@ static inline void spop_strm_propagate_term_flags(struct spop_conn *spop_conn, s
|
||||||
*/
|
*/
|
||||||
static void spop_strm_destroy(struct spop_strm *spop_strm)
|
static void spop_strm_destroy(struct spop_strm *spop_strm)
|
||||||
{
|
{
|
||||||
struct connection *conn = spop_strm->spop_conn->conn;
|
struct connection __maybe_unused *conn = spop_strm->spop_conn->conn;
|
||||||
|
|
||||||
TRACE_ENTER(SPOP_EV_SPOP_STRM_END, conn, spop_strm);
|
TRACE_ENTER(SPOP_EV_SPOP_STRM_END, conn, spop_strm);
|
||||||
|
|
||||||
|
|
@ -3199,7 +3199,7 @@ static int spop_subscribe(struct stconn *sc, int event_type, struct wait_event *
|
||||||
static int spop_unsubscribe(struct stconn *sc, int event_type, struct wait_event *es)
|
static int spop_unsubscribe(struct stconn *sc, int event_type, struct wait_event *es)
|
||||||
{
|
{
|
||||||
struct spop_strm *spop_strm = __sc_mux_strm(sc);
|
struct spop_strm *spop_strm = __sc_mux_strm(sc);
|
||||||
struct spop_conn *spop_conn = spop_strm->spop_conn;
|
struct spop_conn __maybe_unused *spop_conn = spop_strm->spop_conn;
|
||||||
|
|
||||||
BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV));
|
BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV));
|
||||||
BUG_ON(spop_strm->subs && spop_strm->subs != es);
|
BUG_ON(spop_strm->subs && spop_strm->subs != es);
|
||||||
|
|
@ -3738,10 +3738,10 @@ static const struct mux_ops mux_spop_ops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct mux_proto_list mux_proto_spop =
|
static struct mux_proto_list mux_proto_spop =
|
||||||
{ .token = IST("spop"), .mode = PROTO_MODE_SPOP, .side = PROTO_SIDE_BE, .mux = &mux_spop_ops };
|
{ .mux_proto = IST("spop"), .mode = PROTO_MODE_SPOP, .side = PROTO_SIDE_BE, .mux = &mux_spop_ops };
|
||||||
|
|
||||||
static struct mux_proto_list mux_proto_default_spop =
|
static struct mux_proto_list mux_proto_default_spop =
|
||||||
{ .token = IST(""), .mode = PROTO_MODE_SPOP, .side = PROTO_SIDE_BE, .mux = &mux_spop_ops };
|
{ .mux_proto = IST(""), .mode = PROTO_MODE_SPOP, .side = PROTO_SIDE_BE, .mux = &mux_spop_ops };
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_spop);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_spop);
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_default_spop);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_default_spop);
|
||||||
|
|
|
||||||
|
|
@ -447,8 +447,8 @@ static size_t tcp_fullhdr_find_opt(const struct sample *smp, uint8_t opt)
|
||||||
/* kind1 = NOP and is a single byte, others have a length field */
|
/* kind1 = NOP and is a single byte, others have a length field */
|
||||||
if (smp->data.u.str.area[next] == 1)
|
if (smp->data.u.str.area[next] == 1)
|
||||||
next++;
|
next++;
|
||||||
else if (next + 1 < len)
|
else if (next + 1 < len && smp->data.u.str.area[next + 1] > 1)
|
||||||
next += smp->data.u.str.area[next + 1];
|
next += (uchar)smp->data.u.str.area[next + 1];
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
if (smp->data.u.str.area[curr] == opt && next <= len)
|
if (smp->data.u.str.area[curr] == opt && next <= len)
|
||||||
|
|
@ -605,7 +605,7 @@ static int sample_conv_tcp_options_list(const struct arg *arg_p, struct sample *
|
||||||
/* kind1 = NOP and is a single byte, others have a length field */
|
/* kind1 = NOP and is a single byte, others have a length field */
|
||||||
if (smp->data.u.str.area[ofs] == 1)
|
if (smp->data.u.str.area[ofs] == 1)
|
||||||
ofs++;
|
ofs++;
|
||||||
else if (ofs + 1 < len && smp->data.u.str.area[ofs + 1])
|
else if (ofs + 1 < len && smp->data.u.str.area[ofs + 1] > 1)
|
||||||
ofs += (uchar)smp->data.u.str.area[ofs + 1];
|
ofs += (uchar)smp->data.u.str.area[ofs + 1];
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
@ -780,7 +780,7 @@ static int sample_conv_ip_fp(const struct arg *arg_p, struct sample *smp, void *
|
||||||
/* kind1 = NOP and is a single byte, others have a length field */
|
/* kind1 = NOP and is a single byte, others have a length field */
|
||||||
if (smp->data.u.str.area[ofs] == 1)
|
if (smp->data.u.str.area[ofs] == 1)
|
||||||
next = ofs + 1;
|
next = ofs + 1;
|
||||||
else if ((ofs + 1 < tcplen) && smp->data.u.str.area[ofs + 1]) /* optlen 0 will cause an infinite loop */
|
else if ((ofs + 1 < tcplen) && smp->data.u.str.area[ofs + 1] > 1)
|
||||||
next = ofs + (uchar)smp->data.u.str.area[ofs + 1];
|
next = ofs + (uchar)smp->data.u.str.area[ofs + 1];
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -371,7 +371,7 @@ int rhttp_bind_listener(struct listener *listener, char *errmsg, int errlen)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that server uses HTTP/2 either with proto or ALPN. */
|
/* Check that server uses HTTP/2 either with proto or ALPN. */
|
||||||
if ((!srv->mux_proto || !isteqi(srv->mux_proto->token, ist("h2"))) &&
|
if ((!srv->mux_proto || !isteqi(srv->mux_proto->mux_proto, ist("h2"))) &&
|
||||||
(!srv->use_ssl || !isteqi(ist(srv->ssl_ctx.alpn_str), ist("\x02h2")))) {
|
(!srv->use_ssl || !isteqi(ist(srv->ssl_ctx.alpn_str), ist("\x02h2")))) {
|
||||||
snprintf(errmsg, errlen, "Cannot reverse connect with server '%s/%s' unless HTTP/2 is activated on it with either proto or alpn keyword.", name, ist0(sv_name));
|
snprintf(errmsg, errlen, "Cannot reverse connect with server '%s/%s' unless HTTP/2 is activated on it with either proto or alpn keyword.", name, ist0(sv_name));
|
||||||
goto err;
|
goto err;
|
||||||
|
|
|
||||||
67
src/proxy.c
67
src/proxy.c
|
|
@ -69,7 +69,7 @@
|
||||||
#include <haproxy/uri_auth.h>
|
#include <haproxy/uri_auth.h>
|
||||||
|
|
||||||
/* Lock to ensure multiple backends deletion concurrently is safe */
|
/* Lock to ensure multiple backends deletion concurrently is safe */
|
||||||
static __decl_spinlock(proxies_del_lock);
|
__decl_spinlock(proxies_del_lock);
|
||||||
|
|
||||||
int listeners; /* # of proxy listeners, set by cfgparse */
|
int listeners; /* # of proxy listeners, set by cfgparse */
|
||||||
struct proxy *proxies_list = NULL; /* list of main proxies */
|
struct proxy *proxies_list = NULL; /* list of main proxies */
|
||||||
|
|
@ -903,7 +903,7 @@ static int proxy_parse_declare(char **args, int section, struct proxy *curpx,
|
||||||
len = strtol(args[4], &error, 10);
|
len = strtol(args[4], &error, 10);
|
||||||
if (*error != '\0') {
|
if (*error != '\0') {
|
||||||
memprintf(err, "'%s %s': cannot parse the length '%s'.",
|
memprintf(err, "'%s %s': cannot parse the length '%s'.",
|
||||||
args[0], args[1], args[3]);
|
args[0], args[1], args[4]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1006,7 +1006,7 @@ proxy_parse_retry_on(char **args, int section, struct proxy *curpx,
|
||||||
PR_RE_JUNK_REQUEST;
|
PR_RE_JUNK_REQUEST;
|
||||||
else if (strcmp(args[i], "none") == 0) {
|
else if (strcmp(args[i], "none") == 0) {
|
||||||
if (i != 1 || *args[i + 1]) {
|
if (i != 1 || *args[i + 1]) {
|
||||||
memprintf(err, "'%s' 'none' keyworld only usable alone", args[0]);
|
memprintf(err, "'%s' 'none' keyword only usable alone", args[0]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1689,13 +1689,13 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
||||||
* due to the proxy's mode not being taken into account
|
* due to the proxy's mode not being taken into account
|
||||||
* on first pass. Let's adjust it now.
|
* on first pass. Let's adjust it now.
|
||||||
*/
|
*/
|
||||||
mux_ent = conn_get_best_mux_entry(bind_conf->mux_proto->token, PROTO_SIDE_FE, is_quic, mode);
|
mux_ent = conn_get_best_mux_entry(bind_conf->mux_proto->mux_proto, IST_NULL, PROTO_SIDE_FE, is_quic, mode);
|
||||||
|
|
||||||
if (!mux_ent || !isteq(mux_ent->token, bind_conf->mux_proto->token)) {
|
if (!mux_ent || !isteq(mux_ent->mux_proto, bind_conf->mux_proto->mux_proto)) {
|
||||||
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for 'bind %s' at [%s:%d].\n",
|
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for 'bind %s' at [%s:%d].\n",
|
||||||
proxy_type_str(px), px->id,
|
proxy_type_str(px), px->id,
|
||||||
(int)bind_conf->mux_proto->token.len,
|
(int)bind_conf->mux_proto->mux_proto.len,
|
||||||
bind_conf->mux_proto->token.ptr,
|
bind_conf->mux_proto->mux_proto.ptr,
|
||||||
bind_conf->arg, bind_conf->file, bind_conf->line);
|
bind_conf->arg, bind_conf->file, bind_conf->line);
|
||||||
cfgerr++;
|
cfgerr++;
|
||||||
}
|
}
|
||||||
|
|
@ -1703,16 +1703,16 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
||||||
if ((mux_ent->mux->flags & MX_FL_FRAMED) && !(bind_conf->options & BC_O_USE_SOCK_DGRAM)) {
|
if ((mux_ent->mux->flags & MX_FL_FRAMED) && !(bind_conf->options & BC_O_USE_SOCK_DGRAM)) {
|
||||||
ha_alert("%s '%s' : frame-based MUX protocol '%.*s' is incompatible with stream transport of 'bind %s' at [%s:%d].\n",
|
ha_alert("%s '%s' : frame-based MUX protocol '%.*s' is incompatible with stream transport of 'bind %s' at [%s:%d].\n",
|
||||||
proxy_type_str(px), px->id,
|
proxy_type_str(px), px->id,
|
||||||
(int)bind_conf->mux_proto->token.len,
|
(int)bind_conf->mux_proto->mux_proto.len,
|
||||||
bind_conf->mux_proto->token.ptr,
|
bind_conf->mux_proto->mux_proto.ptr,
|
||||||
bind_conf->arg, bind_conf->file, bind_conf->line);
|
bind_conf->arg, bind_conf->file, bind_conf->line);
|
||||||
cfgerr++;
|
cfgerr++;
|
||||||
}
|
}
|
||||||
else if (!(mux_ent->mux->flags & MX_FL_FRAMED) && !(bind_conf->options & BC_O_USE_SOCK_STREAM)) {
|
else if (!(mux_ent->mux->flags & MX_FL_FRAMED) && !(bind_conf->options & BC_O_USE_SOCK_STREAM)) {
|
||||||
ha_alert("%s '%s' : stream-based MUX protocol '%.*s' is incompatible with framed transport of 'bind %s' at [%s:%d].\n",
|
ha_alert("%s '%s' : stream-based MUX protocol '%.*s' is incompatible with framed transport of 'bind %s' at [%s:%d].\n",
|
||||||
proxy_type_str(px), px->id,
|
proxy_type_str(px), px->id,
|
||||||
(int)bind_conf->mux_proto->token.len,
|
(int)bind_conf->mux_proto->mux_proto.len,
|
||||||
bind_conf->mux_proto->token.ptr,
|
bind_conf->mux_proto->mux_proto.ptr,
|
||||||
bind_conf->arg, bind_conf->file, bind_conf->line);
|
bind_conf->arg, bind_conf->file, bind_conf->line);
|
||||||
cfgerr++;
|
cfgerr++;
|
||||||
}
|
}
|
||||||
|
|
@ -1778,6 +1778,22 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
||||||
}
|
}
|
||||||
#endif /* TLSEXT_TYPE_application_layer_protocol_negotiation */
|
#endif /* TLSEXT_TYPE_application_layer_protocol_negotiation */
|
||||||
} /* HTTP && bufsize < 16384 */
|
} /* HTTP && bufsize < 16384 */
|
||||||
|
|
||||||
|
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
||||||
|
if (px->mode == PR_MODE_HTTP && !bind_conf->mux_proto &&
|
||||||
|
bind_conf->ssl_conf.alpn_str &&
|
||||||
|
strstr(bind_conf->ssl_conf.alpn_str, "\002h3")) {
|
||||||
|
if (!experimental_directives_allowed) {
|
||||||
|
ha_alert("HTTP/3 on TCP listed via ALPN on frontend '%s' at [%s:%d] relies on the experimental QMux protocol, "
|
||||||
|
"must be allowed via a global 'expose-experimental-directives'.\n",
|
||||||
|
px->id, bind_conf->file, bind_conf->line);
|
||||||
|
cfgerr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
||||||
|
}
|
||||||
|
#endif /* TLSEXT_TYPE_application_layer_protocol_negotiation */
|
||||||
|
|
||||||
#endif /* USE_OPENSSL */
|
#endif /* USE_OPENSSL */
|
||||||
|
|
||||||
#ifdef USE_QUIC
|
#ifdef USE_QUIC
|
||||||
|
|
@ -1792,6 +1808,13 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
||||||
proxy_type_str(px), px->id);
|
proxy_type_str(px), px->id);
|
||||||
*err_code |= ERR_WARN;
|
*err_code |= ERR_WARN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bind_conf->ssl_conf.early_data && conn_calc_max_streams(1)) {
|
||||||
|
ha_notice("Binding [%s:%d] for %s %s: "
|
||||||
|
"stream elasticity is ignored for initial connection settings as this is incompatible with 0-RTT.",
|
||||||
|
bind_conf->file, bind_conf->line,
|
||||||
|
proxy_type_str(px), px->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif /* USE_QUIC */
|
#endif /* USE_QUIC */
|
||||||
|
|
||||||
|
|
@ -2849,13 +2872,13 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
||||||
* due to the proxy's mode not being taken into account
|
* due to the proxy's mode not being taken into account
|
||||||
* on first pass. Let's adjust it now.
|
* on first pass. Let's adjust it now.
|
||||||
*/
|
*/
|
||||||
mux_ent = conn_get_best_mux_entry(newsrv->mux_proto->token, PROTO_SIDE_BE, srv_is_quic(newsrv), mode);
|
mux_ent = conn_get_best_mux_entry(newsrv->mux_proto->mux_proto, IST_NULL, PROTO_SIDE_BE, srv_is_quic(newsrv), mode);
|
||||||
|
|
||||||
if (!mux_ent || !isteq(mux_ent->token, newsrv->mux_proto->token)) {
|
if (!mux_ent || !isteq(mux_ent->mux_proto, newsrv->mux_proto->mux_proto)) {
|
||||||
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for server '%s' at [%s:%d].\n",
|
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for server '%s' at [%s:%d].\n",
|
||||||
proxy_type_str(px), px->id,
|
proxy_type_str(px), px->id,
|
||||||
(int)newsrv->mux_proto->token.len,
|
(int)newsrv->mux_proto->mux_proto.len,
|
||||||
newsrv->mux_proto->token.ptr,
|
newsrv->mux_proto->mux_proto.ptr,
|
||||||
newsrv->id, newsrv->conf.file, newsrv->conf.line);
|
newsrv->id, newsrv->conf.file, newsrv->conf.line);
|
||||||
cfgerr++;
|
cfgerr++;
|
||||||
}
|
}
|
||||||
|
|
@ -2863,16 +2886,16 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
||||||
if ((mux_ent->mux->flags & MX_FL_FRAMED) && !srv_is_quic(newsrv)) {
|
if ((mux_ent->mux->flags & MX_FL_FRAMED) && !srv_is_quic(newsrv)) {
|
||||||
ha_alert("%s '%s' : MUX protocol '%.*s' is incompatible with stream transport used by server '%s' at [%s:%d].\n",
|
ha_alert("%s '%s' : MUX protocol '%.*s' is incompatible with stream transport used by server '%s' at [%s:%d].\n",
|
||||||
proxy_type_str(px), px->id,
|
proxy_type_str(px), px->id,
|
||||||
(int)newsrv->mux_proto->token.len,
|
(int)newsrv->mux_proto->mux_proto.len,
|
||||||
newsrv->mux_proto->token.ptr,
|
newsrv->mux_proto->mux_proto.ptr,
|
||||||
newsrv->id, newsrv->conf.file, newsrv->conf.line);
|
newsrv->id, newsrv->conf.file, newsrv->conf.line);
|
||||||
cfgerr++;
|
cfgerr++;
|
||||||
}
|
}
|
||||||
else if (!(mux_ent->mux->flags & MX_FL_FRAMED) && srv_is_quic(newsrv)) {
|
else if (!(mux_ent->mux->flags & MX_FL_FRAMED) && srv_is_quic(newsrv)) {
|
||||||
ha_alert("%s '%s' : MUX protocol '%.*s' is incompatible with framed transport used by server '%s' at [%s:%d].\n",
|
ha_alert("%s '%s' : MUX protocol '%.*s' is incompatible with framed transport used by server '%s' at [%s:%d].\n",
|
||||||
proxy_type_str(px), px->id,
|
proxy_type_str(px), px->id,
|
||||||
(int)newsrv->mux_proto->token.len,
|
(int)newsrv->mux_proto->mux_proto.len,
|
||||||
newsrv->mux_proto->token.ptr,
|
newsrv->mux_proto->mux_proto.ptr,
|
||||||
newsrv->id, newsrv->conf.file, newsrv->conf.line);
|
newsrv->id, newsrv->conf.file, newsrv->conf.line);
|
||||||
cfgerr++;
|
cfgerr++;
|
||||||
}
|
}
|
||||||
|
|
@ -3562,7 +3585,7 @@ struct proxy *parse_new_proxy(const char *name, unsigned int cap,
|
||||||
ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg);
|
ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg);
|
||||||
free(errmsg);
|
free(errmsg);
|
||||||
|
|
||||||
ha_free(&curproxy);
|
proxy_drop(curproxy);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5443,8 +5466,8 @@ static int cli_io_handler_show_errors(struct appctx *appctx)
|
||||||
|
|
||||||
/* register cli keywords */
|
/* register cli keywords */
|
||||||
static struct cli_kw_list cli_kws = {{ },{
|
static struct cli_kw_list cli_kws = {{ },{
|
||||||
{ { "add", "backend", NULL }, "add backend <backend> : add a new backend", cli_parse_add_backend, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
|
{ { "add", "backend", NULL }, "add backend <backend> : add a new backend", cli_parse_add_backend, NULL, NULL, NULL, 0 },
|
||||||
{ { "del", "backend", NULL }, "del backend <backend> : delete a backend", cli_parse_delete_backend, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
|
{ { "del", "backend", NULL }, "del backend <backend> : delete a backend", cli_parse_delete_backend, NULL, NULL, NULL, 0 },
|
||||||
{ { "disable", "frontend", NULL }, "disable frontend <frontend> : temporarily disable specific frontend", cli_parse_disable_frontend, NULL, NULL },
|
{ { "disable", "frontend", NULL }, "disable frontend <frontend> : temporarily disable specific frontend", cli_parse_disable_frontend, NULL, NULL },
|
||||||
{ { "enable", "frontend", NULL }, "enable frontend <frontend> : re-enable specific frontend", cli_parse_enable_frontend, NULL, NULL },
|
{ { "enable", "frontend", NULL }, "enable frontend <frontend> : re-enable specific frontend", cli_parse_enable_frontend, NULL, NULL },
|
||||||
{ { "publish", "backend", NULL }, "publish backend <backend> : mark backend as ready for traffic", cli_parse_publish_backend, NULL, NULL },
|
{ { "publish", "backend", NULL }, "publish backend <backend> : mark backend as ready for traffic", cli_parse_publish_backend, NULL, NULL },
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,10 @@ static int qmux_parse_frm(struct qcc *qcc, struct buffer *buf)
|
||||||
struct qf_reset_stream *rst_frm = &frm.reset_stream;
|
struct qf_reset_stream *rst_frm = &frm.reset_stream;
|
||||||
qcc_recv_reset_stream(qcc, rst_frm->id, rst_frm->app_error_code, rst_frm->final_size);
|
qcc_recv_reset_stream(qcc, rst_frm->id, rst_frm->app_error_code, rst_frm->final_size);
|
||||||
}
|
}
|
||||||
|
else if (frm.type == QUIC_FT_STOP_SENDING) {
|
||||||
|
struct qf_stop_sending *ss_frm = &frm.stop_sending;
|
||||||
|
qcc_recv_stop_sending(qcc, ss_frm->id, ss_frm->app_error_code);
|
||||||
|
}
|
||||||
else if (frm.type == QUIC_FT_MAX_DATA) {
|
else if (frm.type == QUIC_FT_MAX_DATA) {
|
||||||
struct qf_max_data *md_frm = &frm.max_data;
|
struct qf_max_data *md_frm = &frm.max_data;
|
||||||
qcc_recv_max_data(qcc, md_frm->max_data);
|
qcc_recv_max_data(qcc, md_frm->max_data);
|
||||||
|
|
@ -82,13 +86,26 @@ static int qmux_parse_frm(struct qcc *qcc, struct buffer *buf)
|
||||||
struct qf_max_streams *ms_frm = &frm.max_streams_bidi;
|
struct qf_max_streams *ms_frm = &frm.max_streams_bidi;
|
||||||
qcc_recv_max_streams(qcc, ms_frm->max_streams, 1);
|
qcc_recv_max_streams(qcc, ms_frm->max_streams, 1);
|
||||||
}
|
}
|
||||||
|
else if (frm.type == QUIC_FT_MAX_STREAMS_UNI) {
|
||||||
|
struct qf_max_streams *ms_frm = &frm.max_streams_uni;
|
||||||
|
qcc_recv_max_streams(qcc, ms_frm->max_streams, 0);
|
||||||
|
}
|
||||||
else if (frm.type == QUIC_FT_DATA_BLOCKED ||
|
else if (frm.type == QUIC_FT_DATA_BLOCKED ||
|
||||||
frm.type == QUIC_FT_STREAM_DATA_BLOCKED ||
|
frm.type == QUIC_FT_STREAM_DATA_BLOCKED ||
|
||||||
frm.type == QUIC_FT_STREAMS_BLOCKED_BIDI ||
|
frm.type == QUIC_FT_STREAMS_BLOCKED_BIDI ||
|
||||||
frm.type == QUIC_FT_STREAMS_BLOCKED_UNI) {
|
frm.type == QUIC_FT_STREAMS_BLOCKED_UNI) {
|
||||||
/* TODO */
|
/* TODO */
|
||||||
|
CHECK_IF("received flow control blocked frame not yet handled in QMux");
|
||||||
|
}
|
||||||
|
else if (frm.type == QUIC_FT_PADDING) {
|
||||||
|
CHECK_IF("received padding frame not yet handled in QMux");
|
||||||
|
}
|
||||||
|
else if (frm.type == QUIC_FT_CONNECTION_CLOSE ||
|
||||||
|
frm.type == QUIC_FT_CONNECTION_CLOSE_APP) {
|
||||||
|
CHECK_IF("received connection_close frame not yet handled in QMux");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
/* qmux_is_frm_valid() must prevent this */
|
||||||
ABORT_NOW();
|
ABORT_NOW();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -378,7 +378,7 @@ int quic_get_cid_tid(const unsigned char *cid, size_t cid_len,
|
||||||
|
|
||||||
tree = &quic_fe_cid_trees[quic_cid_tree_idx(&derive_cid)];
|
tree = &quic_fe_cid_trees[quic_cid_tree_idx(&derive_cid)];
|
||||||
HA_RWLOCK_RDLOCK(QC_CID_LOCK, &tree->lock);
|
HA_RWLOCK_RDLOCK(QC_CID_LOCK, &tree->lock);
|
||||||
node = ebmb_lookup(&tree->root, cid, cid_len);
|
node = ebmb_lookup(&tree->root, derive_cid.data, derive_cid.len);
|
||||||
if (node) {
|
if (node) {
|
||||||
conn_id = ebmb_entry(node, struct quic_connection_id, node);
|
conn_id = ebmb_entry(node, struct quic_connection_id, node);
|
||||||
cid_tid = HA_ATOMIC_LOAD(&conn_id->tid);
|
cid_tid = HA_ATOMIC_LOAD(&conn_id->tid);
|
||||||
|
|
|
||||||
|
|
@ -710,7 +710,7 @@ static inline int ha_quic_set_write_secret(SSL *ssl, enum ssl_encryption_level_t
|
||||||
|
|
||||||
static int ha_quic_flush_flight(SSL *ssl)
|
static int ha_quic_flush_flight(SSL *ssl)
|
||||||
{
|
{
|
||||||
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
struct quic_conn __maybe_unused *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_FFLIGHT, qc);
|
TRACE_ENTER(QUIC_EV_CONN_FFLIGHT, qc);
|
||||||
TRACE_LEAVE(QUIC_EV_CONN_FFLIGHT, qc);
|
TRACE_LEAVE(QUIC_EV_CONN_FFLIGHT, qc);
|
||||||
|
|
|
||||||
|
|
@ -1060,7 +1060,7 @@ int quic_tls_key_update(struct quic_conn *qc)
|
||||||
struct quic_tls_secrets *rx = &tls_ctx->rx;
|
struct quic_tls_secrets *rx = &tls_ctx->rx;
|
||||||
struct quic_tls_secrets *tx = &tls_ctx->tx;
|
struct quic_tls_secrets *tx = &tls_ctx->tx;
|
||||||
/* Used only for the traces */
|
/* Used only for the traces */
|
||||||
struct quic_kp_trace kp_trace = {
|
struct quic_kp_trace __maybe_unused kp_trace = {
|
||||||
.rx_sec = rx->secret,
|
.rx_sec = rx->secret,
|
||||||
.rx_seclen = rx->secretlen,
|
.rx_seclen = rx->secretlen,
|
||||||
.tx_sec = tx->secret,
|
.tx_sec = tx->secret,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <haproxy/connection.h>
|
||||||
#include <haproxy/global.h>
|
#include <haproxy/global.h>
|
||||||
#include <haproxy/ncbuf-t.h>
|
#include <haproxy/ncbuf-t.h>
|
||||||
#include <haproxy/net_helper.h>
|
#include <haproxy/net_helper.h>
|
||||||
|
|
@ -70,7 +71,7 @@ void quic_transport_params_init(struct quic_transport_params *p, int server)
|
||||||
quic_tune.be.max_idle_timeout;
|
quic_tune.be.max_idle_timeout;
|
||||||
|
|
||||||
/* Set limit on number of concurrently opened streams. */
|
/* Set limit on number of concurrently opened streams. */
|
||||||
p->initial_max_streams_bidi = max_streams_bidi;
|
p->initial_max_streams_bidi = server ? max_streams_bidi : 0;
|
||||||
p->initial_max_streams_uni = max_streams_uni;
|
p->initial_max_streams_uni = max_streams_uni;
|
||||||
|
|
||||||
/* Set connection flow-control data limit, either from configuration,
|
/* Set connection flow-control data limit, either from configuration,
|
||||||
|
|
@ -862,6 +863,15 @@ int qc_lstnr_params_init(struct quic_conn *qc,
|
||||||
rx_params->initial_source_connection_id.len = scidlen;
|
rx_params->initial_source_connection_id.len = scidlen;
|
||||||
TRACE_PROTO("\nRX(local) transp. params.", QUIC_EV_TRANSP_PARAMS, qc, rx_params);
|
TRACE_PROTO("\nRX(local) transp. params.", QUIC_EV_TRANSP_PARAMS, qc, rx_params);
|
||||||
|
|
||||||
|
/* Reduce max-streams-bidi if stream elasticity is active. This is
|
||||||
|
* ignored however if 0-RTT is configured as clients could reuse
|
||||||
|
* different transport parameters than the one advertised.
|
||||||
|
*/
|
||||||
|
if (global.tune.streams_elasticity && !qc->li->bind_conf->ssl_conf.early_data) {
|
||||||
|
rx_params->initial_max_streams_bidi =
|
||||||
|
conn_calc_max_streams(rx_params->initial_max_streams_bidi);
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -444,7 +444,7 @@ INITCALL0(STG_REGISTER, regex_register_build_options);
|
||||||
#ifdef USE_PCRE2
|
#ifdef USE_PCRE2
|
||||||
static int init_pcre2_per_thread(void)
|
static int init_pcre2_per_thread(void)
|
||||||
{
|
{
|
||||||
local_pcre2_match = pcre2_match_data_create(MAX_MATCH - 1, NULL);
|
local_pcre2_match = pcre2_match_data_create(MAX_MATCH, NULL);
|
||||||
if (!local_pcre2_match) {
|
if (!local_pcre2_match) {
|
||||||
ha_alert("Failed to allocate PCRE2 match data context for thread %u.\n", tid);
|
ha_alert("Failed to allocate PCRE2 match data context for thread %u.\n", tid);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ struct show_resolvers_ctx {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* returns the currently accepted address families as a combination of
|
/* returns the currently accepted address families as a combination of
|
||||||
* RSLV_ACCEPT_IPV4 and RSLV_ACCEPT_IPV6 only. It will dynamically adapt adapt
|
* RSLV_ACCEPT_IPV4 and RSLV_ACCEPT_IPV6 only. It will dynamically adapt
|
||||||
* the IPv6 status to sock_inet6_seems_reachable if RSLV_AUTO_FAMILY is set,
|
* the IPv6 status to sock_inet6_seems_reachable if RSLV_AUTO_FAMILY is set,
|
||||||
* otherwise returns the relevant bits of resolv_accept_families.
|
* otherwise returns the relevant bits of resolv_accept_families.
|
||||||
*/
|
*/
|
||||||
|
|
@ -509,7 +509,7 @@ resolv_run_resolution(struct resolv_resolution *resolution)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Check if a resolution has already been started for this server return
|
/* Check if a resolution has already been started for this server return
|
||||||
* directly to avoid resolution pill up. */
|
* directly to avoid resolution pile up. */
|
||||||
if (resolution->step != RSLV_STEP_NONE)
|
if (resolution->step != RSLV_STEP_NONE)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -624,17 +624,21 @@ int resolv_read_name(unsigned char *buffer, unsigned char *bufend,
|
||||||
|
|
||||||
/* Name compression is in use */
|
/* Name compression is in use */
|
||||||
if ((*reader & 0xc0) == 0xc0) {
|
if ((*reader & 0xc0) == 0xc0) {
|
||||||
|
uint16_t ptr_offset;
|
||||||
|
|
||||||
if (reader + 1 >= bufend)
|
if (reader + 1 >= bufend)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
ptr_offset = (*reader & 0x3f) * 256 + reader[1];
|
||||||
|
|
||||||
/* Must point BEFORE current position */
|
/* Must point BEFORE current position */
|
||||||
if ((buffer + reader[1]) > reader)
|
if ((buffer + ptr_offset) >= reader)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (depth++ > 100)
|
if (depth++ > 100)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
n = resolv_read_name(buffer, bufend, buffer + (*reader & 0x3f)*256 + reader[1],
|
n = resolv_read_name(buffer, bufend, buffer + ptr_offset,
|
||||||
dest, dest_len - nb_bytes, offset, depth);
|
dest, dest_len - nb_bytes, offset, depth);
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
@ -1232,8 +1236,7 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
|
||||||
if (reader + 4 > bufend)
|
if (reader + 4 > bufend)
|
||||||
goto invalid_resp;
|
goto invalid_resp;
|
||||||
|
|
||||||
answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536
|
answer_record->ttl = read_n32(reader);
|
||||||
+ reader[2] * 256 + reader[3];
|
|
||||||
reader += 4;
|
reader += 4;
|
||||||
|
|
||||||
/* Now reading data len */
|
/* Now reading data len */
|
||||||
|
|
@ -1430,7 +1433,7 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
|
||||||
len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE,
|
len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE,
|
||||||
&offset, 0);
|
&offset, 0);
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
continue;
|
goto invalid_resp;
|
||||||
|
|
||||||
if (reader + offset + 10 >= bufend)
|
if (reader + offset + 10 >= bufend)
|
||||||
goto invalid_resp;
|
goto invalid_resp;
|
||||||
|
|
@ -1466,11 +1469,8 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
|
||||||
offset = 0;
|
offset = 0;
|
||||||
len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
|
len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
|
||||||
|
|
||||||
if (len == 0) {
|
if (len == 0)
|
||||||
pool_free(resolv_answer_item_pool, answer_record);
|
goto invalid_resp;
|
||||||
answer_record = NULL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(answer_record->name, tmpname, len);
|
memcpy(answer_record->name, tmpname, len);
|
||||||
answer_record->name[len] = 0;
|
answer_record->name[len] = 0;
|
||||||
|
|
@ -1497,8 +1497,7 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
|
||||||
if (reader + 4 > bufend)
|
if (reader + 4 > bufend)
|
||||||
goto invalid_resp;
|
goto invalid_resp;
|
||||||
|
|
||||||
answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536
|
answer_record->ttl = read_n32(reader);
|
||||||
+ reader[2] * 256 + reader[3];
|
|
||||||
reader += 4;
|
reader += 4;
|
||||||
|
|
||||||
/* Now reading data len */
|
/* Now reading data len */
|
||||||
|
|
@ -1598,7 +1597,6 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
|
||||||
tmp_record->ar_item == NULL &&
|
tmp_record->ar_item == NULL &&
|
||||||
memcmp(tmp_record->data.target, answer_record->name, tmp_record->data_len) == 0) {
|
memcmp(tmp_record->data.target, answer_record->name, tmp_record->data_len) == 0) {
|
||||||
/* Always use the received additional record to refresh info */
|
/* Always use the received additional record to refresh info */
|
||||||
pool_free(resolv_answer_item_pool, tmp_record->ar_item);
|
|
||||||
tmp_record->ar_item = answer_record;
|
tmp_record->ar_item = answer_record;
|
||||||
answer_record = NULL;
|
answer_record = NULL;
|
||||||
break;
|
break;
|
||||||
|
|
@ -1854,7 +1852,15 @@ int resolv_dn_label_to_str(const char *dn, int dn_len, char *str, int str_len)
|
||||||
|
|
||||||
ptr = str;
|
ptr = str;
|
||||||
for (i = 0; i < dn_len; ++i) {
|
for (i = 0; i < dn_len; ++i) {
|
||||||
sz = dn[i];
|
sz = (unsigned char)dn[i];
|
||||||
|
|
||||||
|
if (!sz)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Check str_len adding 1 for the dot if (i!=0) and 1 for null terminator */
|
||||||
|
if (str_len < sz+i+(!!i)+1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
if (i)
|
if (i)
|
||||||
*ptr++ = '.';
|
*ptr++ = '.';
|
||||||
/* copy the string at i+1 to lower case */
|
/* copy the string at i+1 to lower case */
|
||||||
|
|
@ -2695,6 +2701,7 @@ static void resolvers_destroy(struct resolvers *resolvers)
|
||||||
dns_ring_free(ns->stream->ring_req);
|
dns_ring_free(ns->stream->ring_req);
|
||||||
task_destroy(ns->stream->task_req);
|
task_destroy(ns->stream->task_req);
|
||||||
task_destroy(ns->stream->task_rsp);
|
task_destroy(ns->stream->task_rsp);
|
||||||
|
task_destroy(ns->stream->task_idle);
|
||||||
free(ns->stream);
|
free(ns->stream);
|
||||||
}
|
}
|
||||||
LIST_DEL_INIT(&ns->list);
|
LIST_DEL_INIT(&ns->list);
|
||||||
|
|
@ -3198,7 +3205,7 @@ enum act_return resolv_action_do_resolve(struct act_rule *rule, struct proxy *px
|
||||||
if (resolution->status == RSLV_STATUS_NONE)
|
if (resolution->status == RSLV_STATUS_NONE)
|
||||||
goto yield;
|
goto yield;
|
||||||
if (resolution->status == RSLV_STATUS_VALID) {
|
if (resolution->status == RSLV_STATUS_VALID) {
|
||||||
struct sample smp;
|
struct sample smp = { 0 };
|
||||||
short ip_sin_family = 0;
|
short ip_sin_family = 0;
|
||||||
void *ip = NULL;
|
void *ip = NULL;
|
||||||
|
|
||||||
|
|
@ -3404,8 +3411,8 @@ enum act_parse_ret resolv_parse_do_resolve(const char **args, int *orig_arg, str
|
||||||
ha_free(&rule->arg.resolv.varname);
|
ha_free(&rule->arg.resolv.varname);
|
||||||
ha_free(&rule->arg.resolv.resolvers_id);
|
ha_free(&rule->arg.resolv.resolvers_id);
|
||||||
ha_free(&rule->arg.resolv.opts);
|
ha_free(&rule->arg.resolv.opts);
|
||||||
memprintf(err, "Can't parse '%s'. Expects 'do-resolve(<varname>,<resolvers>[,<options>]) <expr>'. Available options are 'ipv4' and 'ipv6'",
|
memprintf(err, "Can't parse '%s'%s%s. Expects 'do-resolve(<varname>,<resolvers>[,<options>]) <expr>'. Available options are 'ipv4' and 'ipv6'",
|
||||||
args[cur_arg]);
|
args[cur_arg], *err ? ": " : "", *err ? *err : "");
|
||||||
return ACT_RET_PRS_ERR;
|
return ACT_RET_PRS_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3476,7 +3483,7 @@ static int parse_resolve_conf(char **errmsg, char **warnmsg)
|
||||||
int duplicate_name = 0;
|
int duplicate_name = 0;
|
||||||
int err_code = 0;
|
int err_code = 0;
|
||||||
|
|
||||||
if ((resolv_line = malloc(sizeof(*resolv_line) * LINESIZE)) == NULL) {
|
if ((resolv_line = malloc(array_size_or_fail(sizeof(*resolv_line), LINESIZE))) == NULL) {
|
||||||
memprintf(errmsg, "out of memory.\n");
|
memprintf(errmsg, "out of memory.\n");
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
goto resolv_out;
|
goto resolv_out;
|
||||||
|
|
@ -3568,6 +3575,8 @@ static int parse_resolve_conf(char **errmsg, char **warnmsg)
|
||||||
if (errmsg)
|
if (errmsg)
|
||||||
memprintf(errmsg, "parsing [/etc/resolv.conf:%d] : out of memory.", resolv_linenum);
|
memprintf(errmsg, "parsing [/etc/resolv.conf:%d] : out of memory.", resolv_linenum);
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
dns_ring_free(newnameserver->dgram->ring_req);
|
||||||
|
free(newnameserver->dgram);
|
||||||
free(newnameserver);
|
free(newnameserver);
|
||||||
goto resolv_out;
|
goto resolv_out;
|
||||||
}
|
}
|
||||||
|
|
@ -3578,6 +3587,8 @@ static int parse_resolve_conf(char **errmsg, char **warnmsg)
|
||||||
memprintf(errmsg, "parsing [/etc/resolv.conf:%d] : out of memory.", resolv_linenum);
|
memprintf(errmsg, "parsing [/etc/resolv.conf:%d] : out of memory.", resolv_linenum);
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
free((char *)newnameserver->conf.file);
|
free((char *)newnameserver->conf.file);
|
||||||
|
dns_ring_free(newnameserver->dgram->ring_req);
|
||||||
|
free(newnameserver->dgram);
|
||||||
free(newnameserver);
|
free(newnameserver);
|
||||||
goto resolv_out;
|
goto resolv_out;
|
||||||
}
|
}
|
||||||
|
|
@ -3668,6 +3679,7 @@ err_free_conf_file:
|
||||||
ha_free((void **)&r->conf.file);
|
ha_free((void **)&r->conf.file);
|
||||||
err_free_p:
|
err_free_p:
|
||||||
proxy_drop(p);
|
proxy_drop(p);
|
||||||
|
LIST_DEL_INIT(&r->list);
|
||||||
err_free_r:
|
err_free_r:
|
||||||
ha_free(&r);
|
ha_free(&r);
|
||||||
return err_code;
|
return err_code;
|
||||||
|
|
@ -3712,6 +3724,7 @@ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
|
||||||
ha_alert("Parsing [%s:%d]: resolvers '%s' has same name as another resolvers (declared at %s:%d).\n",
|
ha_alert("Parsing [%s:%d]: resolvers '%s' has same name as another resolvers (declared at %s:%d).\n",
|
||||||
file, linenum, args[1], curr_resolvers->conf.file, curr_resolvers->conf.line);
|
file, linenum, args[1], curr_resolvers->conf.file, curr_resolvers->conf.line);
|
||||||
err_code |= ERR_ALERT | ERR_ABORT;
|
err_code |= ERR_ALERT | ERR_ABORT;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3749,6 +3762,7 @@ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
|
||||||
ha_alert("Parsing [%s:%d]: nameserver '%s' has same name as another nameserver (declared at %s:%d).\n",
|
ha_alert("Parsing [%s:%d]: nameserver '%s' has same name as another nameserver (declared at %s:%d).\n",
|
||||||
file, linenum, args[1], newnameserver->conf.file, newnameserver->conf.line);
|
file, linenum, args[1], newnameserver->conf.file, newnameserver->conf.line);
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3772,30 +3786,56 @@ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
|
||||||
SRV_PARSE_PARSE_ADDR|SRV_PARSE_INITIAL_RESOLVE);
|
SRV_PARSE_PARSE_ADDR|SRV_PARSE_INITIAL_RESOLVE);
|
||||||
if (err_code & (ERR_FATAL|ERR_ABORT)) {
|
if (err_code & (ERR_FATAL|ERR_ABORT)) {
|
||||||
err_code |= ERR_ABORT;
|
err_code |= ERR_ABORT;
|
||||||
|
free(newnameserver);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dns_stream_init(newnameserver, curr_resolvers->px->srv) < 0) {
|
if (dns_stream_init(newnameserver, curr_resolvers->px->srv) < 0) {
|
||||||
ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
||||||
err_code |= ERR_ALERT|ERR_ABORT;
|
err_code |= ERR_ALERT|ERR_ABORT;
|
||||||
|
free(newnameserver);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dns_dgram_init(newnameserver, sk) < 0) {
|
else if (dns_dgram_init(newnameserver, sk) < 0) {
|
||||||
ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
||||||
err_code |= ERR_ALERT | ERR_ABORT;
|
err_code |= ERR_ALERT | ERR_ABORT;
|
||||||
|
free(newnameserver);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((newnameserver->conf.file = strdup(file)) == NULL) {
|
if ((newnameserver->conf.file = strdup(file)) == NULL) {
|
||||||
ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
||||||
err_code |= ERR_ALERT | ERR_ABORT;
|
err_code |= ERR_ALERT | ERR_ABORT;
|
||||||
|
if (newnameserver->stream) {
|
||||||
|
dns_ring_free(newnameserver->stream->ring_req);
|
||||||
|
task_destroy(newnameserver->stream->task_req);
|
||||||
|
task_destroy(newnameserver->stream->task_rsp);
|
||||||
|
task_destroy(newnameserver->stream->task_idle);
|
||||||
|
free(newnameserver->stream);
|
||||||
|
} else if (newnameserver->dgram) {
|
||||||
|
dns_ring_free(newnameserver->dgram->ring_req);
|
||||||
|
free(newnameserver->dgram);
|
||||||
|
}
|
||||||
|
free(newnameserver);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((newnameserver->id = strdup(args[1])) == NULL) {
|
if ((newnameserver->id = strdup(args[1])) == NULL) {
|
||||||
ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
||||||
err_code |= ERR_ALERT | ERR_ABORT;
|
err_code |= ERR_ALERT | ERR_ABORT;
|
||||||
|
free((char *)newnameserver->conf.file);
|
||||||
|
if (newnameserver->stream) {
|
||||||
|
dns_ring_free(newnameserver->stream->ring_req);
|
||||||
|
task_destroy(newnameserver->stream->task_req);
|
||||||
|
task_destroy(newnameserver->stream->task_rsp);
|
||||||
|
task_destroy(newnameserver->stream->task_idle);
|
||||||
|
free(newnameserver->stream);
|
||||||
|
} else if (newnameserver->dgram) {
|
||||||
|
dns_ring_free(newnameserver->dgram->ring_req);
|
||||||
|
free(newnameserver->dgram);
|
||||||
|
}
|
||||||
|
free(newnameserver);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2150,11 +2150,11 @@ static int sample_conv_be2hex_check(struct arg *args, struct sample_conv *conv,
|
||||||
*/
|
*/
|
||||||
static int sample_conv_be2hex(const struct arg *args, struct sample *smp, void *private)
|
static int sample_conv_be2hex(const struct arg *args, struct sample *smp, void *private)
|
||||||
{
|
{
|
||||||
struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data);
|
struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data * 2);
|
||||||
int chunk_size = args[1].data.sint;
|
int chunk_size = args[1].data.sint;
|
||||||
const int last = args[2].data.sint ? smp->data.u.str.data - chunk_size + 1 : smp->data.u.str.data;
|
const int last = args[2].data.sint ? smp->data.u.str.data - chunk_size + 1 : smp->data.u.str.data;
|
||||||
int i;
|
int i;
|
||||||
int max_size;
|
size_t max_size;
|
||||||
int ptr = 0;
|
int ptr = 0;
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
|
|
||||||
|
|
@ -2163,7 +2163,9 @@ static int sample_conv_be2hex(const struct arg *args, struct sample *smp, void *
|
||||||
trash->data = 0;
|
trash->data = 0;
|
||||||
if (args[0].data.str.data == 0 && args[2].data.sint == 0)
|
if (args[0].data.str.data == 0 && args[2].data.sint == 0)
|
||||||
chunk_size = smp->data.u.str.data;
|
chunk_size = smp->data.u.str.data;
|
||||||
max_size = trash->size - 2 * chunk_size;
|
if (2 * (size_t)chunk_size > trash->size)
|
||||||
|
return 0;
|
||||||
|
max_size = trash->size - 2 * (size_t)chunk_size;
|
||||||
|
|
||||||
while (ptr < last && trash->data <= max_size) {
|
while (ptr < last && trash->data <= max_size) {
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
|
|
|
||||||
112
src/server.c
112
src/server.c
|
|
@ -145,6 +145,7 @@ static void srv_reset_path_parameters(struct server *s)
|
||||||
{
|
{
|
||||||
HA_RWLOCK_WRLOCK(SERVER_LOCK, &s->path_params.param_lock);
|
HA_RWLOCK_WRLOCK(SERVER_LOCK, &s->path_params.param_lock);
|
||||||
s->path_params.nego_alpn[0] = 0;
|
s->path_params.nego_alpn[0] = 0;
|
||||||
|
s->path_params.srv_hash = 0;
|
||||||
HA_RWLOCK_WRUNLOCK(SERVER_LOCK, &s->path_params.param_lock);
|
HA_RWLOCK_WRUNLOCK(SERVER_LOCK, &s->path_params.param_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -643,7 +644,7 @@ int srv_check_reuse_ws(struct server *srv)
|
||||||
* for mux selection.
|
* for mux selection.
|
||||||
*/
|
*/
|
||||||
const struct ist srv_mux = srv->mux_proto ?
|
const struct ist srv_mux = srv->mux_proto ?
|
||||||
srv->mux_proto->token : IST_NULL;
|
srv->mux_proto->mux_proto : IST_NULL;
|
||||||
|
|
||||||
switch (srv->ws) {
|
switch (srv->ws) {
|
||||||
/* "auto" means use the same protocol : reuse is possible. */
|
/* "auto" means use the same protocol : reuse is possible. */
|
||||||
|
|
@ -935,6 +936,8 @@ static int srv_parse_enabled(char **args, int *cur_arg,
|
||||||
newsrv->next_state = SRV_ST_RUNNING;
|
newsrv->next_state = SRV_ST_RUNNING;
|
||||||
newsrv->check.state &= ~CHK_ST_PAUSED;
|
newsrv->check.state &= ~CHK_ST_PAUSED;
|
||||||
newsrv->check.health = newsrv->check.rise;
|
newsrv->check.health = newsrv->check.rise;
|
||||||
|
|
||||||
|
srv_set_init_state(newsrv);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1013,13 +1016,19 @@ static int srv_parse_hash_key(char **args, int *cur_arg,
|
||||||
struct proxy *curproxy, struct server *newsrv, char **err)
|
struct proxy *curproxy, struct server *newsrv, char **err)
|
||||||
{
|
{
|
||||||
if (!args[*cur_arg + 1]) {
|
if (!args[*cur_arg + 1]) {
|
||||||
memprintf(err, "'%s expects 'id', 'addr', or 'addr-port' value", args[*cur_arg]);
|
memprintf(err, "'%s expects 'id', 'id32', 'guid', 'addr', or 'addr-port' value", args[*cur_arg]);
|
||||||
return ERR_ALERT | ERR_FATAL;
|
return ERR_ALERT | ERR_FATAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(args[*cur_arg + 1], "id") == 0) {
|
if (strcmp(args[*cur_arg + 1], "id") == 0) {
|
||||||
newsrv->hash_key = SRV_HASH_KEY_ID;
|
newsrv->hash_key = SRV_HASH_KEY_ID;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(args[*cur_arg + 1], "id32") == 0) {
|
||||||
|
newsrv->hash_key = SRV_HASH_KEY_ID32;
|
||||||
|
}
|
||||||
|
else if (strcmp(args[*cur_arg + 1], "guid") == 0) {
|
||||||
|
newsrv->hash_key = SRV_HASH_KEY_GUID;
|
||||||
|
}
|
||||||
else if (strcmp(args[*cur_arg + 1], "addr") == 0) {
|
else if (strcmp(args[*cur_arg + 1], "addr") == 0) {
|
||||||
newsrv->hash_key = SRV_HASH_KEY_ADDR;
|
newsrv->hash_key = SRV_HASH_KEY_ADDR;
|
||||||
}
|
}
|
||||||
|
|
@ -1027,7 +1036,7 @@ static int srv_parse_hash_key(char **args, int *cur_arg,
|
||||||
newsrv->hash_key = SRV_HASH_KEY_ADDR_PORT;
|
newsrv->hash_key = SRV_HASH_KEY_ADDR_PORT;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
memprintf(err, "'%s' has to be 'id', 'addr', or 'addr-port'", args[*cur_arg]);
|
memprintf(err, "'%s' has to be 'id', 'id32', 'guid', 'addr', or 'addr-port'", args[*cur_arg]);
|
||||||
return ERR_ALERT | ERR_FATAL;
|
return ERR_ALERT | ERR_FATAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1124,22 +1133,25 @@ static int srv_parse_init_addr(char **args, int *cur_arg,
|
||||||
|
|
||||||
/* Parse the "init-state" server keyword */
|
/* Parse the "init-state" server keyword */
|
||||||
static int srv_parse_init_state(char **args, int *cur_arg,
|
static int srv_parse_init_state(char **args, int *cur_arg,
|
||||||
struct proxy *curproxy, struct server *newsrv, char **err)
|
struct proxy *curproxy, struct server *newsrv, char **err)
|
||||||
{
|
{
|
||||||
if (strcmp(args[*cur_arg + 1], "fully-up") == 0)
|
if (strcmp(args[*cur_arg + 1], "none") == 0)
|
||||||
newsrv->init_state= SRV_INIT_STATE_FULLY_UP;
|
newsrv->init_state = SRV_INIT_STATE_NONE;
|
||||||
|
else if (strcmp(args[*cur_arg + 1], "fully-up") == 0)
|
||||||
|
newsrv->init_state = SRV_INIT_STATE_FULLY_UP;
|
||||||
else if (strcmp(args[*cur_arg + 1], "up") == 0)
|
else if (strcmp(args[*cur_arg + 1], "up") == 0)
|
||||||
newsrv->init_state = SRV_INIT_STATE_UP;
|
newsrv->init_state = SRV_INIT_STATE_UP;
|
||||||
else if (strcmp(args[*cur_arg + 1], "down") == 0)
|
else if (strcmp(args[*cur_arg + 1], "down") == 0)
|
||||||
newsrv->init_state= SRV_INIT_STATE_DOWN;
|
newsrv->init_state = SRV_INIT_STATE_DOWN;
|
||||||
else if (strcmp(args[*cur_arg + 1], "fully-down") == 0)
|
else if (strcmp(args[*cur_arg + 1], "fully-down") == 0)
|
||||||
newsrv->init_state= SRV_INIT_STATE_FULLY_DOWN;
|
newsrv->init_state = SRV_INIT_STATE_FULLY_DOWN;
|
||||||
else {
|
else {
|
||||||
memprintf(err, "'%s' expects one of 'fully-up', 'up', 'down', or 'fully-down' but got '%s'",
|
memprintf(err, "'%s' expects one of 'none', 'fully-up', 'up', 'down', or 'fully-down' but got '%s'",
|
||||||
args[*cur_arg], args[*cur_arg + 1]);
|
args[*cur_arg], args[*cur_arg + 1]);
|
||||||
return ERR_ALERT | ERR_FATAL;
|
return ERR_ALERT | ERR_FATAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srv_set_init_state(newsrv);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1308,19 +1320,20 @@ static int srv_parse_pool_max_conn(char **args, int *cur_arg, struct proxy *curp
|
||||||
static int srv_parse_id(char **args, int *cur_arg, struct proxy *curproxy, struct server *newsrv, char **err)
|
static int srv_parse_id(char **args, int *cur_arg, struct proxy *curproxy, struct server *newsrv, char **err)
|
||||||
{
|
{
|
||||||
struct server *target;
|
struct server *target;
|
||||||
|
llong id;
|
||||||
|
|
||||||
if (!*args[*cur_arg + 1]) {
|
if (!*args[*cur_arg + 1]) {
|
||||||
memprintf(err, "'%s' : expects an integer argument", args[*cur_arg]);
|
memprintf(err, "'%s' : expects an integer argument", args[*cur_arg]);
|
||||||
return ERR_ALERT | ERR_FATAL;
|
return ERR_ALERT | ERR_FATAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
newsrv->puid = atol(args[*cur_arg + 1]);
|
id = atol(args[*cur_arg + 1]);
|
||||||
|
if (id < 1 || id > ~0U) {
|
||||||
if (newsrv->puid <= 0) {
|
memprintf(err, "'%s' : custom id has to be between 1 and 4294967295.", args[*cur_arg]);
|
||||||
memprintf(err, "'%s' : custom id has to be > 0", args[*cur_arg]);
|
|
||||||
return ERR_ALERT | ERR_FATAL;
|
return ERR_ALERT | ERR_FATAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newsrv->puid = id;
|
||||||
target = server_find_by_id(curproxy, newsrv->puid);
|
target = server_find_by_id(curproxy, newsrv->puid);
|
||||||
if (target) {
|
if (target) {
|
||||||
memprintf(err, "'%s' : custom id %d already used at %s:%d ('server %s')",
|
memprintf(err, "'%s' : custom id %d already used at %s:%d ('server %s')",
|
||||||
|
|
@ -2880,7 +2893,7 @@ void srv_settings_init(struct server *srv)
|
||||||
srv->agent.fall = DEF_AGENT_FALLTIME;
|
srv->agent.fall = DEF_AGENT_FALLTIME;
|
||||||
srv->agent.port = 0;
|
srv->agent.port = 0;
|
||||||
|
|
||||||
srv->init_state = SRV_INIT_STATE_UP;
|
srv->init_state = SRV_INIT_STATE_NONE;
|
||||||
|
|
||||||
srv->maxqueue = 0;
|
srv->maxqueue = 0;
|
||||||
srv->minconn = 0;
|
srv->minconn = 0;
|
||||||
|
|
@ -3014,7 +3027,9 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
|
||||||
srv->check.rise = srv->check.health = src->check.rise;
|
srv->check.rise = srv->check.health = src->check.rise;
|
||||||
srv->check.fall = src->check.fall;
|
srv->check.fall = src->check.fall;
|
||||||
|
|
||||||
/* Here we check if 'disabled' is the default server state */
|
/* Here we check if 'disabled' is the default server state. Otherwise,
|
||||||
|
* we check 'init-state' parameter
|
||||||
|
*/
|
||||||
if (src->next_admin & (SRV_ADMF_CMAINT | SRV_ADMF_FMAINT)) {
|
if (src->next_admin & (SRV_ADMF_CMAINT | SRV_ADMF_FMAINT)) {
|
||||||
srv->next_admin |= SRV_ADMF_CMAINT | SRV_ADMF_FMAINT;
|
srv->next_admin |= SRV_ADMF_CMAINT | SRV_ADMF_FMAINT;
|
||||||
srv->next_state = SRV_ST_STOPPED;
|
srv->next_state = SRV_ST_STOPPED;
|
||||||
|
|
@ -3042,6 +3057,7 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
|
||||||
srv->init_addr = src->init_addr;
|
srv->init_addr = src->init_addr;
|
||||||
|
|
||||||
srv->init_state = src->init_state;
|
srv->init_state = src->init_state;
|
||||||
|
srv_set_init_state(srv);
|
||||||
#if defined(USE_OPENSSL)
|
#if defined(USE_OPENSSL)
|
||||||
srv_ssl_settings_cpy(srv, src);
|
srv_ssl_settings_cpy(srv, src);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -3055,8 +3071,8 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
|
||||||
srv->tcp_ut = src->tcp_ut;
|
srv->tcp_ut = src->tcp_ut;
|
||||||
#endif
|
#endif
|
||||||
srv->mux_proto = src->mux_proto;
|
srv->mux_proto = src->mux_proto;
|
||||||
if (srv->pool_conn_name)
|
if (src->pool_conn_name)
|
||||||
srv->pool_conn_name = strdup(srv->pool_conn_name);
|
srv->pool_conn_name = strdup(src->pool_conn_name);
|
||||||
srv->pool_purge_delay = src->pool_purge_delay;
|
srv->pool_purge_delay = src->pool_purge_delay;
|
||||||
srv->low_idle_conns = src->low_idle_conns;
|
srv->low_idle_conns = src->low_idle_conns;
|
||||||
srv->max_idle_conns = src->max_idle_conns;
|
srv->max_idle_conns = src->max_idle_conns;
|
||||||
|
|
@ -3616,7 +3632,7 @@ int srv_postinit(struct server *srv)
|
||||||
|
|
||||||
/* initialize idle conns lists */
|
/* initialize idle conns lists */
|
||||||
if (srv->max_idle_conns != 0) {
|
if (srv->max_idle_conns != 0) {
|
||||||
srv->curr_idle_thr = ha_aligned_zalloc(64, global.nbthread * sizeof(*srv->curr_idle_thr));
|
srv->curr_idle_thr = ha_aligned_zalloc(64, array_size_or_fail(global.nbthread, sizeof(*srv->curr_idle_thr)));
|
||||||
if (!srv->curr_idle_thr) {
|
if (!srv->curr_idle_thr) {
|
||||||
ha_alert("memory error during idle conn list init for %s/%s server\n",
|
ha_alert("memory error during idle conn list init for %s/%s server\n",
|
||||||
srv->proxy->id, srv->id);
|
srv->proxy->id, srv->id);
|
||||||
|
|
@ -3958,6 +3974,10 @@ static int _srv_parse_finalize(char **args, int cur_arg,
|
||||||
ha_alert("unable to enable checks and tracking at the same time!\n");
|
ha_alert("unable to enable checks and tracking at the same time!\n");
|
||||||
return ERR_ALERT | ERR_FATAL;
|
return ERR_ALERT | ERR_FATAL;
|
||||||
}
|
}
|
||||||
|
if (srv->init_state != SRV_INIT_STATE_NONE && srv->trackit) {
|
||||||
|
ha_alert("unable to set init-state and tracking at the same time!\n");
|
||||||
|
return ERR_ALERT | ERR_FATAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (srv->do_agent && !srv->agent.port) {
|
if (srv->do_agent && !srv->agent.port) {
|
||||||
ha_alert("server %s does not have agent port. Agent check has been disabled.\n",
|
ha_alert("server %s does not have agent port. Agent check has been disabled.\n",
|
||||||
|
|
@ -4024,6 +4044,18 @@ static int _srv_parse_finalize(char **args, int cur_arg,
|
||||||
return ERR_ALERT | ERR_FATAL;
|
return ERR_ALERT | ERR_FATAL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if (srv->proxy->mode == PR_MODE_HTTP && !srv->mux_proto &&
|
||||||
|
srv->ssl_ctx.alpn_str && strstr(srv->ssl_ctx.alpn_str, "\002h3")) {
|
||||||
|
if (!experimental_directives_allowed) {
|
||||||
|
ha_alert("HTTP/3 on TCP listed via ALPN requires the QMUX protocol which is experimental, "
|
||||||
|
"must be allowed via a global 'expose-experimental-directives'.\n");
|
||||||
|
return ERR_ALERT | ERR_FATAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!(srv->proxy->cap & PR_CAP_LB)) {
|
if (!(srv->proxy->cap & PR_CAP_LB)) {
|
||||||
/* No need to wait for effective proxy mode, it is already known:
|
/* No need to wait for effective proxy mode, it is already known:
|
||||||
|
|
@ -5226,6 +5258,8 @@ int srv_set_fqdn(struct server *srv, const char *hostname, int resolv_locked)
|
||||||
struct resolv_resolution *resolution;
|
struct resolv_resolution *resolution;
|
||||||
char *hostname_dn;
|
char *hostname_dn;
|
||||||
int hostname_len, hostname_dn_len;
|
int hostname_len, hostname_dn_len;
|
||||||
|
char *hostname_dup = NULL;
|
||||||
|
char *hostname_dn_dup = NULL;
|
||||||
|
|
||||||
/* Note that the server lock is already held. */
|
/* Note that the server lock is already held. */
|
||||||
if (!srv->resolvers)
|
if (!srv->resolvers)
|
||||||
|
|
@ -5256,13 +5290,18 @@ int srv_set_fqdn(struct server *srv, const char *hostname, int resolv_locked)
|
||||||
|
|
||||||
resolv_unlink_resolution(srv->resolv_requester);
|
resolv_unlink_resolution(srv->resolv_requester);
|
||||||
|
|
||||||
|
hostname_dup = strdup(hostname);
|
||||||
|
hostname_dn_dup = strdup(hostname_dn);
|
||||||
|
if (!hostname_dup || !hostname_dn_dup)
|
||||||
|
goto err;
|
||||||
|
|
||||||
free(srv->hostname);
|
free(srv->hostname);
|
||||||
free(srv->hostname_dn);
|
free(srv->hostname_dn);
|
||||||
srv->hostname = strdup(hostname);
|
srv->hostname = hostname_dup;
|
||||||
srv->hostname_dn = strdup(hostname_dn);
|
srv->hostname_dn = hostname_dn_dup;
|
||||||
srv->hostname_dn_len = hostname_dn_len;
|
srv->hostname_dn_len = hostname_dn_len;
|
||||||
if (!srv->hostname || !srv->hostname_dn)
|
hostname_dup = NULL;
|
||||||
goto err;
|
hostname_dn_dup = NULL;
|
||||||
|
|
||||||
if (srv->flags & SRV_F_NO_RESOLUTION)
|
if (srv->flags & SRV_F_NO_RESOLUTION)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
@ -5278,6 +5317,8 @@ int srv_set_fqdn(struct server *srv, const char *hostname, int resolv_locked)
|
||||||
err:
|
err:
|
||||||
if (!resolv_locked)
|
if (!resolv_locked)
|
||||||
HA_SPIN_UNLOCK(DNS_LOCK, &srv->resolvers->lock);
|
HA_SPIN_UNLOCK(DNS_LOCK, &srv->resolvers->lock);
|
||||||
|
ha_free(&hostname_dup);
|
||||||
|
ha_free(&hostname_dn_dup);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5702,11 +5743,13 @@ static int cli_parse_set_server(char **args, char *payload, struct appctx *appct
|
||||||
HA_SPIN_LOCK(SERVER_LOCK, &sv->lock);
|
HA_SPIN_LOCK(SERVER_LOCK, &sv->lock);
|
||||||
if (strcmp(args[4], "on") == 0) {
|
if (strcmp(args[4], "on") == 0) {
|
||||||
if (srv_set_ssl(sv, 1)) {
|
if (srv_set_ssl(sv, 1)) {
|
||||||
|
HA_SPIN_UNLOCK(SERVER_LOCK, &sv->lock);
|
||||||
cli_dynerr(appctx, memprintf(&err, "failed to enable ssl for server %s.\n", args[2]));
|
cli_dynerr(appctx, memprintf(&err, "failed to enable ssl for server %s.\n", args[2]));
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
} else if (strcmp(args[4], "off") == 0) {
|
} else if (strcmp(args[4], "off") == 0) {
|
||||||
if (srv_set_ssl(sv, 0)) {
|
if (srv_set_ssl(sv, 0)) {
|
||||||
|
HA_SPIN_UNLOCK(SERVER_LOCK, &sv->lock);
|
||||||
cli_dynerr(appctx, memprintf(&err, "failed to disable ssl for server %s.\n", args[2]));
|
cli_dynerr(appctx, memprintf(&err, "failed to disable ssl for server %s.\n", args[2]));
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -5916,6 +5959,7 @@ static int cli_parse_enable_health(char **args, char *payload, struct appctx *ap
|
||||||
|
|
||||||
HA_SPIN_LOCK(SERVER_LOCK, &sv->lock);
|
HA_SPIN_LOCK(SERVER_LOCK, &sv->lock);
|
||||||
sv->check.state |= CHK_ST_ENABLED;
|
sv->check.state |= CHK_ST_ENABLED;
|
||||||
|
srv_set_init_state(sv);
|
||||||
HA_SPIN_UNLOCK(SERVER_LOCK, &sv->lock);
|
HA_SPIN_UNLOCK(SERVER_LOCK, &sv->lock);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -6062,8 +6106,8 @@ static int srv_init_per_thr(struct server *srv)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
srv->per_thr = ha_aligned_zalloc(64, global.nbthread * sizeof(*srv->per_thr));
|
srv->per_thr = ha_aligned_zalloc(64, array_size_or_fail(global.nbthread, sizeof(*srv->per_thr)));
|
||||||
srv->per_tgrp = ha_aligned_zalloc(64, global.nbtgroups * sizeof(*srv->per_tgrp));
|
srv->per_tgrp = ha_aligned_zalloc(64, array_size_or_fail(global.nbtgroups, sizeof(*srv->per_tgrp)));
|
||||||
if (!srv->per_thr || !srv->per_tgrp)
|
if (!srv->per_thr || !srv->per_tgrp)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
@ -6254,9 +6298,9 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
|
||||||
int proto_mode = conn_pr_mode_to_proto_mode(be->mode);
|
int proto_mode = conn_pr_mode_to_proto_mode(be->mode);
|
||||||
const struct mux_proto_list *mux_ent;
|
const struct mux_proto_list *mux_ent;
|
||||||
|
|
||||||
mux_ent = conn_get_best_mux_entry(srv->mux_proto->token, PROTO_SIDE_BE, srv_is_quic(srv), proto_mode);
|
mux_ent = conn_get_best_mux_entry(srv->mux_proto->mux_proto, IST_NULL, PROTO_SIDE_BE, srv_is_quic(srv), proto_mode);
|
||||||
|
|
||||||
if (!mux_ent || !isteq(mux_ent->token, srv->mux_proto->token)) {
|
if (!mux_ent || !isteq(mux_ent->mux_proto, srv->mux_proto->mux_proto)) {
|
||||||
ha_alert("MUX protocol is not usable for server.\n");
|
ha_alert("MUX protocol is not usable for server.\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -6960,20 +7004,12 @@ static int _srv_update_status_adm(struct server *s, enum srv_adm_st_chg_cause ca
|
||||||
*/
|
*/
|
||||||
if (s->check.state & CHK_ST_ENABLED) {
|
if (s->check.state & CHK_ST_ENABLED) {
|
||||||
s->check.state &= ~CHK_ST_PAUSED;
|
s->check.state &= ~CHK_ST_PAUSED;
|
||||||
if(s->init_state == SRV_INIT_STATE_FULLY_UP) {
|
s->check.health = s->check.rise; /* start OK but check immediately */
|
||||||
s->check.health = s->check.rise + s->check.fall - 1; /* initially UP, when all checks fail to bring server DOWN */
|
|
||||||
}
|
|
||||||
else if(s->init_state == SRV_INIT_STATE_DOWN) {
|
|
||||||
s->check.health = s->check.rise - 1; /* initially DOWN, when one check is successful bring server UP */
|
|
||||||
}
|
|
||||||
else if(s->init_state == SRV_INIT_STATE_FULLY_DOWN) {
|
|
||||||
s->check.health = 0; /* initially DOWN, when all checks are successful bring server UP */
|
|
||||||
} else {
|
|
||||||
s->check.health = s->check.rise; /* initially UP, when one check fails check brings server DOWN */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
srv_set_init_state(s);
|
||||||
|
|
||||||
if ((!s->track || s->track->next_state != SRV_ST_STOPPED) &&
|
if (s->init_state == SRV_INIT_STATE_NONE &&
|
||||||
|
(!s->track || s->track->next_state != SRV_ST_STOPPED) &&
|
||||||
(!(s->agent.state & CHK_ST_ENABLED) || (s->agent.health >= s->agent.rise)) &&
|
(!(s->agent.state & CHK_ST_ENABLED) || (s->agent.health >= s->agent.rise)) &&
|
||||||
(!(s->check.state & CHK_ST_ENABLED) || (s->check.health >= s->check.rise))) {
|
(!(s->check.state & CHK_ST_ENABLED) || (s->check.health >= s->check.rise))) {
|
||||||
if (s->track && s->track->next_state == SRV_ST_STOPPING) {
|
if (s->track && s->track->next_state == SRV_ST_STOPPING) {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ static const struct trace_event sess_trace_events[] = {
|
||||||
{ .mask = SESS_EV_NEW, .name = "sess_new", .desc = "new session creation" },
|
{ .mask = SESS_EV_NEW, .name = "sess_new", .desc = "new session creation" },
|
||||||
#define SESS_EV_END (1ULL << 1)
|
#define SESS_EV_END (1ULL << 1)
|
||||||
{ .mask = SESS_EV_END, .name = "sess_end", .desc = "session termination" },
|
{ .mask = SESS_EV_END, .name = "sess_end", .desc = "session termination" },
|
||||||
#define SESS_EV_ERR (1ULL << 1)
|
#define SESS_EV_ERR (1ULL << 2)
|
||||||
{ .mask = SESS_EV_ERR, .name = "sess_err", .desc = "session error" },
|
{ .mask = SESS_EV_ERR, .name = "sess_err", .desc = "session error" },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
@ -241,14 +241,17 @@ int session_accept_fd(struct connection *cli_conn)
|
||||||
if (l->bind_conf->options & BC_O_ACC_CIP)
|
if (l->bind_conf->options & BC_O_ACC_CIP)
|
||||||
cli_conn->flags |= CO_FL_ACCEPT_CIP;
|
cli_conn->flags |= CO_FL_ACCEPT_CIP;
|
||||||
|
|
||||||
if (l->bind_conf->mux_proto && isteq(l->bind_conf->mux_proto->token, ist("qmux")))
|
|
||||||
cli_conn->flags |= (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
|
|
||||||
|
|
||||||
/* Add the handshake pseudo-XPRT */
|
/* Add the handshake pseudo-XPRT */
|
||||||
if (cli_conn->flags & (CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP)) {
|
if (cli_conn->flags & (CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP)) {
|
||||||
if (xprt_add_hs(cli_conn) != 0)
|
if (xprt_add_hs(cli_conn) != 0)
|
||||||
goto out_free_conn;
|
goto out_free_conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add handshake layer prior to MUX init if required. Does nothing if SSL layer is active though. */
|
||||||
|
if (l->bind_conf->mux_proto && l->bind_conf->mux_proto->init_xprt) {
|
||||||
|
if (xprt_add_l6hs(cli_conn, l->bind_conf->mux_proto->init_xprt))
|
||||||
|
goto out_free_conn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reversed conns already have an assigned session, do not recreate it. */
|
/* Reversed conns already have an assigned session, do not recreate it. */
|
||||||
|
|
@ -351,7 +354,7 @@ int session_accept_fd(struct connection *cli_conn)
|
||||||
* v | | |
|
* v | | |
|
||||||
* conn -- owner ---> task <-----+
|
* conn -- owner ---> task <-----+
|
||||||
*/
|
*/
|
||||||
if (cli_conn->flags & (CO_FL_WAIT_XPRT | CO_FL_EARLY_SSL_HS)) {
|
if (cli_conn->flags & (CO_FL_WAIT_XPRT | CO_FL_EARLY_SSL_HS | CO_FL_WAIT_XPRT_L6)) {
|
||||||
int timeout;
|
int timeout;
|
||||||
int clt_tmt = p->timeout.client;
|
int clt_tmt = p->timeout.client;
|
||||||
int hs_tmt = p->timeout.client_hs;
|
int hs_tmt = p->timeout.client_hs;
|
||||||
|
|
|
||||||
|
|
@ -533,7 +533,7 @@ int sock_get_old_sockets(const char *unixsocket)
|
||||||
|
|
||||||
}
|
}
|
||||||
memset(&msghdr, 0, sizeof(msghdr));
|
memset(&msghdr, 0, sizeof(msghdr));
|
||||||
cmsgbuf = malloc(CMSG_SPACE(sizeof(int)) * MAX_SEND_FD);
|
cmsgbuf = malloc(array_size_or_fail(CMSG_SPACE(sizeof(int)), MAX_SEND_FD));
|
||||||
if (!cmsgbuf) {
|
if (!cmsgbuf) {
|
||||||
ha_warning("Failed to allocate memory to send sockets\n");
|
ha_warning("Failed to allocate memory to send sockets\n");
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -561,13 +561,13 @@ int sock_get_old_sockets(const char *unixsocket)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpbuf = malloc(fd_nb * (1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof(int)));
|
tmpbuf = malloc(array_size_or_fail(fd_nb, (1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof(int))));
|
||||||
if (tmpbuf == NULL) {
|
if (tmpbuf == NULL) {
|
||||||
ha_warning("Failed to allocate memory while receiving sockets\n");
|
ha_warning("Failed to allocate memory while receiving sockets\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpfd = malloc(fd_nb * sizeof(int));
|
tmpfd = malloc(array_size_or_fail(fd_nb, sizeof(int)));
|
||||||
if (tmpfd == NULL) {
|
if (tmpfd == NULL) {
|
||||||
ha_warning("Failed to allocate memory while receiving sockets\n");
|
ha_warning("Failed to allocate memory while receiving sockets\n");
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -1011,6 +1011,7 @@ int sock_conn_check(struct connection *conn)
|
||||||
goto wait;
|
goto wait;
|
||||||
|
|
||||||
if (errno && errno != EISCONN) {
|
if (errno && errno != EISCONN) {
|
||||||
|
conn_set_errno(conn, errno);
|
||||||
conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_connect_err);
|
conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_connect_err);
|
||||||
goto out_error;
|
goto out_error;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1099,7 +1099,7 @@ struct ckch_store *ckchs_dup(const struct ckch_store *src)
|
||||||
/* copy the array of domain strings */
|
/* copy the array of domain strings */
|
||||||
|
|
||||||
while (src->conf.acme.domains[n]) {
|
while (src->conf.acme.domains[n]) {
|
||||||
r = my_realloc2(r, sizeof(char *) * (n + 2));
|
r = my_realloc2(r, array_size_or_fail(sizeof(char *), (n + 2)));
|
||||||
if (!r)
|
if (!r)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|
@ -1120,7 +1120,7 @@ struct ckch_store *ckchs_dup(const struct ckch_store *src)
|
||||||
/* copy the array of IP strings */
|
/* copy the array of IP strings */
|
||||||
|
|
||||||
while (src->conf.acme.ips[n]) {
|
while (src->conf.acme.ips[n]) {
|
||||||
r = my_realloc2(r, sizeof(char *) * (n + 2));
|
r = my_realloc2(r, array_size_or_fail(sizeof(char *), (n + 2)));
|
||||||
if (!r)
|
if (!r)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|
@ -5329,7 +5329,7 @@ int ckch_conf_parse(char **args, int cur_arg, struct ckch_conf *f, int *found, c
|
||||||
do {
|
do {
|
||||||
while (*e != ',' && *e != '\0')
|
while (*e != ',' && *e != '\0')
|
||||||
e++;
|
e++;
|
||||||
r = my_realloc2(r, sizeof(char *) * (n + 2));
|
r = my_realloc2(r, array_size_or_fail(sizeof(char *), (n + 2)));
|
||||||
if (!r) {
|
if (!r) {
|
||||||
ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
|
ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
|
||||||
err_code |= ERR_ALERT | ERR_ABORT;
|
err_code |= ERR_ALERT | ERR_ABORT;
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg)
|
||||||
if ((TRACE_SOURCE)->verbosity >= SSL_VERB_ADVANCED) {
|
if ((TRACE_SOURCE)->verbosity >= SSL_VERB_ADVANCED) {
|
||||||
if (TRACE_ENABLED(TRACE_LEVEL_DATA, SSL_EV_CONN_CIPHERS_EXT, conn, 0, 0, 0)) {
|
if (TRACE_ENABLED(TRACE_LEVEL_DATA, SSL_EV_CONN_CIPHERS_EXT, conn, 0, 0, 0)) {
|
||||||
const uint8_t *cipher_suites;
|
const uint8_t *cipher_suites;
|
||||||
size_t len;
|
size_t __maybe_unused len;
|
||||||
|
|
||||||
#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
|
#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
|
||||||
len = ctx->cipher_suites_len;
|
len = ctx->cipher_suites_len;
|
||||||
|
|
|
||||||
|
|
@ -356,8 +356,10 @@ int ssl_sock_generate_certificate(const char *servername, struct bind_conf *bind
|
||||||
ssl_ctx = (SSL_CTX *)lru->data;
|
ssl_ctx = (SSL_CTX *)lru->data;
|
||||||
if (!ssl_ctx && lru) {
|
if (!ssl_ctx && lru) {
|
||||||
ssl_ctx = ssl_sock_do_create_cert(servername, bind_conf, ssl);
|
ssl_ctx = ssl_sock_do_create_cert(servername, bind_conf, ssl);
|
||||||
if (!ssl_ctx)
|
if (!ssl_ctx) {
|
||||||
|
HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock);
|
||||||
goto error;
|
goto error;
|
||||||
|
}
|
||||||
lru64_commit(lru, ssl_ctx, cacert, 0, (void (*)(void *))SSL_CTX_free);
|
lru64_commit(lru, ssl_ctx, cacert, 0, (void (*)(void *))SSL_CTX_free);
|
||||||
}
|
}
|
||||||
SSL_set_SSL_CTX(ssl, ssl_ctx);
|
SSL_set_SSL_CTX(ssl, ssl_ctx);
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,8 @@ int ssl_sock_load_ocsp_response(struct buffer *ocsp_response,
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
#ifdef HAVE_ASN1_TIME_TO_TM
|
#ifdef HAVE_ASN1_TIME_TO_TM
|
||||||
struct tm nextupd_tm = {0};
|
struct tm nextupd_tm = {0};
|
||||||
|
#else
|
||||||
|
long expire = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
resp = d2i_OCSP_RESPONSE(NULL, (const unsigned char **)&p,
|
resp = d2i_OCSP_RESPONSE(NULL, (const unsigned char **)&p,
|
||||||
|
|
@ -391,11 +393,12 @@ int ssl_sock_load_ocsp_response(struct buffer *ocsp_response,
|
||||||
}
|
}
|
||||||
ocsp->expire = my_timegm(&nextupd_tm) - OCSP_MAX_RESPONSE_TIME_SKEW;
|
ocsp->expire = my_timegm(&nextupd_tm) - OCSP_MAX_RESPONSE_TIME_SKEW;
|
||||||
#else
|
#else
|
||||||
ocsp->expire = asn1_generalizedtime_to_epoch(nextupd) - OCSP_MAX_RESPONSE_TIME_SKEW;
|
expire = asn1_generalizedtime_to_epoch(nextupd) - OCSP_MAX_RESPONSE_TIME_SKEW;
|
||||||
if (ocsp->expire < 0) {
|
if (expire < 0) {
|
||||||
memprintf(err, "OCSP single response: Invalid \"Next Update\" time");
|
memprintf(err, "OCSP single response: Invalid \"Next Update\" time");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
ocsp->expire = expire;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ocsp->expire < date.tv_sec) {
|
if (ocsp->expire < date.tv_sec) {
|
||||||
|
|
|
||||||
|
|
@ -3743,7 +3743,7 @@ static void ssl_sock_resize_passphrase_cache(void)
|
||||||
int idx;
|
int idx;
|
||||||
int new_size = passphrase_cache_size << 1;
|
int new_size = passphrase_cache_size << 1;
|
||||||
|
|
||||||
passphrase_randoms = my_realloc2(passphrase_randoms, sizeof(*passphrase_randoms) * (new_size));
|
passphrase_randoms = my_realloc2(passphrase_randoms, array_size_or_fail(sizeof(*passphrase_randoms), (new_size)));
|
||||||
if (!passphrase_randoms) {
|
if (!passphrase_randoms) {
|
||||||
ha_alert("ssl_sock_passwd_cb: passphrase randoms realloc failed");
|
ha_alert("ssl_sock_passwd_cb: passphrase randoms realloc failed");
|
||||||
passphrase_idx = -1;
|
passphrase_idx = -1;
|
||||||
|
|
@ -3759,7 +3759,7 @@ static void ssl_sock_resize_passphrase_cache(void)
|
||||||
|
|
||||||
if (passphrase_cache_size) {
|
if (passphrase_cache_size) {
|
||||||
passphrase_cache_size = new_size;
|
passphrase_cache_size = new_size;
|
||||||
passphrase_cache = my_realloc2(passphrase_cache, sizeof(*passphrase_cache) * passphrase_cache_size);
|
passphrase_cache = my_realloc2(passphrase_cache, array_size_or_fail(sizeof(*passphrase_cache), passphrase_cache_size));
|
||||||
if (!passphrase_cache) {
|
if (!passphrase_cache) {
|
||||||
ha_alert("ssl_sock_passwd_cb: passphrase cache realloc failed");
|
ha_alert("ssl_sock_passwd_cb: passphrase cache realloc failed");
|
||||||
passphrase_idx = -1;
|
passphrase_idx = -1;
|
||||||
|
|
@ -4287,13 +4287,15 @@ static int ssl_sess_new_srv_cb(SSL *ssl, SSL_SESSION *sess)
|
||||||
if (ssl_sock_get_alpn(conn, qc->xprt_ctx, &alpn, &len)) {
|
if (ssl_sock_get_alpn(conn, qc->xprt_ctx, &alpn, &len)) {
|
||||||
struct quic_early_transport_params *etps = &s->path_params.tps;
|
struct quic_early_transport_params *etps = &s->path_params.tps;
|
||||||
|
|
||||||
if (len < sizeof(s->path_params.nego_alpn) &&
|
if (s->path_params.srv_hash != conn->hash_node.key ||
|
||||||
(len != strlen(s->path_params.nego_alpn) ||
|
(len < sizeof(s->path_params.nego_alpn) &&
|
||||||
memcmp(&s->path_params.nego_alpn, alpn, len) != 0)) {
|
(len != strlen(s->path_params.nego_alpn) ||
|
||||||
|
memcmp(&s->path_params.nego_alpn, alpn, len) != 0))) {
|
||||||
HA_RWLOCK_WRLOCK(SERVER_LOCK, &s->path_params.param_lock);
|
HA_RWLOCK_WRLOCK(SERVER_LOCK, &s->path_params.param_lock);
|
||||||
memcpy(&s->path_params.nego_alpn, alpn, len);
|
memcpy(&s->path_params.nego_alpn, alpn, len);
|
||||||
s->path_params.nego_alpn[len] = 0;
|
s->path_params.nego_alpn[len] = 0;
|
||||||
/* The transport parameters are not stored without ALPN */
|
/* The transport parameters are not stored without ALPN */
|
||||||
|
s->path_params.srv_hash = conn->hash_node.key;
|
||||||
qc_early_transport_params_cpy(qc, etps, &qc->tx.params);
|
qc_early_transport_params_cpy(qc, etps, &qc->tx.params);
|
||||||
HA_RWLOCK_WRUNLOCK(SERVER_LOCK, &s->path_params.param_lock);
|
HA_RWLOCK_WRUNLOCK(SERVER_LOCK, &s->path_params.param_lock);
|
||||||
}
|
}
|
||||||
|
|
@ -6920,12 +6922,14 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state)
|
||||||
|
|
||||||
srv = objt_server(conn->target);
|
srv = objt_server(conn->target);
|
||||||
if (srv && ssl_sock_get_alpn(conn, ctx, &alpn, &len)) {
|
if (srv && ssl_sock_get_alpn(conn, ctx, &alpn, &len)) {
|
||||||
if (len < sizeof(srv->path_params.nego_alpn) &&
|
if (srv->path_params.srv_hash != conn->hash_node.key ||
|
||||||
(len != strlen(srv->path_params.nego_alpn) ||
|
(len < sizeof(srv->path_params.nego_alpn) &&
|
||||||
memcmp(&srv->path_params.nego_alpn, alpn, len) != 0)) {
|
(len != strlen(srv->path_params.nego_alpn) ||
|
||||||
|
memcmp(&srv->path_params.nego_alpn, alpn, len) != 0))) {
|
||||||
HA_RWLOCK_WRLOCK(SERVER_LOCK, &srv->path_params.param_lock);
|
HA_RWLOCK_WRLOCK(SERVER_LOCK, &srv->path_params.param_lock);
|
||||||
memcpy(&srv->path_params.nego_alpn, alpn, len);
|
memcpy(&srv->path_params.nego_alpn, alpn, len);
|
||||||
srv->path_params.nego_alpn[len] = 0;
|
srv->path_params.nego_alpn[len] = 0;
|
||||||
|
srv->path_params.srv_hash = conn->hash_node.key;
|
||||||
HA_RWLOCK_WRUNLOCK(SERVER_LOCK, &srv->path_params.param_lock);
|
HA_RWLOCK_WRUNLOCK(SERVER_LOCK, &srv->path_params.param_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6962,29 +6966,35 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state)
|
||||||
* woke a tasklet already.
|
* woke a tasklet already.
|
||||||
*/
|
*/
|
||||||
if (ctx->conn->xprt_ctx == ctx) {
|
if (ctx->conn->xprt_ctx == ctx) {
|
||||||
|
const struct mux_proto_list *mux;
|
||||||
int closed_connection = 0;
|
int closed_connection = 0;
|
||||||
|
|
||||||
if (!ctx->conn->mux) {
|
if (!ctx->conn->mux) {
|
||||||
if (ctx->conn->flags & (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND)) {
|
mux = !conn_is_back(conn) ?
|
||||||
const struct xprt_ops *ops = xprt_get(XPRT_QMUX);
|
conn_select_mux_fe(conn) : conn_select_mux_be(conn);
|
||||||
void *xprt_ctx_hs = NULL;
|
|
||||||
|
|
||||||
ret = ops->init(conn, &xprt_ctx_hs);
|
if (mux->init_xprt) {
|
||||||
BUG_ON(ret);
|
ret = xprt_add_l6hs(conn, mux->init_xprt);
|
||||||
|
/* Frontend conn must be freed in case of XPRT init failure. */
|
||||||
|
if (ret) {
|
||||||
|
if (!conn_is_back(conn)) {
|
||||||
|
conn->flags |= CO_FL_ERROR; /* Ensure conn will be freed on next call. */
|
||||||
|
ret = conn_complete_session(conn);
|
||||||
|
BUG_ON(ret >= 0); /* conn_complete_session() expected to fail on CO_FL_ERROR */
|
||||||
|
t = NULL;
|
||||||
|
}
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
ret = ops->add_xprt(conn, xprt_ctx_hs,
|
ret = conn_xprt_start(conn);
|
||||||
conn->xprt_ctx, conn->xprt, NULL, NULL);
|
|
||||||
BUG_ON(ret);
|
|
||||||
|
|
||||||
conn->xprt = ops;
|
|
||||||
conn->xprt_ctx = xprt_ctx_hs;
|
|
||||||
|
|
||||||
|
|
||||||
ret = conn->xprt->start(conn, xprt_ctx_hs);
|
|
||||||
BUG_ON(ret);
|
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
|
/* TODO MUX selection already performs by conn_select_mux_fe/be().
|
||||||
|
* Implement an alternative to conn_create_mux() to skip this
|
||||||
|
* part and directly init the connection and its MUX.
|
||||||
|
*/
|
||||||
ret = conn_create_mux(ctx->conn, &closed_connection);
|
ret = conn_create_mux(ctx->conn, &closed_connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret >= 0 && ctx->conn->mux && !woke && ctx->conn->mux && ctx->conn->mux->wake) {
|
if (ret >= 0 && ctx->conn->mux && !woke && ctx->conn->mux && ctx->conn->mux->wake) {
|
||||||
|
|
|
||||||
|
|
@ -417,7 +417,7 @@ void apply_stats_file(void)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
line = malloc(sizeof(char) * LINESIZE);
|
line = malloc(array_size_or_fail(sizeof(char), LINESIZE));
|
||||||
if (!line) {
|
if (!line) {
|
||||||
ha_warning("config: Can't load stats-file '%s': line alloc error.\n", global.stats_file);
|
ha_warning("config: Can't load stats-file '%s': line alloc error.\n", global.stats_file);
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
||||||
|
|
@ -1208,7 +1208,7 @@ static int allocate_stats_px_postcheck(void)
|
||||||
|
|
||||||
stat_cols_len[STATS_DOMAIN_PROXY] += ST_I_PX_MAX;
|
stat_cols_len[STATS_DOMAIN_PROXY] += ST_I_PX_MAX;
|
||||||
|
|
||||||
stat_cols[STATS_DOMAIN_PROXY] = malloc(stat_cols_len[STATS_DOMAIN_PROXY] * sizeof(struct name_desc));
|
stat_cols[STATS_DOMAIN_PROXY] = malloc(array_size_or_fail(stat_cols_len[STATS_DOMAIN_PROXY], sizeof(struct name_desc)));
|
||||||
if (!stat_cols[STATS_DOMAIN_PROXY]) {
|
if (!stat_cols[STATS_DOMAIN_PROXY]) {
|
||||||
ha_alert("stats: cannot allocate all fields for proxy statistics\n");
|
ha_alert("stats: cannot allocate all fields for proxy statistics\n");
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
|
@ -1247,7 +1247,7 @@ static int allocate_stats_rslv_postcheck(void)
|
||||||
size_t i = 0, offset;
|
size_t i = 0, offset;
|
||||||
int err_code = 0;
|
int err_code = 0;
|
||||||
|
|
||||||
stat_cols[STATS_DOMAIN_RESOLVERS] = malloc(stat_cols_len[STATS_DOMAIN_RESOLVERS] * sizeof(struct name_desc));
|
stat_cols[STATS_DOMAIN_RESOLVERS] = malloc(array_size_or_fail(stat_cols_len[STATS_DOMAIN_RESOLVERS], sizeof(struct name_desc)));
|
||||||
if (!stat_cols[STATS_DOMAIN_RESOLVERS]) {
|
if (!stat_cols[STATS_DOMAIN_RESOLVERS]) {
|
||||||
ha_alert("stats: cannot allocate all fields for resolver statistics\n");
|
ha_alert("stats: cannot allocate all fields for resolver statistics\n");
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
|
@ -1282,7 +1282,7 @@ static int allocate_stat_lines_per_thread(void)
|
||||||
for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
|
for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
|
||||||
const int domain = domains[i];
|
const int domain = domains[i];
|
||||||
|
|
||||||
stat_lines[domain] = malloc(stat_cols_len[domain] * sizeof(struct field));
|
stat_lines[domain] = malloc(array_size_or_fail(stat_cols_len[domain], sizeof(struct field)));
|
||||||
if (!stat_lines[domain])
|
if (!stat_lines[domain])
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -276,6 +276,7 @@ int stktable_trash_oldest(struct stktable *t)
|
||||||
{
|
{
|
||||||
struct stksess *ts;
|
struct stksess *ts;
|
||||||
struct eb32_node *eb;
|
struct eb32_node *eb;
|
||||||
|
struct list tofree_list;
|
||||||
int max_search; // no more than 50% misses
|
int max_search; // no more than 50% misses
|
||||||
int max_per_bucket;
|
int max_per_bucket;
|
||||||
int done_per_bucket;
|
int done_per_bucket;
|
||||||
|
|
@ -289,7 +290,7 @@ int stktable_trash_oldest(struct stktable *t)
|
||||||
|
|
||||||
/* start from a random bucket number to avoid starvation in the last ones */
|
/* start from a random bucket number to avoid starvation in the last ones */
|
||||||
bucket = init_bucket = statistical_prng_range(CONFIG_HAP_TBL_BUCKETS - 1);
|
bucket = init_bucket = statistical_prng_range(CONFIG_HAP_TBL_BUCKETS - 1);
|
||||||
|
LIST_INIT(&tofree_list);
|
||||||
to_batch = STKTABLE_MAX_UPDATES_AT_ONCE;
|
to_batch = STKTABLE_MAX_UPDATES_AT_ONCE;
|
||||||
|
|
||||||
max_search = to_batch * 2; // no more than 50% misses
|
max_search = to_batch * 2; // no more than 50% misses
|
||||||
|
|
@ -390,7 +391,8 @@ int stktable_trash_oldest(struct stktable *t)
|
||||||
ebmb_delete(&ts->key);
|
ebmb_delete(&ts->key);
|
||||||
MT_LIST_DELETE(&ts->pend_updts);
|
MT_LIST_DELETE(&ts->pend_updts);
|
||||||
eb32_delete(&ts->upd);
|
eb32_delete(&ts->upd);
|
||||||
__stksess_free(t, ts);
|
LIST_APPEND(&tofree_list, mt_list_to_list(&ts->pend_updts));
|
||||||
|
|
||||||
batched++;
|
batched++;
|
||||||
done_per_bucket++;
|
done_per_bucket++;
|
||||||
|
|
||||||
|
|
@ -417,6 +419,12 @@ int stktable_trash_oldest(struct stktable *t)
|
||||||
bucket = 0;
|
bucket = 0;
|
||||||
} while (max_search > 0 && bucket != init_bucket);
|
} while (max_search > 0 && bucket != init_bucket);
|
||||||
|
|
||||||
|
while (!LIST_ISEMPTY(&tofree_list)) {
|
||||||
|
ts = LIST_ELEM(tofree_list.n, struct stksess *, pend_updts);
|
||||||
|
LIST_DELETE(mt_list_to_list(&ts->pend_updts));
|
||||||
|
__stksess_free(t, ts);
|
||||||
|
}
|
||||||
|
|
||||||
return batched;
|
return batched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -953,6 +961,7 @@ struct task *process_tables_expire(struct task *task, void *context, unsigned in
|
||||||
struct stktable *t;
|
struct stktable *t;
|
||||||
struct stksess *ts;
|
struct stksess *ts;
|
||||||
struct eb32_node *table_eb, *eb;
|
struct eb32_node *table_eb, *eb;
|
||||||
|
struct list tofree_list;
|
||||||
int updt_locked;
|
int updt_locked;
|
||||||
int to_visit;
|
int to_visit;
|
||||||
int task_exp;
|
int task_exp;
|
||||||
|
|
@ -960,6 +969,8 @@ struct task *process_tables_expire(struct task *task, void *context, unsigned in
|
||||||
|
|
||||||
task_exp = TICK_ETERNITY;
|
task_exp = TICK_ETERNITY;
|
||||||
|
|
||||||
|
LIST_INIT(&tofree_list);
|
||||||
|
|
||||||
bucket = (ps - &per_bucket[0]);
|
bucket = (ps - &per_bucket[0]);
|
||||||
|
|
||||||
to_visit = STKTABLE_MAX_UPDATES_AT_ONCE;
|
to_visit = STKTABLE_MAX_UPDATES_AT_ONCE;
|
||||||
|
|
@ -1088,7 +1099,7 @@ struct task *process_tables_expire(struct task *task, void *context, unsigned in
|
||||||
ebmb_delete(&ts->key);
|
ebmb_delete(&ts->key);
|
||||||
MT_LIST_DELETE(&ts->pend_updts);
|
MT_LIST_DELETE(&ts->pend_updts);
|
||||||
eb32_delete(&ts->upd);
|
eb32_delete(&ts->upd);
|
||||||
__stksess_free(t, ts);
|
LIST_APPEND(&tofree_list, mt_list_to_list(&ts->pend_updts));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updt_locked)
|
if (updt_locked)
|
||||||
|
|
@ -1110,6 +1121,13 @@ struct task *process_tables_expire(struct task *task, void *context, unsigned in
|
||||||
if (!tick_isset(task_exp) || (tick_isset(next_exp_table) && tick_is_lt(next_exp_table, task_exp)))
|
if (!tick_isset(task_exp) || (tick_isset(next_exp_table) && tick_is_lt(next_exp_table, task_exp)))
|
||||||
task_exp = next_exp_table;
|
task_exp = next_exp_table;
|
||||||
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->buckets[bucket].sh_lock);
|
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->buckets[bucket].sh_lock);
|
||||||
|
|
||||||
|
while (!LIST_ISEMPTY(&tofree_list)) {
|
||||||
|
ts = LIST_ELEM(tofree_list.n, struct stksess *, pend_updts);
|
||||||
|
LIST_DELETE(mt_list_to_list(&ts->pend_updts));
|
||||||
|
__stksess_free(t, ts);
|
||||||
|
}
|
||||||
|
|
||||||
tmpnode = eb32_next(table_eb);
|
tmpnode = eb32_next(table_eb);
|
||||||
|
|
||||||
if (table_eb->key != next_exp_table) {
|
if (table_eb->key != next_exp_table) {
|
||||||
|
|
@ -2675,10 +2693,12 @@ static enum act_return action_inc_gpc(struct act_rule *rule, struct proxy *px,
|
||||||
struct stkctr *stkctr;
|
struct stkctr *stkctr;
|
||||||
|
|
||||||
/* Extract the stksess, return OK if no stksess available. */
|
/* Extract the stksess, return OK if no stksess available. */
|
||||||
if (s)
|
if (s && s->stkctr)
|
||||||
stkctr = &s->stkctr[rule->arg.gpc.sc];
|
stkctr = &s->stkctr[rule->arg.gpc.sc];
|
||||||
else
|
else if (sess->stkctr)
|
||||||
stkctr = &sess->stkctr[rule->arg.gpc.sc];
|
stkctr = &sess->stkctr[rule->arg.gpc.sc];
|
||||||
|
else
|
||||||
|
return ACT_RET_CONT;
|
||||||
|
|
||||||
ts = stkctr_entry(stkctr);
|
ts = stkctr_entry(stkctr);
|
||||||
if (ts) {
|
if (ts) {
|
||||||
|
|
@ -2716,10 +2736,12 @@ static enum act_return action_inc_gpc0(struct act_rule *rule, struct proxy *px,
|
||||||
unsigned int period = 0;
|
unsigned int period = 0;
|
||||||
|
|
||||||
/* Extract the stksess, return OK if no stksess available. */
|
/* Extract the stksess, return OK if no stksess available. */
|
||||||
if (s)
|
if (s && s->stkctr)
|
||||||
stkctr = &s->stkctr[rule->arg.gpc.sc];
|
stkctr = &s->stkctr[rule->arg.gpc.sc];
|
||||||
else
|
else if (sess->stkctr)
|
||||||
stkctr = &sess->stkctr[rule->arg.gpc.sc];
|
stkctr = &sess->stkctr[rule->arg.gpc.sc];
|
||||||
|
else
|
||||||
|
return ACT_RET_CONT;
|
||||||
|
|
||||||
ts = stkctr_entry(stkctr);
|
ts = stkctr_entry(stkctr);
|
||||||
if (ts) {
|
if (ts) {
|
||||||
|
|
@ -5956,7 +5978,7 @@ static int stkt_create_stk_ctr_pool(void)
|
||||||
if (!global.tune.nb_stk_ctr)
|
if (!global.tune.nb_stk_ctr)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pool_head_stk_ctr = create_pool("stk_ctr", sizeof(*((struct session*)0)->stkctr) * global.tune.nb_stk_ctr, MEM_F_SHARED);
|
pool_head_stk_ctr = create_pool("stk_ctr", array_size_or_fail(sizeof(*((struct session*)0)->stkctr), global.tune.nb_stk_ctr), MEM_F_SHARED);
|
||||||
if (!pool_head_stk_ctr) {
|
if (!pool_head_stk_ctr) {
|
||||||
ha_alert("out of memory while creating the stick-counters pool.\n");
|
ha_alert("out of memory while creating the stick-counters pool.\n");
|
||||||
return ERR_ABORT;
|
return ERR_ABORT;
|
||||||
|
|
|
||||||
10
src/stream.c
10
src/stream.c
|
|
@ -1617,7 +1617,7 @@ int stream_set_http_mode(struct stream *s, const struct mux_proto_list *mux_prot
|
||||||
|
|
||||||
sc_conn_prepare_endp_upgrade(sc);
|
sc_conn_prepare_endp_upgrade(sc);
|
||||||
if (conn_upgrade_mux_fe(conn, sc, &s->req.buf,
|
if (conn_upgrade_mux_fe(conn, sc, &s->req.buf,
|
||||||
(mux_proto ? mux_proto->token : ist("")),
|
(mux_proto ? mux_proto->mux_proto : ist("")),
|
||||||
PROTO_MODE_HTTP) == -1) {
|
PROTO_MODE_HTTP) == -1) {
|
||||||
sc_conn_abort_endp_upgrade(sc);
|
sc_conn_abort_endp_upgrade(sc);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -3268,15 +3268,15 @@ static int check_tcp_switch_stream_mode(struct act_rule *rule, struct proxy *px,
|
||||||
px->options |= PR_O_HTTP_UPG;
|
px->options |= PR_O_HTTP_UPG;
|
||||||
|
|
||||||
if (mux_proto) {
|
if (mux_proto) {
|
||||||
mux_ent = conn_get_best_mux_entry(mux_proto->token, PROTO_SIDE_FE, 0, mode);
|
mux_ent = conn_get_best_mux_entry(mux_proto->mux_proto, IST_NULL, PROTO_SIDE_FE, 0, mode);
|
||||||
if (!mux_ent || !isteq(mux_ent->token, mux_proto->token)) {
|
if (!mux_ent || !isteq(mux_ent->mux_proto, mux_proto->mux_proto)) {
|
||||||
memprintf(err, "MUX protocol '%.*s' is not compatible with the selected mode",
|
memprintf(err, "MUX protocol '%.*s' is not compatible with the selected mode",
|
||||||
(int)mux_proto->token.len, mux_proto->token.ptr);
|
(int)mux_proto->mux_proto.len, mux_proto->mux_proto.ptr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mux_ent = conn_get_best_mux_entry(IST_NULL, PROTO_SIDE_FE, 0, mode);
|
mux_ent = conn_get_best_mux_entry(IST_NULL, IST_NULL, PROTO_SIDE_FE, 0, mode);
|
||||||
if (!mux_ent) {
|
if (!mux_ent) {
|
||||||
memprintf(err, "Unable to find compatible MUX protocol with the selected mode");
|
memprintf(err, "Unable to find compatible MUX protocol with the selected mode");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -834,6 +834,8 @@ enum tcpcheck_eval_ret tcpcheck_spop_expect_hello(struct check *check, struct tc
|
||||||
goto invalid_frame;
|
goto invalid_frame;
|
||||||
if (decode_varint(&ptr, end, &sz) == -1)
|
if (decode_varint(&ptr, end, &sz) == -1)
|
||||||
goto invalid_frame;
|
goto invalid_frame;
|
||||||
|
if (sz >= SPOP_ERR_ENTRIES)
|
||||||
|
sz = SPOP_ERR_UNKNOWN;
|
||||||
check->code = sz;
|
check->code = sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -989,7 +991,7 @@ enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct t
|
||||||
const char *sc = NULL; /* maxconn */
|
const char *sc = NULL; /* maxconn */
|
||||||
const char *err = NULL; /* first error to report */
|
const char *err = NULL; /* first error to report */
|
||||||
const char *wrn = NULL; /* first warning to report */
|
const char *wrn = NULL; /* first warning to report */
|
||||||
char *cmd, *p;
|
char *cmd, *p, *end;
|
||||||
|
|
||||||
TRACE_ENTER(CHK_EV_TCPCHK_EXP, check);
|
TRACE_ENTER(CHK_EV_TCPCHK_EXP, check);
|
||||||
|
|
||||||
|
|
@ -1018,10 +1020,11 @@ enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct t
|
||||||
*/
|
*/
|
||||||
|
|
||||||
p = b_head(&check->bi);
|
p = b_head(&check->bi);
|
||||||
while (*p && *p != '\n' && *p != '\r')
|
end = b_tail(&check->bi);
|
||||||
|
while (p < end && *p && *p != '\n' && *p != '\r')
|
||||||
p++;
|
p++;
|
||||||
|
|
||||||
if (!*p) {
|
if (!*p || p == end) {
|
||||||
if (!last_read)
|
if (!last_read)
|
||||||
goto wait_more_data;
|
goto wait_more_data;
|
||||||
|
|
||||||
|
|
@ -1570,7 +1573,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
|
||||||
else {
|
else {
|
||||||
int mode = tcpchk_rules_type_to_proto_mode(check->tcpcheck->rs->flags);
|
int mode = tcpchk_rules_type_to_proto_mode(check->tcpcheck->rs->flags);
|
||||||
|
|
||||||
mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
|
mux_ops = conn_get_best_mux(conn, IST_NULL, IST_NULL, PROTO_SIDE_BE, mode);
|
||||||
}
|
}
|
||||||
if (mux_ops && conn_install_mux(conn, mux_ops, check->sc, proxy, check->sess) < 0) {
|
if (mux_ops && conn_install_mux(conn, mux_ops, check->sc, proxy, check->sess) < 0) {
|
||||||
TRACE_ERROR("failed to install mux", CHK_EV_TCPCHK_CONN|CHK_EV_TCPCHK_ERR, check);
|
TRACE_ERROR("failed to install mux", CHK_EV_TCPCHK_CONN|CHK_EV_TCPCHK_ERR, check);
|
||||||
|
|
@ -1611,6 +1614,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
|
||||||
case SF_ERR_RESOURCE:
|
case SF_ERR_RESOURCE:
|
||||||
case SF_ERR_INTERNAL:
|
case SF_ERR_INTERNAL:
|
||||||
TRACE_ERROR("report connection error", CHK_EV_TCPCHK_CONN|CHK_EV_TCPCHK_ERR, check, 0, 0, (size_t[]){status});
|
TRACE_ERROR("report connection error", CHK_EV_TCPCHK_CONN|CHK_EV_TCPCHK_ERR, check, 0, 0, (size_t[]){status});
|
||||||
|
/* note: errno is no longer guaranteed here */
|
||||||
chk_report_conn_err(check, errno, 0);
|
chk_report_conn_err(check, errno, 0);
|
||||||
ret = TCPCHK_EVAL_STOP;
|
ret = TCPCHK_EVAL_STOP;
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -2648,6 +2652,7 @@ int tcpcheck_main(struct check *check)
|
||||||
out_end_tcpcheck:
|
out_end_tcpcheck:
|
||||||
if ((conn && conn->flags & CO_FL_ERROR) || sc_ep_test(sc, SE_FL_ERROR)) {
|
if ((conn && conn->flags & CO_FL_ERROR) || sc_ep_test(sc, SE_FL_ERROR)) {
|
||||||
TRACE_ERROR("report connection error", CHK_EV_TCPCHK_EVAL|CHK_EV_TCPCHK_ERR, check);
|
TRACE_ERROR("report connection error", CHK_EV_TCPCHK_EVAL|CHK_EV_TCPCHK_ERR, check);
|
||||||
|
/* note: errno is no longer guaranteed here */
|
||||||
chk_report_conn_err(check, errno, 0);
|
chk_report_conn_err(check, errno, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@
|
||||||
#include <haproxy/quic_frame.h>
|
#include <haproxy/quic_frame.h>
|
||||||
#include <haproxy/quic_tp-t.h>
|
#include <haproxy/quic_tp-t.h>
|
||||||
|
|
||||||
|
/* Default protocol when not running over SSL layer. */
|
||||||
|
#define XPRT_QMUX_DEFAULT_ALPN "h3"
|
||||||
|
|
||||||
struct xprt_qmux_ctx {
|
struct xprt_qmux_ctx {
|
||||||
struct connection *conn;
|
struct connection *conn;
|
||||||
struct wait_event wait_event;
|
struct wait_event wait_event;
|
||||||
|
|
@ -207,7 +210,7 @@ struct task *xprt_qmux_io_cb(struct task *t, void *context, unsigned int state)
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if ((conn->flags & CO_FL_ERROR) ||
|
if ((conn->flags & CO_FL_ERROR) ||
|
||||||
!(conn->flags & (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND))) {
|
!(conn->flags & CO_FL_WAIT_XPRT_L6)) {
|
||||||
/* XPRT should be unsubscribed when transfer done or on error. */
|
/* XPRT should be unsubscribed when transfer done or on error. */
|
||||||
BUG_ON(ctx->wait_event.events);
|
BUG_ON(ctx->wait_event.events);
|
||||||
|
|
||||||
|
|
@ -300,6 +303,11 @@ static int xprt_qmux_init(struct connection *conn, void **xprt_ctx)
|
||||||
ctx->lparams.initial_max_stream_data_bidi_remote = qcm_stream_rx_bufsz();
|
ctx->lparams.initial_max_stream_data_bidi_remote = qcm_stream_rx_bufsz();
|
||||||
ctx->lparams.initial_max_stream_data_uni = qcm_stream_rx_bufsz();
|
ctx->lparams.initial_max_stream_data_uni = qcm_stream_rx_bufsz();
|
||||||
|
|
||||||
|
/* Ensure the connection flags are set. Necessary when current XPRT is
|
||||||
|
* activated without explicit "proto qmux" configuration.
|
||||||
|
*/
|
||||||
|
conn->flags |= (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
|
||||||
|
|
||||||
*xprt_ctx = ctx;
|
*xprt_ctx = ctx;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -327,7 +335,7 @@ static void xprt_qmux_close(struct connection *conn, void *xprt_ctx)
|
||||||
if (ctx->ops_lower && ctx->ops_lower->close)
|
if (ctx->ops_lower && ctx->ops_lower->close)
|
||||||
ctx->ops_lower->close(conn, ctx->ctx_lower);
|
ctx->ops_lower->close(conn, ctx->ctx_lower);
|
||||||
|
|
||||||
conn->flags &= ~(CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
|
conn->flags &= ~CO_FL_WAIT_XPRT_L6;
|
||||||
|
|
||||||
BUG_ON(conn->xprt_ctx != ctx);
|
BUG_ON(conn->xprt_ctx != ctx);
|
||||||
conn->xprt_ctx = ctx->ctx_lower;
|
conn->xprt_ctx = ctx->ctx_lower;
|
||||||
|
|
@ -341,6 +349,14 @@ static int xprt_qmux_get_alpn(const struct connection *conn, void *xprt_ctx,
|
||||||
const char **str, int *len)
|
const char **str, int *len)
|
||||||
{
|
{
|
||||||
struct xprt_qmux_ctx *ctx = xprt_ctx;
|
struct xprt_qmux_ctx *ctx = xprt_ctx;
|
||||||
|
|
||||||
|
/* Return a the default ALPN if lower layer is not able to negotiate it. */
|
||||||
|
if (!ctx->ops_lower || !ctx->ops_lower->get_alpn) {
|
||||||
|
*str = XPRT_QMUX_DEFAULT_ALPN;
|
||||||
|
*len = strlen(XPRT_QMUX_DEFAULT_ALPN);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return ctx->ops_lower->get_alpn(conn, ctx->ctx_lower, str, len);
|
return ctx->ops_lower->get_alpn(conn, ctx->ctx_lower, str, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue