Compare commits

..

No commits in common. "master" and "v3.4-dev12" have entirely different histories.

110 changed files with 598 additions and 2159 deletions

163
CHANGELOG
View file

@ -1,169 +1,6 @@
ChangeLog : ChangeLog :
=========== ===========
2026/05/26 : 3.4-dev14
- MINOR: config: shm-stats-file is no longer experimental
- BUILD: proxy: unstatify the proxies_del_lock to avoid a warning without threads
- BUG/MEDIUM: net_helper: fix a remaining possibly infinite loop in converters
- MINOR: ssl_sock: remove unneeded check on QMux flags
- MINOR: connection: define xprt_add_l6hs()
- MINOR: xprt_qmux: define default value for get_alpn
- MINOR: connection: define mask CO_FL_WAIT_XPRT_L6
- MINOR: session: support QMux in clear on FE side
- MINOR: backend: support QMux in clear for BE side
- BUG/MINOR: ocsp: Manage date too far away in the future
- MINOR: mux_quic: handle STOP_SENDING in QMux
- MINOR: mux_quic: handle MAX_STREAMS for uni stream in QMux
- MINOR: mux_quic: do not crash on unhandled QMux frame reception
- BUG/MEDIUM: applet: Properly handle receives of size 0
- BUG/MEDIUM: resolvers: Fix test on dn label size in resolv_dn_label_to_str()
- BUG/MEDIUM: ssl-gencert: Unlock LRU cache if failing to generate certificate
- BUG/MINOR: quic: fix ODCID lookup from derived value
- BUG/MEDIUM: dict: hold lock while decrementing refcount in dict_entry_unref
- BUG/MINOR: tcpchecks: Limit parsing of agent-check reply to the buffer
- BUG/MEDIUM: hlua: Fix integer underflow when receiving line from lua cosocket
- BUG/MEDIUM: cli: Fix parsing of pattern finishing a command payload
- BUG/MEDIUM: acme: NUL terminate response buffer before PEM parsing
- BUILD: intops: mask the fail value in array_size_or_fail()
- BUG/MEDIUM: log-forward: make sure the month is unsigned
- BUG/MEDIUM: regex: allocate a large enough pcre2 match for all matches
- BUG/MEDIUM: tcpcheck/spoe: bound the SPOP error code to valid values
- BUG/MEDIUM: cache: fix a refcount leak for missed secondary entries
- BUG/MINOR: log: free logformat expr on compile failure in cfg_parse_log_profile
- BUG/MINOR: resolvers: fix room for trailing zero in resolv_dn_label_to_str()
- BUG/MINOR: resolvers: fix risk of appending garbage past the domain name
- BUG/MINOR: mux-h2: validate HEADERS frame length before reading stream dep
- BUG/MINOR: log: look for the end of priority before the end of the buffer
- BUG/MINOR: dict: fix refcount race on insert collision
- BUG/MINOR: init: use more than ha_random64() for the cluster secret
- BUG/MINOR: sample: limit the be2hex converter's chunk size
- CLEANUP: resolvers: use read_n32() instead of open-coded big-endian read
- CLEANUP: resolvers: remove pool_free(NULL) in SRV additional record matching
- CLEANUP: resolvers: fix comment typos and wrong filenames in file headers
- BUG/MINOR: haterm: fix the random suffix multiplication
- MINOR: haterm: enable h3 for TCP bindings
- MINOR: haterm: do not emit a warning when not using SSL
- BUG/MEDIUM: h1: drop headers whose names contain invalid chars
- BUG/MEDIUM: h1: limit status codes to 3 digits by default
- BUG/MEDIUM: cache: always verify the primary hash in get_secondary_entry()
- BUG/MINOR: cache: also recognize directives in the form "token="
- BUG/MINOR: resolvers: relax size checks in authority record parsing
- BUG/MINOR: sample: request an extra output byte for the url_dec converter
- BUG/MINOR: http-fetch: check against the whole token in get_http_auth()
- BUG/MEDIUM: acme: protect against risk of null-deref on connection failure
- BUG/MINOR: http-ext: always check remaining data when reading rfc7239 nodeport
- BUG/MINOR: base64: return empty string for empty input in base64dec()
- BUG/MINOR: payload: fix the handshake length bounds check smp_client_hello_parse()
- BUG/MINOR: ssl-hello: make use of the null-terminated servername
- BUG/MINOR: resolvers: switch to a better PRNG for query IDs
- BUG/MINOR: addons/51d: NUL-terminate headers before passing them to Trie API
- BUG/MEDIUM: tools: insert an XXH64 layer on the PRNG output
- MINOR: tools: provide a function to generate a hashed random pair
- MEDIUM: init: fall back to ha_random64_pair_hashed() for the cluster secret
- MEDIUM: tools: use the hashed random pair for UUID generation
- MEDIUM: h1: use ha_random64_pair_hashed() for the WebSocket key
- MEDIUM: quic: use ha_random64_pair_hashed() to generate the QUIC retry tokens
- MEDIUM: tools: switch the main PRNG to a thread-local xoshiro256**
- BUG/MEDIUM: h3: reject client push stream
- BUG/MINOR: h3: reject server push stream
- BUG/MINOR: h3: reject client CANCEL_PUSH frame
- BUG/MINOR: h3: adjust error on PUSH_PROMISE frame reception
- BUG/MINOR: h3: reject server MAX_PUSH_ID frame
- BUG/MEDIUM: auth: fix unconfigured password NULL deref
- BUG/MINOR: h3: add missing break on rcv_buf()
- BUG/MINOR: hlua: prevent Lua from passing CR/LF/NUL in HTTP headers
- BUG/MINOR: qmux: do not crash on frame parsing issue
- BUG/MINOR: quic: reject packet too short for HP decryption
- BUG/MINOR: jwe: enforce GCM tag length to 128 bits
- BUG/MEDIUM: jwe: substitute random CEK on RSA1_5 decryption failure per RFC 7516 #11.5
- BUG/MEDIUM: mux-fcgi: reject stream ID 0 for application records
- MINOR: http: Add function to remove all occurrences of a value in a header
- MINOR: h1: Add a H1M flag to specify a non-empty 'Upgrade:' header was parsed
- BUG/MEDIUM: h1-htx: Sanitize parsing to properly handle upgrade requests
- BUG/MINOR: mux-fcgi: Use relative offset to compute contig data in demux buf
- BUG/MINOR: mux-spop: Use relative offset to compute contig data in demux buf
- CLEANUP: mux-fcgi/mux-spop: Remove copy/pasted comment about slow realign
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

View file

@ -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 16 tested). Older versions are no longer supported due to - GCC >= 4.7 (up to 15 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 21 were successfully tested. Recent versions may emit a bit versions up to 19 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 4.0. It is recommended to use 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
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

View file

@ -44,7 +44,6 @@
# 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.
@ -344,7 +343,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_TRACE \ USE_ECH \
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 \
@ -367,9 +366,6 @@ $(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=),)

View file

@ -1,2 +1,2 @@
$Format:%ci$ $Format:%ci$
2026/05/26 2026/05/13

View file

@ -1 +1 @@
3.4-dev14 3.4-dev12

View file

@ -303,7 +303,6 @@ static void _51d_init_device_offsets(fiftyoneDegreesDeviceOffsets *offsets) {
static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets) static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets)
{ {
struct buffer *temp = get_trash_chunk();
struct channel *chn; struct channel *chn;
struct htx *htx; struct htx *htx;
struct http_hdr_ctx ctx; struct http_hdr_ctx ctx;
@ -325,15 +324,7 @@ static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOff
if (http_find_header(htx, name, &ctx, 1)) { if (http_find_header(htx, name, &ctx, 1)) {
(offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i); (offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i);
/* Copy value into trash and NUL-terminate before passing to the (offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, ctx.value.ptr);
* 51Degrees Trie API, which expects a C string.
*/
if (ctx.value.len >= temp->size)
continue;
memcpy(temp->area, ctx.value.ptr, ctx.value.len);
temp->area[ctx.value.len] = '\0';
temp->data = ctx.value.len + 1;
(offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, temp->area);
offsets->size++; offsets->size++;
} }
} }

View file

@ -3,7 +3,7 @@
Configuration Manual Configuration Manual
---------------------- ----------------------
version 3.4 version 3.4
2026/05/26 2026/05/13
This document covers the configuration language as implemented in the version This document covers the configuration language as implemented in the version
@ -2126,28 +2126,13 @@ 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> | auto } chroot <jail dir>
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. It is important to ensure that <jail dir> attacker to exploit the system. This only works when the process is started
is both empty and non-writable to anyone. When the process is started with with superuser privileges. It is important to ensure that <jail_dir> is both
superuser privileges, the chroot() is performed directly. On Linux, when empty and non-writable to anyone.
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
@ -3329,7 +3314,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> shm-stats-file <name> [ EXPERIMENTAL ]
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
@ -3345,7 +3330,7 @@ shm-stats-file <name>
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> shm-stats-file-max-objects <number> [ EXPERIMENTAL ]
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
@ -5321,26 +5306,17 @@ 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>
On frontend side, this is used as the value for the advertised Sets the QUIC initial_max_streams_bidi transport parameter either on frontend
initial_max_streams_bidi transport parameter. This is enforced as the maximum or backend side. This is the maximum number of bidirectional streams that the
number of bidirectional streams that the remote peer will be authorized to remote peer will be authorized to open concurrently during the connection
open concurrently during the connection lifetime. This effectively limits the lifetime. On frontend side, this limits the number of concurrent HTTP/3
number of concurrent HTTP/3 client requests. 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"
@ -5743,16 +5719,6 @@ 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.
@ -8294,10 +8260,7 @@ 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. The operation will and let haproxy use the result directly.
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 :
@ -9962,7 +9925,7 @@ no option accept-unsafe-violations-in-http-request
When this option is set, the following rules are observed: When this option is set, the following rules are observed:
* In H1 only, invalid characters, including NULL character, in header name * In H1 only, invalid characters, including NULL character, in header name
will not be rejected; however the header will be dropped. will be accepted;
* In H1 only, NULL character in header value will be accepted; * In H1 only, NULL character in header value will be accepted;
@ -10027,11 +9990,8 @@ no option accept-unsafe-violations-in-http-response
When this option is set, the following rules are observed: When this option is set, the following rules are observed:
* In H1 only, status codes longer than 3 digits but whose value fits in 16
bits are not rejected.
* In H1 only, invalid characters, including NULL character, in header name * In H1 only, invalid characters, including NULL character, in header name
will not be rejected; however the header will be dropped. will be accepted;
* In H1 only, NULL character in header value will be accepted; * In H1 only, NULL character in header value will be accepted;
@ -18887,21 +18847,6 @@ 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".
@ -18931,13 +18876,9 @@ 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 a 32-bit positive number Set a persistent ID for the server. This ID must be positive and unique for
and unique for the proxy. An unused ID will automatically be assigned if the proxy. An unused ID will automatically be assigned if unset. The first
unset. The first assigned value will be 1. This ID is currently only returned assigned value will be 1. This ID is currently only returned in statistics.
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
@ -19031,7 +18972,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 | none } init-state { fully-up | up | down | fully-down }
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
@ -19039,25 +18980,20 @@ init-state { fully-up | up | down | fully-down | none }
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, if health checks are enabled for this server, it will be turned to and can turn to the DOWN state when ALL health checks fail.
the DOWN state when ALL health checks fail. - when set to 'up' (the default), the server is considered immediately
- when set to 'up', the server is considered immediately available and, if available and will initiate a health check that can turn it to the DOWN
health checks are enabled for this server, it will be turned to the DOWN state immediately if it fails.
state immediately if the next health check fails. - when set to 'down', the server initially is considered unavailable and
- when set to 'down', the server initially is considered unavailable and, will initiate a health check that can turn it to the UP state immediately
if health checks are enabled for this server, it can be turned to the UP if it succeeds.
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, if health checks are enabled for this server, it will turned to the and can turn to the UP state when ALL health checks succeed.
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. This directive cannot be used when the server is tracking another one. etc.
Examples: Examples:
# pass client traffic ONLY to Redis "master" node # pass client traffic ONLY to Redis "master" node
@ -20253,11 +20189,7 @@ 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>. As mentioned above, Creates a new name server list labeled <resolvers id>
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:

View file

@ -1,229 +0,0 @@
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.

View file

@ -179,8 +179,6 @@ 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 */
@ -675,12 +673,11 @@ struct connection {
}; };
struct mux_proto_list { struct mux_proto_list {
const struct ist mux_proto; /* Mux protocol, to be used with the "proto" directive */ const struct ist token; /* token name and length. Empty is catch-all */
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;
}; };

View file

@ -86,10 +86,7 @@ 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);
@ -114,7 +111,6 @@ 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);
@ -653,7 +649,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->mux_proto)) if (isteq(proto, item->token))
return item; return item;
} }
return NULL; return NULL;
@ -672,7 +668,6 @@ 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;
@ -681,14 +676,10 @@ 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->mux_proto)) { if (istlen(mux_proto) && isteq(mux_proto, item->token)) {
return item; return item;
} }
else if (istlen(alpn) && item->alpn && else if (!istlen(item->token)) {
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;
} }
@ -705,12 +696,11 @@ 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, alpn, proto_side, proto_is_quic(conn->ctrl), proto_mode); item = conn_get_best_mux_entry(mux_proto, proto_side, proto_is_quic(conn->ctrl), proto_mode);
return item ? item->mux : NULL; return item ? item->mux : NULL;
} }

View file

@ -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_HDR 0x00040000 // non-empty Upgrapde header found #define H1_MF_UPG_H2C 0x00040000 // "h2c" or "h2" used as upgrade token
#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);

View file

@ -326,50 +326,6 @@ static inline int is_immutable_header(struct ist hdr)
} }
} }
/* This function parses comma-separated values from <hv> and rewrite it in place,
* skip all occurrences of <value>. It is the caller responsibility to deal with
* empty header value.
*/
static inline void http_remove_header_value(struct ist *hv, struct ist value)
{
char *e, *n, *p;
struct ist word;
word.ptr = hv->ptr - 1; // -1 for next loop's pre-increment
p = hv->ptr;
e = hv->ptr + hv->len;
hv->len = 0;
while (++word.ptr < e) {
/* skip leading delimiter and blanks */
if (HTTP_IS_LWS(*word.ptr))
continue;
n = http_find_hdr_value_end(word.ptr, e); // next comma or end of line
word.len = n - word.ptr;
/* trim trailing blanks */
while (word.len && HTTP_IS_LWS(word.ptr[word.len-1]))
word.len--;
if (isteqi(word, value))
goto skip_val;
if (hv->ptr + hv->len == p) {
/* no rewrite done till now */
hv->len = n - hv->ptr;
}
else {
if (hv->len)
hv->ptr[hv->len++] = ',';
istcat(hv, word, e - hv->ptr);
}
skip_val:
word.ptr = p = n;
}
}
#endif /* _HAPROXY_HTTP_H */ #endif /* _HAPROXY_HTTP_H */
/* /*

View file

@ -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; /* HTTP client appctx */ struct appctx *appctx; /* HTTPclient 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 use an absolute URI */ #define HTTPCLIENT_O_HTTPPROXY 0x00000001 /* the request must be 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_HTTPCLIENT_T_H */ #endif /* ! _HAPROXY_HTTCLIENT__T_H */

View file

@ -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_HTTPCLIENT_H */ #endif /* ! _HAPROXY_HTTCLIENT_H */

View file

@ -141,7 +141,6 @@
#define HTX_SL_F_CONN_UPG 0x00001000 /* The message contains "connection: upgrade" header */ #define HTX_SL_F_CONN_UPG 0x00001000 /* The message contains "connection: upgrade" header */
#define HTX_SL_F_BODYLESS_RESP 0x00002000 /* The response to this message is bodyless (only for request) */ #define HTX_SL_F_BODYLESS_RESP 0x00002000 /* The response to this message is bodyless (only for request) */
#define HTX_SL_F_NOT_HTTP 0x00004000 /* Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted) */ #define HTX_SL_F_NOT_HTTP 0x00004000 /* Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted) */
#define HTX_SL_F_UPG_HDR 0x00008000 /* non-empty Upgrapde header found */
/* This function is used to report flags in debugging tools. Please reflect /* This function is used to report flags in debugging tools. Please reflect
* below any single-bit flag addition above in the same order via the * below any single-bit flag addition above in the same order via the
@ -158,8 +157,7 @@ static forceinline char *hsl_show_flags(char *buf, size_t len, const char *delim
_(HTX_SL_F_CLEN, _(HTX_SL_F_CHNK, _(HTX_SL_F_VER_11, _(HTX_SL_F_CLEN, _(HTX_SL_F_CHNK, _(HTX_SL_F_VER_11,
_(HTX_SL_F_BODYLESS, _(HTX_SL_F_HAS_SCHM, _(HTX_SL_F_SCHM_HTTP, _(HTX_SL_F_BODYLESS, _(HTX_SL_F_HAS_SCHM, _(HTX_SL_F_SCHM_HTTP,
_(HTX_SL_F_SCHM_HTTPS, _(HTX_SL_F_HAS_AUTHORITY, _(HTX_SL_F_SCHM_HTTPS, _(HTX_SL_F_HAS_AUTHORITY,
_(HTX_SL_F_NORMALIZED_URI, _(HTX_SL_F_CONN_UPG, _(HTX_SL_F_BODYLESS_RESP, _(HTX_SL_F_NORMALIZED_URI, _(HTX_SL_F_CONN_UPG)))))))))))));
_(HTX_SL_F_NOT_HTTP, _(HTX_SL_F_UPG_HDR))))))))))))))));
/* epilogue */ /* epilogue */
_(~0U); _(~0U);
return buf; return buf;

View file

@ -65,9 +65,7 @@ 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 0x00000004 /* Only Transfer header blocks (start-line, header and EOH) */ #define HTX_XFER_HDRS_ONLY 0x00000003 /* 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

View file

@ -76,56 +76,6 @@ 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)
{ {

View file

@ -53,7 +53,6 @@ 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 */

View file

@ -10,7 +10,6 @@
#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>
@ -129,9 +128,6 @@ 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);

View file

@ -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_backend(struct appctx *appctx, const char *arg); struct proxy *cli_find_frontend(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);

View file

@ -1,5 +1,5 @@
/* /*
* include/haproxy/resolvers-t.h * include/haproxy/dns-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 */
uint32_t ttl; /* response TTL */ int32_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 matched and an IP * OR provided IP found and preference is not match 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 */

View file

@ -1,5 +1,5 @@
/* /*
* include/haproxy/resolvers.h * include/haproxy/dns.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>

View file

@ -111,8 +111,7 @@ enum srv_initaddr {
* at start up time. * at start up time.
*/ */
enum srv_init_state { enum srv_init_state {
SRV_INIT_STATE_NONE = 0, 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_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. */
@ -249,9 +248,7 @@ 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, 28 LSB used */ SRV_HASH_KEY_ID = 0, /* derived from server puid */
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 */
}; };
@ -329,7 +326,6 @@ 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

View file

@ -278,35 +278,6 @@ 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)
{ {

View file

@ -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;
unsigned long expire; 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) */

View file

@ -188,12 +188,4 @@ struct file_name_node {
char name[VAR_ARRAY]; /* storage, used with cebus_*() */ char name[VAR_ARRAY]; /* storage, used with cebus_*() */
}; };
/* a pair of uint64_t. It's purposely arranged in little endian to help
* being vectorized on modern processors.
*/
struct uint64_pair {
uint64_t l;
uint64_t h;
};
#endif /* _HAPROXY_TOOLS_T_H */ #endif /* _HAPROXY_TOOLS_T_H */

View file

@ -1288,27 +1288,11 @@ static inline void _ha_aligned_free(void *ptr)
int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz); int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz);
/* PRNG */ /* PRNG */
struct uint64_pair _ha_random64_pair_hashed(void);
void ha_generate_uuid_v4(struct buffer *output); void ha_generate_uuid_v4(struct buffer *output);
void ha_generate_uuid_v7(struct buffer *output); void ha_generate_uuid_v7(struct buffer *output);
void ha_random_seed(const unsigned char *seed, size_t len); void ha_random_seed(const unsigned char *seed, size_t len);
void ha_random_seed_thread(void); void ha_random_jump96(uint32_t dist);
void ha_random_jump128(uint32_t dist);
void ha_random_jump192(uint32_t dist);
uint64_t ha_random64(void); uint64_t ha_random64(void);
uint64_t ha_random64_internal(void);
/* Returns a pair of uint64_t randoms hashed so as not to disclose the internal
* PRNG state.
*/
static inline void ha_random64_pair_hashed(uint64_t *l, uint64_t *h)
{
struct uint64_pair ret = _ha_random64_pair_hashed();
*l = ret.l;
*h = ret.h;
}
static inline uint32_t ha_random32() static inline uint32_t ha_random32()
{ {

View file

@ -34,8 +34,6 @@
#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.
*/ */
@ -141,23 +139,8 @@
&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 (USE_TRACE) && (defined(DEBUG_DEV) || defined(DEBUG_FULL)) #if 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)

View file

@ -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: \"ECONNREFUSED returned by OS.*\".+check duration: [[:digit:]]+ms.+status: 0/1 DOWN." 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."
} -start } -start
server s1 { server s1 {

View file

@ -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.*ECONNREFUSED returned by OS.* at step 2 of tcp-check.*connect port 1" expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv1 failed.*Connection refused 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.*ECONNREFUSED returned by OS.* at step 1 of tcp-check.*connect port 1" expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv1 failed.*Connection refused at step 1 of tcp-check.*connect port 1"
} -start } -start
server s1 { server s1 {

View file

@ -146,7 +146,7 @@ client c1 -connect ${hap_fe1_sock} {
} -run } -run
# missing websocket key # missing websocket key
client c2_1 -connect ${hap_fe1_sock} { client c2 -connect ${hap_fe1_sock} {
txreq \ txreq \
-req "GET" \ -req "GET" \
-url "/" \ -url "/" \
@ -158,19 +158,6 @@ client c2_1 -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 \

View file

@ -50,10 +50,10 @@ client c1 -connect ${h1_feS_sock} {
haproxy h1 -cli { haproxy h1 -cli {
# non existent backend # non existent backend
send "add backend be from def" send "experimental-mode on; add backend be from def"
expect ~ "Mode is required" expect ~ "Mode is required"
send "add backend be from def_http" send "experimental-mode on; 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}"

View file

@ -40,29 +40,29 @@ haproxy h1 -conf {
} -start } -start
haproxy h1 -cli { haproxy h1 -cli {
send "del backend other" send "experimental-mode on; del backend other"
expect ~ "No such backend." expect ~ "No such backend."
send "del backend li" send "experimental-mode on; del backend li"
expect ~ "Cannot delete a listen section." expect ~ "Cannot delete a listen section."
send "del backend be_ref" send "experimental-mode on; 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 "del backend be" send "experimental-mode on; 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 "del backend be" send "experimental-mode on; 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 "del backend be" send "experimental-mode on; 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 "del backend be_unnamed_def_ref" send "experimental-mode on; 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 "del backend be_unnamed_def_ref2" send "experimental-mode on; del backend be_unnamed_def_ref2"
expect ~ "Backend deleted." expect ~ "Backend deleted."
} }

View file

@ -1,12 +1,10 @@
#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)'"
# QUIC backend are not supported with USE_QUIC_OPENSSL_COMPAT feature cmd "$HAPROXY_PROGRAM -cc 'feature(QUIC)'"
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
# #

View file

@ -6,10 +6,6 @@ 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"

View file

@ -1532,7 +1532,7 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);
@ -1562,16 +1562,6 @@ 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;
@ -1745,7 +1735,7 @@ int acme_res_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);
@ -1846,7 +1836,7 @@ enum acme_ret acme_res_challenge(struct task *task, struct acme_ctx *ctx, struct
TRACE_DATA(__FUNCTION__, ACME_EV_RES, ctx, NULL, &hc->res.buf); TRACE_DATA(__FUNCTION__, ACME_EV_RES, ctx, NULL, &hc->res.buf);
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);
@ -1973,7 +1963,7 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);
@ -2289,7 +2279,7 @@ int acme_res_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);
@ -2468,7 +2458,7 @@ int acme_res_account(struct task *task, struct acme_ctx *ctx, int newaccount, ch
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Location"))) { if (isteqi(hdr->n, ist("Location"))) {
istfree(&ctx->kid); istfree(&ctx->kid);
ctx->kid = istdup(hdr->v); ctx->kid = istdup(hdr->v);
@ -2535,7 +2525,7 @@ int acme_nonce(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs; hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) { for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) { if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce); istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v); ctx->nonce = istdup(hdr->v);

View file

@ -539,6 +539,9 @@ 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;
@ -547,8 +550,7 @@ 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);
if (count) ret = CALL_APPLET_WITH_RET(appctx->applet, rcv_buf(appctx, buf, count, flags));
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);
@ -606,7 +608,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_NO_METADATA); htx_xfer(appctx_htx, buf_htx, count, HTX_XFER_DEFAULT);
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);
} }

View file

@ -297,7 +297,7 @@ check_user(struct userlist *ul, const char *user, const char *pass)
fprintf(stderr, ", crypt=%s\n", ((ep) ? ep : "")); fprintf(stderr, ", crypt=%s\n", ((ep) ? ep : ""));
#endif #endif
if (ep && u->pass && strcmp(ep, u->pass) == 0) if (ep && strcmp(ep, u->pass) == 0)
return 1; return 1;
else else
return 0; return 0;

View file

@ -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. It MUST be in string format */ /* use key as a hash */
{ {
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(*end))) { if (unlikely(!HTTP_IS_TOKEN(*p))) {
/* 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(*end))) /* eol, uncertain uri len */ if (likely(HTTP_IS_LWS(*p))) /* 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,14 +545,7 @@ 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;
/* Note that if the hash-type doesn't hash the key, we must provide it smp = sample_fetch_as_type(px, s->sess, s, SMP_OPT_DIR_REQ | SMP_OPT_FINAL, px->lbprm.expr, SMP_T_BIN);
* 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;
@ -1818,10 +1811,7 @@ 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 ?
@ -1843,17 +1833,6 @@ 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;
@ -1865,7 +1844,20 @@ 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)
@ -2087,7 +2079,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.srv_hash != hash || srv->path_params.nego_alpn[0] == 0) if (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);
} }
@ -2138,11 +2130,9 @@ int connect_server(struct stream *s)
srv_conn->flags |= CO_FL_SOCKS4; srv_conn->flags |= CO_FL_SOCKS4;
} }
if (may_start_mux_now) { if (srv && srv->mux_proto && isteq(srv->mux_proto->token, ist("qmux"))) {
/* Delay MUX init if an XPRT handshake is required prior. */ srv_conn->flags |= (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
mux_proto = conn_select_mux_be(srv_conn); may_start_mux_now = 0;
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)
@ -2252,13 +2242,6 @@ 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.

View file

@ -125,9 +125,6 @@ int base64dec(const char *in, size_t ilen, char *out, size_t olen) {
signed char b; signed char b;
int convlen = 0, i = 0, pad = 0; int convlen = 0, i = 0, pad = 0;
if (!ilen)
return 0;
if (ilen % 4) if (ilen % 4)
return -1; return -1;

View file

@ -374,7 +374,7 @@ static int secondary_key_cmp(const char *ref_key, const char *new_key)
* delete_expired==0, write otherwise. * delete_expired==0, write otherwise.
*/ */
struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_entry *entry, struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_entry *entry,
const char *primary_hash, const char *secondary_key, int delete_expired) const char *secondary_key, int delete_expired)
{ {
struct eb32_node *node = &entry->eb; struct eb32_node *node = &entry->eb;
@ -395,12 +395,6 @@ struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_e
entry = node ? eb32_entry(node, struct cache_entry, eb) : NULL; entry = node ? eb32_entry(node, struct cache_entry, eb) : NULL;
} }
/* Now verify the full primary hash matches: eb32 only compares 32 bits so
* we could have ended up on a different, unrelated entry.
*/
if (entry && primary_hash && memcmp(entry->hash, primary_hash, sizeof(entry->hash)))
entry = NULL;
/* Expired entry */ /* Expired entry */
if (entry && entry->expire <= date.tv_sec) { if (entry && entry->expire <= date.tv_sec) {
if (delete_expired) { if (delete_expired) {
@ -1309,7 +1303,7 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
if (old) { if (old) {
if (vary_signature) if (vary_signature)
old = get_secondary_entry(cache_tree, old, old = get_secondary_entry(cache_tree, old,
txn->cache_hash, txn->cache_secondary_hash, 1); txn->cache_secondary_hash, 1);
if (old) { if (old) {
if (!old->complete) { if (!old->complete) {
/* An entry with the same primary key is already being /* An entry with the same primary key is already being
@ -2184,20 +2178,9 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p
if (!http_request_build_secondary_key(s, res->secondary_key_signature)) { if (!http_request_build_secondary_key(s, res->secondary_key_signature)) {
cache_rdlock(cache_tree); cache_rdlock(cache_tree);
sec_entry = get_secondary_entry(cache_tree, res, sec_entry = get_secondary_entry(cache_tree, res,
s->txn.http->cache_hash,
s->txn.http->cache_secondary_hash, s->txn.http->cache_secondary_hash,
0); 0);
if (!sec_entry) { if (sec_entry && sec_entry != res) {
/* 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);

View file

@ -1629,6 +1629,11 @@ 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;
@ -1639,6 +1644,7 @@ 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;
} }
@ -1647,6 +1653,11 @@ 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;
@ -1657,6 +1668,7 @@ 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;
} }

View file

@ -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(array_size_or_fail(TLS_TICKETS_NO, sizeof(union tls_sess_key))); keys_ref->tlskeys = malloc(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;

View file

@ -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(array_size_or_fail(sizeof(*thisline), linesize))) == NULL) { if ((thisline = malloc(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, array_size_or_fail(sizeof(*thisline), newlinesize)); newline = realloc(thisline, 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);

View file

@ -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});

View file

@ -1151,13 +1151,8 @@ 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, if any, and replace it by a \0 /* end of payload was reached, rewind before the previous \n and replace it by a \0 */
* Otherwise, the payload is empty, just b_sub(buf, strlen(appctx->cli_ctx.payload_pat) + 2);
*/
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;
} }
@ -2548,7 +2543,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(array_size_or_fail(MAX_SEND_FD, (1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof(int)))); tmpbuf = malloc(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;

View file

@ -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_WAIT_XPRT_L6))) { if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_QMUX_RECV|CO_FL_QMUX_SEND))) {
ret = conn_create_mux(conn, NULL); ret = conn_create_mux(conn, NULL);
if (ret < 0) if (ret < 0)
goto done; goto done;
@ -282,7 +282,6 @@ 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;
@ -290,9 +289,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);
alpn = ist2(alpn_str, alpn_len); mux_proto = ist2(alpn_str, alpn_len);
} }
new_mux = conn_get_best_mux(conn, mux_proto, alpn, PROTO_SIDE_FE, mode); new_mux = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_FE, mode);
old_mux = conn->mux; old_mux = conn->mux;
/* No mux found */ /* No mux found */
@ -319,29 +318,6 @@ 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.
@ -354,14 +330,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 alpn; struct ist mux_proto;
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);
alpn = ist2(alpn_str, alpn_len); mux_proto = ist2(alpn_str, alpn_len);
mux_ops = conn_get_best_mux(conn, IST_NULL, alpn, PROTO_SIDE_FE, mode); mux_ops = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_FE, mode);
if (!mux_ops) if (!mux_ops)
return -1; return -1;
} }
@ -377,66 +353,6 @@ 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>,
@ -464,20 +380,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 alpn; struct ist mux_proto;
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.srv_hash == conn->hash_node.key && srv->path_params.nego_alpn[0]) { if (srv && 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);
} }
} }
alpn = ist2(alpn_str, alpn_len); mux_proto = ist2(alpn_str, alpn_len);
mux_ops = conn_get_best_mux(conn, IST_NULL, alpn, PROTO_SIDE_BE, mode); mux_ops = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_BE, mode);
if (!mux_ops) if (!mux_ops)
return -1; return -1;
} }
@ -518,15 +434,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 alpn; struct ist mux_proto;
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);
alpn = ist2(alpn_str, alpn_len); mux_proto = ist2(alpn_str, alpn_len);
mux_ops = conn_get_best_mux(conn, IST_NULL, alpn, PROTO_SIDE_BE, mode); mux_ops = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_BE, mode);
if (!mux_ops) if (!mux_ops)
return -1; return -1;
} }
@ -847,43 +763,6 @@ 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.
@ -2112,7 +1991,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->mux_proto; proto = item->token;
if (item->mode == PROTO_MODE_ANY) if (item->mode == PROTO_MODE_ANY)
mode = "TCP|HTTP"; mode = "TCP|HTTP";

View file

@ -1520,8 +1520,7 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
div = ha_cpu_policy[policy].arg; div = ha_cpu_policy[policy].arg;
div = div ? div : 1; div = div ? div : 1;
while (global.nbtgroups < MAX_TGROUPS && (global.nbthread < MAX_THREADS) && while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS) {
(global.thread_limit == 0 || global.nbthread < global.thread_limit)) {
ha_cpuset_zero(&node_cpu_set); ha_cpuset_zero(&node_cpu_set);
ha_cpuset_zero(&touse_tsid); ha_cpuset_zero(&touse_tsid);
ha_cpuset_zero(&touse_ccx); ha_cpuset_zero(&touse_ccx);
@ -1551,10 +1550,6 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id); ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE)) } else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
thr_count++; thr_count++;
if (global.thread_limit != 0 &&
thr_count + global.nbthread >= global.thread_limit)
break;
} }
/* now cid = next cluster_id or -1 if none; cpu_count is the /* now cid = next cluster_id or -1 if none; cpu_count is the
@ -1613,8 +1608,7 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
div = ha_cpu_policy[policy].arg; div = ha_cpu_policy[policy].arg;
div = div ? div : 1; div = div ? div : 1;
while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS && while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS) {
(global.thread_limit == 0 || global.nbthread < global.thread_limit)) {
ha_cpuset_zero(&node_cpu_set); ha_cpuset_zero(&node_cpu_set);
ha_cpuset_zero(&touse_tsid); ha_cpuset_zero(&touse_tsid);
ha_cpuset_zero(&touse_ccx); ha_cpuset_zero(&touse_ccx);
@ -1644,9 +1638,6 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id); ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE)) } else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
thr_count++; thr_count++;
if (global.thread_limit != 0 &&
global.nbthread + thr_count >= global.thread_limit)
break;
} }
/* now l3id = next L3 ID or -1 if none; cpu_count is the /* now l3id = next L3 ID or -1 if none; cpu_count is the

View file

@ -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, *tree_de; struct dict_entry *de;
struct ebpt_node *n; struct ebpt_node *n;
HA_RWLOCK_RDLOCK(DICT_LOCK, &d->rwlock); HA_RWLOCK_RDLOCK(DICT_LOCK, &d->rwlock);
@ -97,18 +97,13 @@ 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);
tree_de = container_of(n, struct dict_entry, value); HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
if (tree_de == de) if (n != &de->value) {
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;
} }
@ -122,11 +117,10 @@ void dict_entry_unref(struct dict *d, struct dict_entry *de)
if (!de) if (!de)
return; return;
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock); if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0)
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0) {
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
return; return;
}
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
ebpt_delete(&de->value); ebpt_delete(&de->value);
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock); HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);

View file

@ -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,6 +475,7 @@ 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);
@ -489,7 +490,6 @@ 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,7 +913,6 @@ static int dns_session_init(struct appctx *appctx)
return 0; return 0;
error: error:
sockaddr_free(&addr);
return -1; return -1;
} }
@ -1346,8 +1345,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;
} }
@ -1363,7 +1362,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 req task for dns tcp server '%s'.\n", srv->id); ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
goto out; goto out;
} }
@ -1380,7 +1379,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 rsp task for dns tcp server '%s'.\n", srv->id); ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
goto out; goto out;
} }
@ -1390,7 +1389,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 idle task for dns tcp server '%s'.\n", srv->id); ha_alert("memory allocation error initializing the ring for dns tcp server '%s'.\n", srv->id);
goto out; goto out;
} }
@ -1426,7 +1425,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()

View file

@ -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(array_size_or_fail(USER_MESSAGES_BUFSIZE, sizeof(char))); usermsgs_buf.area = malloc(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;
} }

View file

@ -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->mux_proto.ptr : "h1"; break; case PR_MODE_HTTP: svmode = (s->mux_proto) ? s->mux_proto->token.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 */

View file

@ -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->mux_proto, ist("fcgi"))) { if (srv->mux_proto && isteq(srv->mux_proto->token, ist("fcgi"))) {
nb_fcgi_srv++; nb_fcgi_srv++;
if (fcgi_conf) if (fcgi_conf)
continue; continue;

View file

@ -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, array_size_or_fail(global.maxsock, sizeof(*fdtab)))) == NULL) { if ((fdtab = ha_aligned_zalloc(64, 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;
} }

View file

@ -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, the value is skipped. * If "h2c" or "h2" found, set H1_MF_UPG_H2C flag.
*/ */
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, *p; char *e, *n;
struct ist word; struct ist word;
word.ptr = value->ptr - 1; // -1 for next loop's pre-increment h1m->flags &= ~(H1_MF_UPG_WEBSOCKET|H1_MF_UPG_H2C);
p = value->ptr;
e = value->ptr + value->len; word.ptr = value.ptr - 1; // -1 for next loop's pre-increment
value->len = 0; e = istend(value);
while (++word.ptr < e) { while (++word.ptr < e) {
/* skip leading delimiter and blanks */ /* skip leading delimiter and blanks */
@ -301,24 +301,10 @@ 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")))
goto skip_val; h1m->flags |= H1_MF_UPG_H2C;
if (value->ptr + value->len == p) { word.ptr = n;
/* 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;
} }
if (istlen(*value))
h1m->flags |= H1_MF_UPG_HDR;
} }
/* Macros used in the HTTP/1 parser, to check for the expected presence of /* Macros used in the HTTP/1 parser, to check for the expected presence of
@ -713,16 +699,6 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
case H1_MSG_RPCODE: case H1_MSG_RPCODE:
http_msg_rpcode: http_msg_rpcode:
if (likely(HTTP_IS_DIGIT(*ptr))) { if (likely(HTTP_IS_DIGIT(*ptr))) {
if (ptr - sl.st.c.ptr >= 3) {
/* more than 3 digits */
if (h1m->err_pos == -1) /* only capture the error pointer */
h1m->err_pos = ptr - start + skip;
else if (h1m->err_pos < -1 || sl.st.status >= ((uint16_t)~0 - 9) / 10) {
/* strict checks or risk of overflow */
state = H1_MSG_RPCODE;
goto http_msg_invalid;
}
}
sl.st.status = sl.st.status * 10 + *ptr - '0'; sl.st.status = sl.st.status * 10 + *ptr - '0';
EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpcode, http_msg_ood, state, H1_MSG_RPCODE); EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpcode, http_msg_ood, state, H1_MSG_RPCODE);
} }
@ -965,20 +941,6 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
goto http_output_full; goto http_output_full;
} }
/* Skip headers whose names contain forbidden
* chars. When any is detected, h1m->err_pos >= 0,
* so we recheck the name only when an error was
* detected.
*/
if (unlikely(h1m->err_pos >= 0)) {
size_t i = 0;
while (i < n.len && HTTP_IS_TOKEN(n.ptr[i]))
i++;
if (i < n.len)
break;
}
if (isteqi(n, ist("transfer-encoding"))) { if (isteqi(n, ist("transfer-encoding"))) {
ret = h1_parse_xfer_enc_header(h1m, v); ret = h1_parse_xfer_enc_header(h1m, v);
if (ret < 0) { if (ret < 0) {
@ -1021,11 +983,7 @@ 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) {
@ -1275,10 +1233,9 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
void h1_generate_random_ws_input_key(char key_out[25]) void h1_generate_random_ws_input_key(char key_out[25])
{ {
/* generate a random websocket key */ /* generate a random websocket key */
uint64_t rand1, rand2; const uint64_t rand1 = ha_random64(), rand2 = ha_random64();
char key[16]; char key[16];
ha_random64_pair_hashed(&rand1, &rand2);
memcpy(key, &rand1, 8); memcpy(key, &rand1, 8);
memcpy(&key[8], &rand2, 8); memcpy(&key[8], &rand2, 8);
a2base64(key, 16, key_out, 25); a2base64(key, 16, key_out, 25);

View file

@ -162,8 +162,6 @@ static unsigned int h1m_htx_sl_flags(struct h1m *h1m)
} }
if (h1m->flags & H1_MF_CONN_UPG) if (h1m->flags & H1_MF_CONN_UPG)
flags |= HTX_SL_F_CONN_UPG; flags |= HTX_SL_F_CONN_UPG;
if (h1m->flags & H1_MF_UPG_HDR)
flags |= HTX_SL_F_UPG_HDR;
return flags; return flags;
} }
@ -215,33 +213,22 @@ static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx
} }
} }
/* Remove Upgrade header if no 'connection: upgrade' found */ flags |= h1m_htx_sl_flags(h1m);
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_HDR)) == H1_MF_UPG_HDR) {
/* 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; int i;
for (i = 0; hdrs[i].n.len; i++) { for (i = 0; hdrs[i].n.len; i++) {
if (isteqi(hdrs[i].n, ist("upgrade"))) if (isteqi(hdrs[i].n, ist("upgrade")))
hdrs[i].v = IST_NULL; hdrs[i].v = IST_NULL;
} }
h1m->flags &=~ (H1_MF_CONN_UPG|H1_MF_UPG_HDR); h1m->flags &=~ H1_MF_CONN_UPG;
flags &= ~HTX_SL_F_CONN_UPG;
} }
/* Remove 'Upgrade' value from connection header if not Upgrade header found */
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_HDR)) == H1_MF_CONN_UPG) {
int i;
for (i = 0; hdrs[i].n.len; i++) {
if (isteqi(hdrs[i].n, ist("connection"))) {
http_remove_header_value(&hdrs[i].v, ist("upgrade"));
if (!istlen(hdrs[i].v))
hdrs[i].v = IST_NULL;
}
}
h1m->flags &=~ (H1_MF_CONN_UPG|H1_MF_UPG_HDR);
}
flags |= h1m_htx_sl_flags(h1m);
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;

View file

@ -212,33 +212,8 @@ static ssize_t h3_init_uni_stream(struct h3c *h3c, struct qcs *qcs,
break; break;
case H3_UNI_S_T_PUSH: case H3_UNI_S_T_PUSH:
if (!conn_is_back(qcs->qcc->conn)) { /* TODO not supported for the moment */
/* RFC 9114 6.2.2. Push Streams h3s->type = H3S_T_PUSH;
*
* Only servers can push; if a server receives a client-initiated push
* stream, this MUST be treated as a connection error of type
* H3_STREAM_CREATION_ERROR.
*/
TRACE_ERROR("reject push from client", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, H3_ERR_STREAM_CREATION_ERROR, 1,
muxc_tevt_type_proto_err);
qcc_report_glitch(qcs->qcc, 1);
goto err;
}
else {
/* RFC 9114 4.6. Server Push
*
* A client MUST treat receipt of a push stream as a connection
* error of type H3_ID_ERROR when no MAX_PUSH_ID frame has been sent or
* when the stream references a push ID that is greater than the maximum
* push ID.
*/
TRACE_ERROR("reject push from server outside of MAX_PUSH_ID", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, H3_ERR_ID_ERROR, 1,
muxc_tevt_type_proto_err);
qcc_report_glitch(qcs->qcc, 1);
goto err;
}
break; break;
case H3_UNI_S_T_QPACK_DEC: case H3_UNI_S_T_QPACK_DEC:
@ -390,6 +365,7 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
case H3_FT_CANCEL_PUSH: case H3_FT_CANCEL_PUSH:
case H3_FT_GOAWAY: case H3_FT_GOAWAY:
case H3_FT_MAX_PUSH_ID:
/* RFC 9114 7.2.3. CANCEL_PUSH /* RFC 9114 7.2.3. CANCEL_PUSH
* *
* A CANCEL_PUSH frame is sent on the control stream. Receiving a * A CANCEL_PUSH frame is sent on the control stream. Receiving a
@ -436,33 +412,14 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
case H3_FT_PUSH_PROMISE: case H3_FT_PUSH_PROMISE:
/* RFC 9114 7.2.5. PUSH_PROMISE /* RFC 9114 7.2.5. PUSH_PROMISE
*
* If a PUSH_PROMISE frame is received on the control stream, the client
* MUST respond with a connection error of type H3_FRAME_UNEXPECTED.
* *
* A client MUST NOT send a PUSH_PROMISE frame. A server MUST treat the * A client MUST NOT send a PUSH_PROMISE frame. A server MUST treat the
* receipt of a PUSH_PROMISE frame as a connection error of type * receipt of a PUSH_PROMISE frame as a connection error of type
* H3_FRAME_UNEXPECTED. * H3_FRAME_UNEXPECTED.
*/ */
if (h3s->type == H3S_T_CTRL || !conn_is_back(qcs->qcc->conn))
ret = H3_ERR_FRAME_UNEXPECTED;
break;
case H3_FT_MAX_PUSH_ID: /* TODO server-side only. */
/* RFC 9114 7.2.7. MAX_PUSH_ID ret = H3_ERR_FRAME_UNEXPECTED;
*
* The MAX_PUSH_ID frame is always sent on the control stream. Receipt
* of a MAX_PUSH_ID frame on any other stream MUST be treated as a
* connection error of type H3_FRAME_UNEXPECTED.
*
* A server MUST NOT send a MAX_PUSH_ID frame. A client MUST treat the
* receipt of a MAX_PUSH_ID frame as a connection error of type
* H3_FRAME_UNEXPECTED.
*/
if (h3s->type == H3S_T_CTRL || conn_is_back(qcs->qcc->conn))
ret = H3_ERR_FRAME_UNEXPECTED;
else if (!(h3c->flags & H3_CF_SETTINGS_RECV))
ret = H3_ERR_MISSING_SETTINGS;
break; break;
default: default:
@ -1973,25 +1930,6 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
h3s->st_req = H3S_ST_REQ_TRAILERS; h3s->st_req = H3S_ST_REQ_TRAILERS;
} }
break; break;
case H3_FT_CANCEL_PUSH:
if (!conn_is_back(qcs->qcc->conn)) {
/* RFC 9114 7.2.3. CANCEL_PUSH
*
* If a server receives a CANCEL_PUSH frame for a push ID
* that has not yet been mentioned by a PUSH_PROMISE frame, this MUST be
* treated as a connection error of type H3_ID_ERROR.
*/
TRACE_ERROR("reject CANCEL_PUSH from client", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, H3_ERR_ID_ERROR, 1,
muxc_tevt_type_proto_err);
qcc_report_glitch(qcs->qcc, 1);
goto err;
}
else {
/* Not supported */
ret = flen;
}
break;
case H3_FT_GOAWAY: case H3_FT_GOAWAY:
ret = h3_parse_goaway_frm(qcs->qcc->ctx, b, flen); ret = h3_parse_goaway_frm(qcs->qcc->ctx, b, flen);
if (ret < 0) { if (ret < 0) {
@ -2000,6 +1938,12 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
goto err; goto err;
} }
break; break;
case H3_FT_CANCEL_PUSH:
case H3_FT_PUSH_PROMISE:
case H3_FT_MAX_PUSH_ID:
/* Not supported */
ret = flen;
break;
case H3_FT_SETTINGS: case H3_FT_SETTINGS:
ret = h3_parse_settings_frm(qcs->qcc->ctx, b, flen); ret = h3_parse_settings_frm(qcs->qcc->ctx, b, flen);
if (ret < 0) { if (ret < 0) {
@ -2009,25 +1953,6 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
} }
h3c->flags |= H3_CF_SETTINGS_RECV; h3c->flags |= H3_CF_SETTINGS_RECV;
break; break;
case H3_FT_PUSH_PROMISE:
/* h3_check_frame_valid() must reject on server side. */
BUG_ON(!conn_is_back(qcs->qcc->conn));
/* RFC 9114 7.2.5. PUSH_PROMISE
*
* A client MUST treat
* receipt of a PUSH_PROMISE frame that contains a larger push ID than
* the client has advertised as a connection error of H3_ID_ERROR.
*/
ret = H3_ERR_ID_ERROR;
break;
case H3_FT_MAX_PUSH_ID:
/* h3_check_frame_valid() must reject on client side. */
BUG_ON(conn_is_back(qcs->qcc->conn));
/* Not supported. */
ret = flen;
break;
default: default:
/* RFC 9114 Section 9. Extensions to HTTP/3 /* RFC 9114 Section 9. Extensions to HTTP/3
* *

View file

@ -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, array_size_or_fail(nb_oldpids + 1, sizeof(int))); oldpids = realloc(oldpids, (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,30 +1926,20 @@ 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. The function prefers RAND_bytes() if available, otherwise falls * limitations.
* back to ha_random64_pair_hashed().
*/ */
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. */
union { uint64_t rand;
uint64_t by64[2];
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 */
ha_random64_pair_hashed(&rand.by64[0], &rand.by64[1]);
}
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;
} }
@ -3098,7 +3088,6 @@ void *run_thread_poll_loop(void *data)
ha_set_thread(data); ha_set_thread(data);
set_thread_cpu_affinity(); set_thread_cpu_affinity();
clock_set_local_source(); clock_set_local_source();
ha_random_seed_thread();
#ifdef USE_THREAD #ifdef USE_THREAD
ha_thread_info[tid].pth_id = ha_get_pthread_id(tid); ha_thread_info[tid].pth_id = ha_get_pthread_id(tid);
@ -3277,105 +3266,6 @@ 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;
@ -3692,48 +3582,16 @@ 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 && do_chroot(argv[0], global.chroot) != 0) { if (global.chroot != NULL) {
if (nb_oldpids) if (chroot(global.chroot) == -1 || chdir("/") == -1) {
tell_old_pids(SIGTTIN); ha_alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot);
protocol_unbind_all(); if (nb_oldpids)
exit(1); tell_old_pids(SIGTTIN);
protocol_unbind_all();
exit(1);
}
} }
ha_free(&global.chroot); ha_free(&global.chroot);

View file

@ -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.h> #include <haproxy/proxy-t.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)statistical_prng() * result) / 0xFFFFFFFFU; result = ((long long)ha_random64() * result) / ((long long)RAND_MAX + 1);
switch (*arg) { switch (*arg) {
case 's': case 's':
@ -1223,22 +1223,11 @@ 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;
@ -1268,7 +1257,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\n", (ulong)master_pipesize); ha_warning("Splicing in haterm is limited to %lu bytes (too old kernel)\n", (ulong)master_pipesize);
} }
} }
else else

View file

@ -401,7 +401,7 @@ void haproxy_init_args(int argc, char **argv)
/* SSL/TCP binding */ /* SSL/TCP binding */
hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread ssl " hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread ssl "
"alpn h3,h2,http1.1,http1.0" "alpn h2,http1.1,http1.0"
" crt " HATERM_RSA_CERT_NAME " crt " HATERM_RSA_CERT_NAME
" crt " HATERM_ECDSA_CERT_NAME "%s%s\n", " crt " HATERM_ECDSA_CERT_NAME "%s%s\n",
ip, port2, ip, port2,
@ -438,8 +438,6 @@ void haproxy_init_args(int argc, char **argv)
} }
hbuf_appendf(&gbuf, "global\n"); hbuf_appendf(&gbuf, "global\n");
hbuf_appendf(&gbuf, "\ttune.memory.hot-size 3145728\n"); hbuf_appendf(&gbuf, "\ttune.memory.hot-size 3145728\n");
if (has_ssl)
hbuf_appendf(&gbuf, "\texpose-experimental-directives\n");
} }
/* "global" section */ /* "global" section */

View file

@ -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 (len1 && blk1[len1-1] == '\n') { if (blk1[len1-1] == '\n') {
len1--; len1--;
skip_at_end++; skip_at_end++;
if (len1 && blk1[len1-1] == '\r') { if (blk1[len1-1] == '\r') {
len1--; len1--;
skip_at_end++; skip_at_end++;
} }
} }
} }
else { else {
if (len2 && blk2[len2-1] == '\n') { if (blk2[len2-1] == '\n') {
len2--; len2--;
skip_at_end++; skip_at_end++;
if (len2 && blk2[len2-1] == '\r') { if (blk2[len2-1] == '\r') {
len2--; len2--;
skip_at_end++; skip_at_end++;
} }
@ -6709,20 +6709,6 @@ __LJMP static inline int hlua_http_add_hdr(lua_State *L, struct http_msg *msg)
size_t value_len; size_t value_len;
const char *value = MAY_LJMP(luaL_checklstring(L, 3, &value_len)); const char *value = MAY_LJMP(luaL_checklstring(L, 3, &value_len));
struct htx *htx = htxbuf(&msg->chn->buf); struct htx *htx = htxbuf(&msg->chn->buf);
size_t i;
/* Reject header values containing CR/LF/NUL to prevent HTTP header
* injection on HTTP/1 output.
*/
for (i = 0; i < name_len; i++) {
if (name[i] == 0 || name[i] == '\r' || name[i] == '\n')
WILL_LJMP(lua_error(L));
}
for (i = 0; i < value_len; i++) {
if (value[i] == 0 || value[i] == '\r' || value[i] == '\n')
WILL_LJMP(lua_error(L));
}
lua_pushboolean(L, http_add_header(htx, ist2(name, name_len), lua_pushboolean(L, http_add_header(htx, ist2(name, name_len),
ist2(value, value_len), 1)); ist2(value, value_len), 1));

View file

@ -3996,19 +3996,19 @@ void http_check_response_for_cacheability(struct stream *s, struct channel *res)
continue; continue;
} }
/* We might have a no-cache="set-cookie" form. */ if (isteqi(ctx.value, ist("private")) ||
if (isteqi(ctx.value, ist("no-cache=\"set-cookie\""))) { isteqi(ctx.value, ist("no-cache")) ||
txn->flags &= ~TX_CACHE_COOK; isteqi(ctx.value, ist("no-store")) ||
continue;
}
if (isteqi(ctx.value, ist("private")) || istmatchi(ctx.value, ist("private=")) ||
isteqi(ctx.value, ist("no-cache")) || istmatchi(ctx.value, ist("no-cache=")) ||
isteqi(ctx.value, ist("no-store")) || istmatchi(ctx.value, ist("no-store=")) ||
isteqi(ctx.value, ist("s-maxage=0"))) { isteqi(ctx.value, ist("s-maxage=0"))) {
txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK; txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
continue; continue;
} }
/* We might have a no-cache="set-cookie" form. */
if (istmatchi(ctx.value, ist("no-cache=\"set-cookie"))) {
txn->flags &= ~TX_CACHE_COOK;
continue;
}
if (istmatchi(ctx.value, ist("s-maxage"))) { if (istmatchi(ctx.value, ist("s-maxage"))) {
has_freshness_info = 1; has_freshness_info = 1;
has_null_maxage = 0; /* The null max-age is overridden, ignore it */ has_null_maxage = 0; /* The null max-age is overridden, ignore it */

View file

@ -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 pointers to the client must be removed * Once this function is used, all pointer sto the client must be removed
* *
*/ */
void httpclient_stop_and_destroy(struct httpclient *hc) void httpclient_stop_and_destroy(struct httpclient *hc)

View file

@ -263,7 +263,7 @@ static int sample_conv_url_dec(const struct arg *args, struct sample *smp, void
* before decoding. * before decoding.
*/ */
if (smp->flags & SMP_F_CONST || smp->data.u.str.size <= smp->data.u.str.data) { if (smp->flags & SMP_F_CONST || smp->data.u.str.size <= smp->data.u.str.data) {
struct buffer *str = get_trash_chunk_sz(smp->data.u.str.data + 1); struct buffer *str = get_trash_chunk_sz(smp->data.u.str.data);
if (!str) if (!str)
return 0; return 0;

View file

@ -356,7 +356,7 @@ static inline int http_7239_extract_node(struct ist *input, struct forwarded_hea
if (!quoted) if (!quoted)
return 0; /* not supported */ return 0; /* not supported */
*input = istnext(*input); *input = istnext(*input);
if (!istlen(*input) || !http_7239_extract_nodeport(input, nodeport)) if (!http_7239_extract_nodeport(input, nodeport))
return 0; /* invalid nodeport */ return 0; /* invalid nodeport */
out: out:
/* ok */ /* ok */

View file

@ -135,7 +135,7 @@ static int get_http_auth(struct sample *smp, struct htx *htx)
chunk_initlen(&txn->auth.method_data, p, 0, istend(ctx.value) - p); chunk_initlen(&txn->auth.method_data, p, 0, istend(ctx.value) - p);
if (isteqi(ist2(auth_method.area, auth_method.data), ist("Basic"))) { if (!strncasecmp("Basic", auth_method.area, auth_method.data)) {
struct buffer *http_auth = get_trash_chunk(); struct buffer *http_auth = get_trash_chunk();
len = base64dec(txn->auth.method_data.area, len = base64dec(txn->auth.method_data.area,
@ -159,7 +159,7 @@ static int get_http_auth(struct sample *smp, struct htx *htx)
txn->auth.method = HTTP_AUTH_BASIC; txn->auth.method = HTTP_AUTH_BASIC;
return 1; return 1;
} else if (isteqi(ist2(auth_method.area, auth_method.data), ist("Bearer"))) { } else if (!strncasecmp("Bearer", auth_method.area, auth_method.data)) {
txn->auth.method = HTTP_AUTH_BEARER; txn->auth.method = HTTP_AUTH_BEARER;
return 1; return 1;
} }

View file

@ -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 = NULL; struct httpclient *hc;
char *err = NULL; char *err = NULL;
enum http_meth_t meth; enum http_meth_t meth;
char *meth_str; char *meth_str;
@ -159,7 +159,6 @@ 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);
} }

View file

@ -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_sz(n.len + v.len + delta); struct buffer *chunk = alloc_trash_chunk();
void *ptr; void *ptr;
if (!chunk) if (!chunk)
@ -730,20 +730,17 @@ 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 > meta_sz; blk = htx_get_next_blk(src, blk)) { for (blk = htx_get_head_blk(src); blk && count; 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;
@ -758,10 +755,7 @@ 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_free_data_space(dst); max = htx_get_max_blksz(dst, count);
if (max > count - meta_sz)
max = count - meta_sz;
if (!max) if (!max)
break; break;
@ -777,8 +771,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 -= meta_sz + v.len; count -= sizeof(*blk) + v.len;
ret += meta_sz + v.len; ret += sizeof(*blk) + v.len;
if (v.len != sz) { if (v.len != sz) {
dst_full = 1; dst_full = 1;
goto stop; goto stop;
@ -799,8 +793,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 -= meta_sz + sz; count -= sizeof(*blk) + sz;
ret += meta_sz + sz; ret += sizeof(*blk) + sz;
break; break;
} }
@ -832,7 +826,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 -= meta_sz + htx_get_blksz(blk); ret -= sizeof(*blk) + 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);

View file

@ -266,10 +266,8 @@ static int parse_jose(struct buffer *decoded_jose, int *alg, int *enc, struct jo
if (gcm) { if (gcm) {
/* Look for "tag" field (used by aes gcm encryption). /* Look for "tag" field (used by aes gcm encryption) */
* GCMKW tag must be exactly 16 bytes per RFC 7518 */ if (decode_jose_field(decoded_jose, "$.tag", &jose_fields->tag, 1))
if (decode_jose_field(decoded_jose, "$.tag", &jose_fields->tag, 1) ||
b_data(jose_fields->tag) != 16)
goto end; goto end;
/* Look for "iv" field (used by aes gcm encryption) */ /* Look for "iv" field (used by aes gcm encryption) */
@ -450,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((uint64_t)items[JWE_ELT_JOSE].length << 3); uint64_t aad_len = my_htonll(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();
@ -558,12 +556,6 @@ static int decrypt_ciphertext(jwe_enc enc, struct jwt_item items[JWE_ELT_MAX],
(*aead_tag)->data = size; (*aead_tag)->data = size;
if (gcm) { if (gcm) {
/* RFC 7518 mandates a 128-bit (16 byte) authentication tag for A*GCM.
* OpenSSL accepts 1-16 bytes but only verifies that many bytes, so a
* truncated tag reduces forgery work factor to ~256 per byte short. */
if ((*aead_tag)->data != 16)
goto end;
aad = alloc_trash_chunk(); aad = alloc_trash_chunk();
if (!aad) if (!aad)
goto end; goto end;
@ -584,13 +576,8 @@ static int decrypt_ciphertext(jwe_enc enc, struct jwt_item items[JWE_ELT_MAX],
goto end; goto end;
/* Only use the second part of the decrypted key for actual /* Only use the second part of the decrypted key for actual
* content decryption. * content decryption. */
* Because of the RSAES-PKCS1-V1_5 algorithm, we might have a if (b_data(decrypted_cek) != key_size * 2)
* bigger than expected decrypted_cek (if it was filled with
* random bytes in do_decrypt_cek_rsa) and still want to call
* aes_process on the ciphertext in order to avoid timing
* attacks. */
if (b_data(decrypted_cek) < key_size * 2)
goto end; goto end;
chunk_memcpy(aes_key, decrypted_cek->area + key_size, key_size); chunk_memcpy(aes_key, decrypted_cek->area + key_size, key_size);
} }
@ -824,31 +811,8 @@ static int do_decrypt_cek_rsa(struct buffer *cek, struct buffer *decrypted_cek,
} }
if (EVP_PKEY_decrypt(ctx, (unsigned char*)b_orig(decrypted_cek), &outl, if (EVP_PKEY_decrypt(ctx, (unsigned char*)b_orig(decrypted_cek), &outl,
(unsigned char*)b_orig(cek), b_data(cek)) <= 0) { (unsigned char*)b_orig(cek), b_data(cek)) <= 0)
/* Per RFC 7516 #11.5, on RSAES-PKCS1-V1_5 decryption failure, goto end;
* substitute a random CEK and continue into content decryption.
* This prevents the Bleichenbacher timing oracle: without this
* guard, "padding invalid" (fast exit) is distinguishable from
* "padding valid + AEAD tag fail" (full decrypt path).
* We will build the biggest decrypted_cek necessary rather than
* filling the entire buffer, which would be a key for the
* A256CBC_HS512 encrypting algorithm for which the decrypted
* cek contains the actual key as well as the tag.
*/
if (pad == RSA_PKCS1_PADDING) {
#define MAX_DECRYPTED_CEK_LEN (32 * 2) /* See https://datatracker.ietf.org/doc/html/rfc7518#section-5.2.2.1 */
int i;
unsigned char *p = (unsigned char *)b_orig(decrypted_cek);
for (i = 0; i < MAX_DECRYPTED_CEK_LEN; i++) {
uint64_t r = ha_random64();
memcpy(p, &r, 8);
p+=8;
}
outl = MAX_DECRYPTED_CEK_LEN;
} else
goto end;
}
decrypted_cek->data = outl; decrypted_cek->data = outl;

View file

@ -54,15 +54,13 @@ 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;
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_X, &x);
goto out; EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &y);
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;
@ -146,7 +144,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;
@ -294,7 +292,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;

View file

@ -324,8 +324,6 @@ 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;
} }

View file

@ -20,7 +20,6 @@
#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>
@ -83,7 +82,6 @@ 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
@ -98,11 +96,6 @@ 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;
} }
@ -128,14 +121,6 @@ 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;

View file

@ -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 = global.maxpipes ? global.maxpipes : compute_ideal_maxpipes(); int pipes = 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;

View file

@ -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(array_size_or_fail(sizeof(*cpy->lb.smp_rgs), def->lb.smp_rgs_sz)); cpy->lb.smp_rgs = malloc(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) {
uint month; int 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,7 +6743,6 @@ 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;
} }

View file

@ -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 __maybe_unused *conn = fstrm->fconn->conn; struct connection *conn = fstrm->fconn->conn;
TRACE_ENTER(FCGI_EV_FSTRM_END, conn, fstrm); TRACE_ENTER(FCGI_EV_FSTRM_END, conn, fstrm);
@ -1595,7 +1595,7 @@ static int fcgi_conn_handle_values_result(struct fcgi_conn *fconn)
return 0; return 0;
} }
if (unlikely(b_contig_data(dbuf, 0) < fconn->drl)) { if (unlikely(b_contig_data(dbuf, b_head_ofs(dbuf)) < fconn->drl)) {
/* Realign the dmux buffer if the record wraps. It is unexpected /* Realign the dmux buffer if the record wraps. It is unexpected
* at this stage because it should be the first record received * at this stage because it should be the first record received
* from the FCGI application. * from the FCGI application.
@ -2516,8 +2516,13 @@ static int fcgi_strm_handle_end_request(struct fcgi_conn *fconn, struct fcgi_str
return 0; return 0;
} }
if (unlikely(b_contig_data(dbuf, 0) < fconn->drl)) if (unlikely(b_contig_data(dbuf, b_head_ofs(dbuf)) < fconn->drl)) {
/* Realign the dmux buffer if the record wraps. It is unexpected
* at this stage because it should be the first record received
* from the FCGI application.
*/
b_slow_realign_ofs(dbuf, trash.area, 0); b_slow_realign_ofs(dbuf, trash.area, 0);
}
inbuf = b_make(b_head(dbuf), b_data(dbuf), 0, fconn->drl); inbuf = b_make(b_head(dbuf), b_data(dbuf), 0, fconn->drl);
@ -2639,16 +2644,6 @@ static void fcgi_process_demux(struct fcgi_conn *fconn)
} }
fstrm = tmp_fstrm; fstrm = tmp_fstrm;
if (fconn->dsi == 0 && fconn->drt != FCGI_GET_VALUES_RESULT && fconn->drt != FCGI_UNKNOWN_TYPE) {
/* Stream ID 0 is reserved for management records and
* must not used for application record type.
*/
fconn->state = FCGI_CS_CLOSED;
TRACE_ERROR("Application record with SID 0", FCGI_EV_RX_RECORD|FCGI_EV_RX_FHDR|FCGI_EV_RX_GETVAL|FCGI_EV_FCONN_ERR, fconn->conn);
TRACE_STATE("switching to CLOSED", FCGI_EV_RX_RECORD|FCGI_EV_RX_FHDR|FCGI_EV_RX_GETVAL|FCGI_EV_FCONN_ERR, fconn->conn);
goto fail;
}
if (fstrm->state == FCGI_SS_CLOSED && fconn->dsi != 0) { if (fstrm->state == FCGI_SS_CLOSED && fconn->dsi != 0) {
/* ignore all record for closed streams */ /* ignore all record for closed streams */
goto ignore_record; goto ignore_record;
@ -3545,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 __maybe_unused *fconn = fstrm->fconn; struct fcgi_conn *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;
@ -4036,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 __maybe_unused *fconn = fstrm->fconn; struct fcgi_conn *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);
@ -4556,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 =
{ .mux_proto = IST("fcgi"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BE, .mux = &mux_fcgi_ops }; { .token = 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);

View file

@ -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 __maybe_unused *h1c = h1s->h1c; struct h1c *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,9 +2713,7 @@ 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))) {
@ -4257,11 +4255,11 @@ static int h1_process(struct h1c * h1c)
h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event); h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event);
} }
} }
no_parsing:
if (h1c->glitches != prev_glitches && !(h1c->flags & H1C_F_IS_BACK)) if (h1c->glitches != prev_glitches && !(h1c->flags & H1C_F_IS_BACK))
session_add_glitch_ctr(sess, h1c->glitches - prev_glitches); session_add_glitch_ctr(sess, h1c->glitches - prev_glitches);
} }
no_parsing:
h1_send(h1c); h1_send(h1c);
/* H1 connection must be released ASAP if: /* H1 connection must be released ASAP if:
@ -6125,9 +6123,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 =
{ .mux_proto = IST("h1"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &mux_h1_ops }; { .token = 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 =
{ .mux_proto = IST(""), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &mux_http_ops, .alpn = "\010http/1.1" }; { .token = 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);

View file

@ -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 __maybe_unused *c_str, *s_str; const char *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 (global.tune.streams_elasticity && h2c->streams_hard_limit > 1) if (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,10 +1527,7 @@ 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:
/* Unnecessary code for now as fail_stream only occurs with BE conns. if (!(h2c->flags & H2_CF_IS_BACK) && h2c->streams_hard_limit > 1)
* 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:
@ -1610,7 +1607,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) && global.tune.streams_elasticity && h2c->streams_hard_limit > 1) if (!(h2c->flags & H2_CF_IS_BACK) && 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);
@ -2024,7 +2021,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 __maybe_unused *conn = h2s->h2c->conn; struct connection *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);
@ -4200,7 +4197,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 && global.tune.streams_elasticity) if (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);
h2c->flags |= H2_CF_IS_BACK; h2c->flags |= H2_CF_IS_BACK;
@ -4222,7 +4219,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 && global.tune.streams_elasticity) if (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);
h2c->flags &= ~H2_CF_IS_BACK; h2c->flags &= ~H2_CF_IS_BACK;
@ -6236,13 +6233,6 @@ 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");
@ -6252,6 +6242,13 @@ 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;
} }
@ -7613,7 +7610,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 __maybe_unused *h2c = h2s->h2c; struct h2c *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 */
@ -9013,7 +9010,7 @@ static const struct mux_ops h2_ops = {
}; };
static struct mux_proto_list mux_proto_h2 = static struct mux_proto_list mux_proto_h2 =
{ .mux_proto = IST("h2"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &h2_ops, .alpn = "\002h2" }; { .token = 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);

View file

@ -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 =
{ .mux_proto = IST("none"), .mode = PROTO_MODE_TCP, .side = PROTO_SIDE_BOTH, .mux = &mux_pt_ops }; { .token = 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 =
{ .mux_proto = IST(""), .mode = PROTO_MODE_TCP, .side = PROTO_SIDE_BOTH, .mux = &mux_tcp_ops }; { .token = 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);

View file

@ -2467,7 +2467,6 @@ 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);
@ -2484,42 +2483,8 @@ 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_rel / 2 || if (qcc->lfctl.cl_bidi_r > qcc->lfctl.ms_bidi_init / 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) {
@ -2528,11 +2493,12 @@ 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 + inc; frm->max_streams_bidi.max_streams = qcc->lfctl.ms_bidi +
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 += inc; qcc->lfctl.ms_bidi += qcc->lfctl.cl_bidi_r;
qcc->lfctl.cl_bidi_r = 0; qcc->lfctl.cl_bidi_r = 0;
} }
} }
@ -3341,10 +3307,6 @@ 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.
@ -3624,12 +3586,6 @@ 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);
@ -3893,7 +3849,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 = qcc->lfctl.ms_bidi_rel = lparams->initial_max_streams_bidi; qcc->lfctl.ms_bidi = qcc->lfctl.ms_bidi_init = 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;
@ -3922,7 +3878,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 = qcc->lfctl.ms_bidi_rel= lparams->initial_max_streams_bidi; qcc->lfctl.ms_bidi = qcc->lfctl.ms_bidi_init = 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;
@ -3972,11 +3928,6 @@ 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();
@ -4203,10 +4154,6 @@ 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;
} }
@ -4690,7 +4637,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_rel; return qcc->lfctl.ms_bidi_init;
case MUX_CTL_TEVTS: case MUX_CTL_TEVTS:
return qcc->term_evts_log; return qcc->term_evts_log;
@ -4837,7 +4784,7 @@ void qcc_show_quic(struct qcc *qcc)
} }
static struct mux_proto_list mux_proto_quic = static struct mux_proto_list mux_proto_quic =
{ .mux_proto = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &quic_ops }; { .token = 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);
@ -4866,7 +4813,6 @@ static const struct mux_ops qmux_ops = {
}; };
static struct mux_proto_list mux_proto_qmux = static struct mux_proto_list mux_proto_qmux =
{ .mux_proto = IST("qmux"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &qmux_ops, { .token = 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);

View file

@ -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 __maybe_unused *conn = spop_strm->spop_conn->conn; struct connection *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);
@ -1652,10 +1652,10 @@ static int spop_conn_handle_hello(struct spop_conn *spop_conn)
return 0; return 0;
} }
if (unlikely(b_contig_data(dbuf, 0) < spop_conn->dfl)) { if (unlikely(b_contig_data(dbuf, b_head_ofs(dbuf)) < spop_conn->dfl)) {
/* Realign the dmux buffer if the frame wraps. It is unexpected /* Realign the dmux buffer if the frame wraps. It is unexpected
* at this stage because it should be the first record received * at this stage because it should be the first record received
* from the SPOA. * from the FCGI application.
*/ */
b_slow_realign_ofs(dbuf, trash.area, 0); b_slow_realign_ofs(dbuf, trash.area, 0);
} }
@ -1824,8 +1824,13 @@ static int spop_conn_handle_disconnect(struct spop_conn *spop_conn)
return 0; return 0;
} }
if (unlikely(b_contig_data(dbuf, 0) < spop_conn->dfl)) if (unlikely(b_contig_data(dbuf, b_head_ofs(dbuf)) < spop_conn->dfl)) {
/* Realign the dmux buffer if the frame wraps. It is unexpected
* at this stage because it should be the first record received
* from the FCGI application.
*/
b_slow_realign_ofs(dbuf, trash.area, 0); b_slow_realign_ofs(dbuf, trash.area, 0);
}
p = b_head(dbuf); p = b_head(dbuf);
end = p + spop_conn->dfl; end = p + spop_conn->dfl;
@ -1931,8 +1936,13 @@ static int spop_conn_handle_ack(struct spop_conn *spop_conn, struct spop_strm *s
return 0; return 0;
} }
if (unlikely(b_contig_data(dbuf, 0) < spop_conn->dfl)) if (unlikely(b_contig_data(dbuf, b_head_ofs(dbuf)) < spop_conn->dfl)) {
/* Realign the dmux buffer if the frame wraps. It is unexpected
* at this stage because it should be the first record received
* from the FCGI application.
*/
b_slow_realign_ofs(dbuf, trash.area, 0); b_slow_realign_ofs(dbuf, trash.area, 0);
}
spop_conn->flags &= ~SPOP_CF_DEM_SFULL; spop_conn->flags &= ~SPOP_CF_DEM_SFULL;
rxbuf = spop_get_buf(spop_conn, &spop_strm->rxbuf); rxbuf = spop_get_buf(spop_conn, &spop_strm->rxbuf);
@ -3189,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 __maybe_unused *spop_conn = spop_strm->spop_conn; struct spop_conn *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);
@ -3728,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 =
{ .mux_proto = IST("spop"), .mode = PROTO_MODE_SPOP, .side = PROTO_SIDE_BE, .mux = &mux_spop_ops }; { .token = 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 =
{ .mux_proto = IST(""), .mode = PROTO_MODE_SPOP, .side = PROTO_SIDE_BE, .mux = &mux_spop_ops }; { .token = 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);

View file

@ -1298,7 +1298,7 @@ void mworker_apply_master_worker_mode(void)
/* This one must not be exported, it's internal! */ /* This one must not be exported, it's internal! */
unsetenv("HAPROXY_MWORKER_REEXEC"); unsetenv("HAPROXY_MWORKER_REEXEC");
ha_random_jump128(1); ha_random_jump96(1);
list_for_each_entry(child, &proc_list, list) { list_for_each_entry(child, &proc_list, list) {
if ((child->options & PROC_O_TYPE_WORKER) && (child->options & PROC_O_INIT)) { if ((child->options & PROC_O_TYPE_WORKER) && (child->options & PROC_O_INIT)) {

View file

@ -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 && smp->data.u.str.area[next + 1] > 1) else if (next + 1 < len)
next += (uchar)smp->data.u.str.area[next + 1]; next += 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] > 1) else if (ofs + 1 < len && smp->data.u.str.area[ofs + 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] > 1) else if ((ofs + 1 < tcplen) && smp->data.u.str.area[ofs + 1]) /* optlen 0 will cause an infinite loop */
next = ofs + (uchar)smp->data.u.str.area[ofs + 1]; next = ofs + (uchar)smp->data.u.str.area[ofs + 1];
else else
break; break;

View file

@ -116,9 +116,6 @@ smp_client_hello_parse( struct sample *smp, enum client_hello_type type, unsigne
data += 5; /* enter TLS handshake */ data += 5; /* enter TLS handshake */
bleft -= 5; bleft -= 5;
if (bleft < hs_len)
goto too_short;
/* Check for a complete client hello starting at <data> */ /* Check for a complete client hello starting at <data> */
if (bleft < 1) if (bleft < 1)
goto too_short; goto too_short;
@ -132,18 +129,15 @@ smp_client_hello_parse( struct sample *smp, enum client_hello_type type, unsigne
if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2) if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
goto not_ssl_hello; /* too short to have an extension */ goto not_ssl_hello; /* too short to have an extension */
data += 4;
bleft -= 4;
/* We want the full handshake here */ /* We want the full handshake here */
if (bleft < hs_len) if (bleft < hs_len)
goto too_short; goto too_short;
data += 4;
/* Start of the ClientHello message */ /* Start of the ClientHello message */
if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */ if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
goto not_ssl_hello; goto not_ssl_hello;
/* Note: covered by the hs_len test 30 lines above */
ext_len = data[34]; /* session_id_len */ ext_len = data[34]; /* session_id_len */
if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct session_id len */ if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct session_id len */
goto not_ssl_hello; goto not_ssl_hello;

View file

@ -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->mux_proto, ist("h2"))) && if ((!srv->mux_proto || !isteqi(srv->mux_proto->token, 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;

View file

@ -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 */
__decl_spinlock(proxies_del_lock); static __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[4]); args[0], args[1], args[3]);
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' keyword only usable alone", args[0]); memprintf(err, "'%s' 'none' keyworld 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->mux_proto, IST_NULL, PROTO_SIDE_FE, is_quic, mode); mux_ent = conn_get_best_mux_entry(bind_conf->mux_proto->token, PROTO_SIDE_FE, is_quic, mode);
if (!mux_ent || !isteq(mux_ent->mux_proto, bind_conf->mux_proto->mux_proto)) { if (!mux_ent || !isteq(mux_ent->token, bind_conf->mux_proto->token)) {
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->mux_proto.len, (int)bind_conf->mux_proto->token.len,
bind_conf->mux_proto->mux_proto.ptr, bind_conf->mux_proto->token.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->mux_proto.len, (int)bind_conf->mux_proto->token.len,
bind_conf->mux_proto->mux_proto.ptr, bind_conf->mux_proto->token.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->mux_proto.len, (int)bind_conf->mux_proto->token.len,
bind_conf->mux_proto->mux_proto.ptr, bind_conf->mux_proto->token.ptr,
bind_conf->arg, bind_conf->file, bind_conf->line); bind_conf->arg, bind_conf->file, bind_conf->line);
cfgerr++; cfgerr++;
} }
@ -1778,22 +1778,6 @@ 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
@ -1808,13 +1792,6 @@ 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 */
@ -2872,13 +2849,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->mux_proto, IST_NULL, PROTO_SIDE_BE, srv_is_quic(newsrv), mode); mux_ent = conn_get_best_mux_entry(newsrv->mux_proto->token, PROTO_SIDE_BE, srv_is_quic(newsrv), mode);
if (!mux_ent || !isteq(mux_ent->mux_proto, newsrv->mux_proto->mux_proto)) { if (!mux_ent || !isteq(mux_ent->token, newsrv->mux_proto->token)) {
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->mux_proto.len, (int)newsrv->mux_proto->token.len,
newsrv->mux_proto->mux_proto.ptr, newsrv->mux_proto->token.ptr,
newsrv->id, newsrv->conf.file, newsrv->conf.line); newsrv->id, newsrv->conf.file, newsrv->conf.line);
cfgerr++; cfgerr++;
} }
@ -2886,16 +2863,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->mux_proto.len, (int)newsrv->mux_proto->token.len,
newsrv->mux_proto->mux_proto.ptr, newsrv->mux_proto->token.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->mux_proto.len, (int)newsrv->mux_proto->token.len,
newsrv->mux_proto->mux_proto.ptr, newsrv->mux_proto->token.ptr,
newsrv->id, newsrv->conf.file, newsrv->conf.line); newsrv->id, newsrv->conf.file, newsrv->conf.line);
cfgerr++; cfgerr++;
} }
@ -3585,7 +3562,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);
proxy_drop(curproxy); ha_free(&curproxy);
return NULL; return NULL;
} }
} }
@ -5466,8 +5443,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, 0 }, { { "add", "backend", NULL }, "add backend <backend> : add a new backend", cli_parse_add_backend, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
{ { "del", "backend", NULL }, "del backend <backend> : delete a backend", cli_parse_delete_backend, NULL, NULL, NULL, 0 }, { { "del", "backend", NULL }, "del backend <backend> : delete a backend", cli_parse_delete_backend, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
{ { "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 },

View file

@ -70,10 +70,6 @@ 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);
@ -86,26 +82,13 @@ 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();
} }
@ -145,8 +128,7 @@ int qcc_qmux_recv(struct qcc *qcc)
*/ */
if (b_head(buf) + qcc->rx.rlen > b_wrap(buf) || if (b_head(buf) + qcc->rx.rlen > b_wrap(buf) ||
(!dec && b_head(buf) + b_data(buf) == b_wrap(buf))) { (!dec && b_head(buf) + b_data(buf) == b_wrap(buf))) {
/* A too large record should have been rejected earlier. */ BUG_ON(qcc->rx.rlen > b_size(buf)); /* TODO max_record_size */
BUG_ON(qcc->rx.rlen > b_size(buf));
memmove(b_orig(buf), b_head(buf), b_data(buf)); memmove(b_orig(buf), b_head(buf), b_data(buf));
buf->head = 0; buf->head = 0;
} }
@ -172,12 +154,6 @@ int qcc_qmux_recv(struct qcc *qcc)
if (b_data(buf) && !qcc->rx.rlen) { if (b_data(buf) && !qcc->rx.rlen) {
dec = b_quic_dec_int(&qcc->rx.rlen, buf, NULL); dec = b_quic_dec_int(&qcc->rx.rlen, buf, NULL);
if (qcc->rx.rlen > b_size(buf)) {
/* TODO report FRAME_ENCODING_ERROR on max_record_size violation */
qcc->conn->flags |= CO_FL_ERROR;
goto out;
}
/* Restart read if an incomplete record has been received /* Restart read if an incomplete record has been received
* until there is no more new data available. * until there is no more new data available.
*/ */
@ -195,10 +171,11 @@ int qcc_qmux_recv(struct qcc *qcc)
buf_rec = b_make(b_orig(buf), b_size(buf), buf_rec = b_make(b_orig(buf), b_size(buf),
b_head_ofs(buf), qcc->rx.rlen); b_head_ofs(buf), qcc->rx.rlen);
frm_ret = qmux_parse_frm(qcc, &buf_rec); frm_ret = qmux_parse_frm(qcc, &buf_rec);
BUG_ON(frm_ret < 0); /* TODO handle fatal errors */
if (!frm_ret) { if (!frm_ret) {
/* TODO implement proper connection closure */ /* emit FRAME_ENCODING_ERROR */
conn->flags |= CO_FL_ERROR; ABORT_NOW();
goto out;
} }
/* A frame cannot be bigger than a record thanks to <buf_rec> delimitation. */ /* A frame cannot be bigger than a record thanks to <buf_rec> delimitation. */

View file

@ -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, derive_cid.data, derive_cid.len); node = ebmb_lookup(&tree->root, cid, 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);

View file

@ -88,7 +88,7 @@ static int qc_do_rm_hp(struct quic_conn *qc,
ret = 0; ret = 0;
/* Check there is enough data in this packet. */ /* Check there is enough data in this packet. */
if (pkt->len - pkt->pn_offset < QUIC_PACKET_PN_MAXLEN + QUIC_TLS_TAG_LEN) { if (pkt->len - (pn - byte0) < QUIC_PACKET_PN_MAXLEN + sizeof mask) {
TRACE_PROTO("too short packet", QUIC_EV_CONN_RMHP, qc, pkt); TRACE_PROTO("too short packet", QUIC_EV_CONN_RMHP, qc, pkt);
goto leave; goto leave;
} }

View file

@ -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 __maybe_unused *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index); struct quic_conn *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);

View file

@ -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 __maybe_unused kp_trace = { struct quic_kp_trace 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,

View file

@ -24,10 +24,8 @@ int quic_generate_token(unsigned char *token, size_t len,
unsigned char aad[sizeof(struct in6_addr)]; unsigned char aad[sizeof(struct in6_addr)];
size_t aadlen; size_t aadlen;
uint32_t ts = (uint32_t)date.tv_sec; uint32_t ts = (uint32_t)date.tv_sec;
union { uint64_t rand_u64;
uint64_t u64[2]; unsigned char rand[QUIC_TOKEN_RAND_DLEN];
uchar u8[QUIC_TOKEN_RAND_DLEN];
} rand;
unsigned char key[16]; unsigned char key[16];
unsigned char iv[QUIC_TLS_IV_LEN]; unsigned char iv[QUIC_TLS_IV_LEN];
const unsigned char *sec = global.cluster_secret; const unsigned char *sec = global.cluster_secret;
@ -37,7 +35,10 @@ int quic_generate_token(unsigned char *token, size_t len,
TRACE_ENTER(QUIC_EV_CONN_TXPKT); TRACE_ENTER(QUIC_EV_CONN_TXPKT);
/* Generate random data to be used as salt to derive the token secret. */ /* Generate random data to be used as salt to derive the token secret. */
ha_random64_pair_hashed(&rand.u64[0], &rand.u64[1]); rand_u64 = ha_random64();
write_u64(rand, rand_u64);
rand_u64 = ha_random64();
write_u64(rand + sizeof(rand_u64), rand_u64);
if (len < QUIC_TOKEN_LEN) { if (len < QUIC_TOKEN_LEN) {
TRACE_ERROR("too small buffer", QUIC_EV_CONN_TXPKT); TRACE_ERROR("too small buffer", QUIC_EV_CONN_TXPKT);
@ -47,7 +48,7 @@ int quic_generate_token(unsigned char *token, size_t len,
/* Generate the AAD. */ /* Generate the AAD. */
aadlen = ipaddrcpy(aad, addr); aadlen = ipaddrcpy(aad, addr);
if (!quic_tls_derive_token_secret(EVP_sha256(), key, sizeof key, if (!quic_tls_derive_token_secret(EVP_sha256(), key, sizeof key,
iv, sizeof iv, rand.u8, sizeof(rand.u8), iv, sizeof iv, rand, sizeof(rand),
sec, seclen)) { sec, seclen)) {
TRACE_ERROR("quic_tls_derive_token_secret() failed", QUIC_EV_CONN_TXPKT); TRACE_ERROR("quic_tls_derive_token_secret() failed", QUIC_EV_CONN_TXPKT);
goto err; goto err;
@ -70,8 +71,8 @@ int quic_generate_token(unsigned char *token, size_t len,
} }
p += QUIC_TLS_TAG_LEN; p += QUIC_TLS_TAG_LEN;
memcpy(p, rand.u8, sizeof(rand.u8)); memcpy(p, rand, sizeof(rand));
p += sizeof(rand.u8); p += sizeof(rand);
ret = p - token; ret = p - token;
leave: leave:

View file

@ -1,7 +1,6 @@
#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>
@ -71,7 +70,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 = server ? max_streams_bidi : 0; p->initial_max_streams_bidi = max_streams_bidi;
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,
@ -863,15 +862,6 @@ 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;
} }

View file

@ -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, NULL); local_pcre2_match = pcre2_match_data_create(MAX_MATCH - 1, 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;

View file

@ -63,6 +63,7 @@ struct list resolv_srvrq_list = LIST_HEAD_INIT(resolv_srvrq_list);
static THREAD_LOCAL struct list death_row; /* list of deferred resolutions to kill, local validity only */ static THREAD_LOCAL struct list death_row; /* list of deferred resolutions to kill, local validity only */
static THREAD_LOCAL unsigned int recurse = 0; /* counter to track calls to public functions */ static THREAD_LOCAL unsigned int recurse = 0; /* counter to track calls to public functions */
static THREAD_LOCAL uint64_t resolv_query_id_seed = 0; /* random seed */
struct resolvers *curr_resolvers = NULL; struct resolvers *curr_resolvers = NULL;
DECLARE_STATIC_TYPED_POOL(resolv_answer_item_pool, "resolv_answer_item", struct resolv_answer_item); DECLARE_STATIC_TYPED_POOL(resolv_answer_item_pool, "resolv_answer_item", struct resolv_answer_item);
@ -225,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 * RSLV_ACCEPT_IPV4 and RSLV_ACCEPT_IPV6 only. It will dynamically adapt 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.
*/ */
@ -364,6 +365,18 @@ struct resolv_answer_item *find_srvrq_answer_record(const struct resolv_requeste
return NULL; return NULL;
} }
/* 2 bytes random generator to generate DNS query ID */
static inline uint16_t resolv_rnd16(void)
{
if (!resolv_query_id_seed)
resolv_query_id_seed = now_ms;
resolv_query_id_seed ^= resolv_query_id_seed << 13;
resolv_query_id_seed ^= resolv_query_id_seed >> 7;
resolv_query_id_seed ^= resolv_query_id_seed << 17;
return resolv_query_id_seed;
}
static inline int resolv_resolution_timeout(struct resolv_resolution *res) static inline int resolv_resolution_timeout(struct resolv_resolution *res)
{ {
return (!LIST_ISEMPTY(&res->requesters) ? res->resolvers->timeout.resolve : res->resolvers->hold.valid); return (!LIST_ISEMPTY(&res->requesters) ? res->resolvers->timeout.resolve : res->resolvers->hold.valid);
@ -496,14 +509,14 @@ 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 pile up. */ * directly to avoid resolution pill up. */
if (resolution->step != RSLV_STEP_NONE) if (resolution->step != RSLV_STEP_NONE)
return 0; return 0;
/* Generates a new query id. We try at most 100 times to find a free /* Generates a new query id. We try at most 100 times to find a free
* query id */ * query id */
for (i = 0; i < 100; ++i) { for (i = 0; i < 100; ++i) {
query_id = (uint16_t)ha_random32(); query_id = resolv_rnd16();
if (!eb32_lookup(&resolvers->query_ids, query_id)) if (!eb32_lookup(&resolvers->query_ids, query_id))
break; break;
query_id = -1; query_id = -1;
@ -611,21 +624,17 @@ 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 + ptr_offset) >= reader) if ((buffer + reader[1]) > reader)
goto err; goto err;
if (depth++ > 100) if (depth++ > 100)
goto err; goto err;
n = resolv_read_name(buffer, bufend, buffer + ptr_offset, n = resolv_read_name(buffer, bufend, buffer + (*reader & 0x3f)*256 + reader[1],
dest, dest_len - nb_bytes, offset, depth); dest, dest_len - nb_bytes, offset, depth);
if (n == 0) if (n == 0)
goto err; goto err;
@ -1223,7 +1232,8 @@ 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 = read_n32(reader); answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536
+ reader[2] * 256 + reader[3];
reader += 4; reader += 4;
/* Now reading data len */ /* Now reading data len */
@ -1422,7 +1432,7 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
if (len == 0) if (len == 0)
continue; continue;
if (reader + offset + 10 > bufend) if (reader + offset + 10 >= bufend)
goto invalid_resp; goto invalid_resp;
reader += offset; reader += offset;
@ -1436,7 +1446,7 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
len = reader[0] * 256 + reader[1]; len = reader[0] * 256 + reader[1];
reader += 2; reader += 2;
if (reader + len > bufend) if (reader + len >= bufend)
goto invalid_resp; goto invalid_resp;
reader += len; reader += len;
@ -1487,7 +1497,8 @@ 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 = read_n32(reader); answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536
+ reader[2] * 256 + reader[3];
reader += 4; reader += 4;
/* Now reading data len */ /* Now reading data len */
@ -1587,6 +1598,7 @@ 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;
@ -1842,15 +1854,7 @@ 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 = (unsigned char)dn[i]; sz = 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 */
@ -2691,7 +2695,6 @@ 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);
@ -3195,7 +3198,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 = { 0 }; struct sample smp;
short ip_sin_family = 0; short ip_sin_family = 0;
void *ip = NULL; void *ip = NULL;
@ -3401,8 +3404,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'%s%s. Expects 'do-resolve(<varname>,<resolvers>[,<options>]) <expr>'. Available options are 'ipv4' and 'ipv6'", memprintf(err, "Can't parse '%s'. Expects 'do-resolve(<varname>,<resolvers>[,<options>]) <expr>'. Available options are 'ipv4' and 'ipv6'",
args[cur_arg], *err ? ": " : "", *err ? *err : ""); args[cur_arg]);
return ACT_RET_PRS_ERR; return ACT_RET_PRS_ERR;
} }
@ -3473,7 +3476,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(array_size_or_fail(sizeof(*resolv_line), LINESIZE))) == NULL) { if ((resolv_line = malloc(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;
@ -3565,8 +3568,6 @@ 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;
} }
@ -3577,8 +3578,6 @@ 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;
} }
@ -3669,7 +3668,6 @@ 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;
@ -3714,7 +3712,6 @@ 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;
} }
} }
@ -3752,7 +3749,6 @@ 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;
} }
} }
@ -3776,56 +3772,30 @@ 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;
} }

View file

@ -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 * 2); struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data);
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;
size_t max_size; int max_size;
int ptr = 0; int ptr = 0;
unsigned char c; unsigned char c;
@ -2163,9 +2163,7 @@ 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;
if (2 * (size_t)chunk_size > trash->size) max_size = trash->size - 2 * chunk_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) {

View file

@ -145,7 +145,6 @@ 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);
} }
@ -644,7 +643,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->mux_proto : IST_NULL; srv->mux_proto->token : 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. */
@ -936,8 +935,6 @@ 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;
} }
@ -1016,19 +1013,13 @@ 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', 'id32', 'guid', 'addr', or 'addr-port' value", args[*cur_arg]); memprintf(err, "'%s expects 'id', '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;
} }
@ -1036,7 +1027,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', 'id32', 'guid', 'addr', or 'addr-port'", args[*cur_arg]); memprintf(err, "'%s' has to be 'id', 'addr', or 'addr-port'", args[*cur_arg]);
return ERR_ALERT | ERR_FATAL; return ERR_ALERT | ERR_FATAL;
} }
@ -1133,25 +1124,22 @@ 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], "none") == 0) if (strcmp(args[*cur_arg + 1], "fully-up") == 0)
newsrv->init_state = SRV_INIT_STATE_NONE; newsrv->init_state= SRV_INIT_STATE_FULLY_UP;
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 'none', 'fully-up', 'up', 'down', or 'fully-down' but got '%s'", memprintf(err, "'%s' expects one of '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;
} }
@ -1320,20 +1308,19 @@ 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;
} }
id = atol(args[*cur_arg + 1]); newsrv->puid = atol(args[*cur_arg + 1]);
if (id < 1 || id > ~0U) {
memprintf(err, "'%s' : custom id has to be between 1 and 4294967295.", args[*cur_arg]); if (newsrv->puid <= 0) {
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')",
@ -2893,7 +2880,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_NONE; srv->init_state = SRV_INIT_STATE_UP;
srv->maxqueue = 0; srv->maxqueue = 0;
srv->minconn = 0; srv->minconn = 0;
@ -3027,9 +3014,7 @@ 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. Otherwise, /* Here we check if 'disabled' is the default server state */
* 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;
@ -3057,7 +3042,6 @@ 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
@ -3071,8 +3055,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 (src->pool_conn_name) if (srv->pool_conn_name)
srv->pool_conn_name = strdup(src->pool_conn_name); srv->pool_conn_name = strdup(srv->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;
@ -3632,7 +3616,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, array_size_or_fail(global.nbthread, sizeof(*srv->curr_idle_thr))); srv->curr_idle_thr = ha_aligned_zalloc(64, 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);
@ -3974,10 +3958,6 @@ 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",
@ -4044,18 +4024,6 @@ 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:
@ -5258,8 +5226,6 @@ 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)
@ -5290,18 +5256,13 @@ 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 = hostname_dup; srv->hostname = strdup(hostname);
srv->hostname_dn = hostname_dn_dup; srv->hostname_dn = strdup(hostname_dn);
srv->hostname_dn_len = hostname_dn_len; srv->hostname_dn_len = hostname_dn_len;
hostname_dup = NULL; if (!srv->hostname || !srv->hostname_dn)
hostname_dn_dup = NULL; goto err;
if (srv->flags & SRV_F_NO_RESOLUTION) if (srv->flags & SRV_F_NO_RESOLUTION)
goto end; goto end;
@ -5317,8 +5278,6 @@ 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;
} }
@ -5743,13 +5702,11 @@ 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;
} }
@ -5959,7 +5916,6 @@ 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;
} }
@ -6106,8 +6062,8 @@ static int srv_init_per_thr(struct server *srv)
{ {
int i; int i;
srv->per_thr = ha_aligned_zalloc(64, array_size_or_fail(global.nbthread, sizeof(*srv->per_thr))); srv->per_thr = ha_aligned_zalloc(64, global.nbthread * sizeof(*srv->per_thr));
srv->per_tgrp = ha_aligned_zalloc(64, array_size_or_fail(global.nbtgroups, sizeof(*srv->per_tgrp))); srv->per_tgrp = ha_aligned_zalloc(64, global.nbtgroups * sizeof(*srv->per_tgrp));
if (!srv->per_thr || !srv->per_tgrp) if (!srv->per_thr || !srv->per_tgrp)
return -1; return -1;
@ -6298,9 +6254,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->mux_proto, IST_NULL, PROTO_SIDE_BE, srv_is_quic(srv), proto_mode); mux_ent = conn_get_best_mux_entry(srv->mux_proto->token, PROTO_SIDE_BE, srv_is_quic(srv), proto_mode);
if (!mux_ent || !isteq(mux_ent->mux_proto, srv->mux_proto->mux_proto)) { if (!mux_ent || !isteq(mux_ent->token, srv->mux_proto->token)) {
ha_alert("MUX protocol is not usable for server.\n"); ha_alert("MUX protocol is not usable for server.\n");
goto out; goto out;
} }
@ -7004,12 +6960,20 @@ 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;
s->check.health = s->check.rise; /* start OK but check immediately */ if(s->init_state == SRV_INIT_STATE_FULLY_UP) {
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->init_state == SRV_INIT_STATE_NONE && if ((!s->track || s->track->next_state != SRV_ST_STOPPED) &&
(!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) {

View file

@ -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 << 2) #define SESS_EV_ERR (1ULL << 1)
{ .mask = SESS_EV_ERR, .name = "sess_err", .desc = "session error" }, { .mask = SESS_EV_ERR, .name = "sess_err", .desc = "session error" },
{ } { }
}; };
@ -241,17 +241,14 @@ 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. */
@ -354,7 +351,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 | CO_FL_WAIT_XPRT_L6)) { if (cli_conn->flags & (CO_FL_WAIT_XPRT | CO_FL_EARLY_SSL_HS)) {
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;

View file

@ -533,7 +533,7 @@ int sock_get_old_sockets(const char *unixsocket)
} }
memset(&msghdr, 0, sizeof(msghdr)); memset(&msghdr, 0, sizeof(msghdr));
cmsgbuf = malloc(array_size_or_fail(CMSG_SPACE(sizeof(int)), MAX_SEND_FD)); cmsgbuf = malloc(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(array_size_or_fail(fd_nb, (1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof(int)))); tmpbuf = malloc(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(array_size_or_fail(fd_nb, sizeof(int))); tmpfd = malloc(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,7 +1011,6 @@ 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;
} }

View file

@ -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, array_size_or_fail(sizeof(char *), (n + 2))); r = my_realloc2(r, 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, array_size_or_fail(sizeof(char *), (n + 2))); r = my_realloc2(r, 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, array_size_or_fail(sizeof(char *), (n + 2))); r = my_realloc2(r, 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;

View file

@ -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 __maybe_unused len; size_t 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;
@ -448,7 +448,6 @@ sni_lookup:
for (i = 0; i < trash.size && i < servername_len; i++) for (i = 0; i < trash.size && i < servername_len; i++)
trash.area[i] = tolower((unsigned char)servername[i]); trash.area[i] = tolower((unsigned char)servername[i]);
trash.area[i] = 0; trash.area[i] = 0;
servername = trash.area;
HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock); HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock);
sni_ctx = ssl_sock_choose_sni_ctx(s, conn, trash.area, has_rsa_sig, has_ecdsa_sig); sni_ctx = ssl_sock_choose_sni_ctx(s, conn, trash.area, has_rsa_sig, has_ecdsa_sig);

Some files were not shown because too many files have changed in this diff Show more