mirror of
https://github.com/haproxy/haproxy.git
synced 2026-05-27 03:33:36 -04:00
Compare commits
No commits in common. "master" and "v3.4-dev12" have entirely different histories.
master
...
v3.4-dev12
109 changed files with 595 additions and 2092 deletions
163
CHANGELOG
163
CHANGELOG
|
|
@ -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
|
||||||
|
|
|
||||||
6
INSTALL
6
INSTALL
|
|
@ -111,12 +111,12 @@ HAProxy requires a working GCC or Clang toolchain and GNU make :
|
||||||
may want to retry with "gmake" which is the name commonly used for GNU make
|
may want to retry with "gmake" which is the name commonly used for GNU make
|
||||||
on BSD systems.
|
on BSD systems.
|
||||||
|
|
||||||
- GCC >= 4.7 (up to 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
|
||||||
|
|
|
||||||
6
Makefile
6
Makefile
|
|
@ -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=),)
|
||||||
|
|
|
||||||
2
VERDATE
2
VERDATE
|
|
@ -1,2 +1,2 @@
|
||||||
$Format:%ci$
|
$Format:%ci$
|
||||||
2026/05/26
|
2026/05/13
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
3.4-dev14
|
3.4-dev12
|
||||||
|
|
|
||||||
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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) */
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 \
|
||||||
|
|
|
||||||
|
|
@ -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}"
|
||||||
|
|
|
||||||
|
|
@ -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."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
24
src/acme.c
24
src/acme.c
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
23
src/cache.c
23
src/cache.c
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -812,7 +812,7 @@ void chk_report_conn_err(struct check *check, int errno_bck, int expired)
|
||||||
}
|
}
|
||||||
|
|
||||||
errno = unclean_errno(errno_bck);
|
errno = unclean_errno(errno_bck);
|
||||||
if (conn && !errno)
|
if (conn && errno)
|
||||||
retrieve_errno_from_socket(conn);
|
retrieve_errno_from_socket(conn);
|
||||||
|
|
||||||
TRACE_ENTER(CHK_EV_HCHK_END|CHK_EV_HCHK_ERR, check, 0, 0, (size_t[]){expired});
|
TRACE_ENTER(CHK_EV_HCHK_END|CHK_EV_HCHK_ERR, check, 0, 0, (size_t[]){expired});
|
||||||
|
|
|
||||||
11
src/cli.c
11
src/cli.c
|
|
@ -1151,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;
|
||||||
|
|
|
||||||
149
src/connection.c
149
src/connection.c
|
|
@ -196,7 +196,7 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake)
|
||||||
* information to create one, typically from the ALPN. If we're
|
* information to create one, typically from the ALPN. If we're
|
||||||
* done with the handshake, attempt to create one.
|
* done with the handshake, attempt to create one.
|
||||||
*/
|
*/
|
||||||
if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_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";
|
||||||
|
|
|
||||||
24
src/dict.c
24
src/dict.c
|
|
@ -79,7 +79,7 @@ static struct dict_entry *__dict_lookup(struct dict *d, const char *s)
|
||||||
*/
|
*/
|
||||||
struct dict_entry *dict_insert(struct dict *d, char *s)
|
struct dict_entry *dict_insert(struct dict *d, char *s)
|
||||||
{
|
{
|
||||||
struct dict_entry *de, *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);
|
||||||
|
|
||||||
|
|
|
||||||
19
src/dns.c
19
src/dns.c
|
|
@ -194,7 +194,7 @@ int dns_send_nameserver(struct dns_nameserver *ns, void *buf, size_t len)
|
||||||
struct ist myist;
|
struct ist myist;
|
||||||
|
|
||||||
myist = ist2(buf, len);
|
myist = ist2(buf, len);
|
||||||
ret = dns_ring_write(ns->stream->ring_req, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
|
ret = dns_ring_write(ns->stream->ring_req, DNS_TCP_MSG_MAX_SIZE, NULL, 0, &myist, 1);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ns->counters->snd_error++;
|
ns->counters->snd_error++;
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -215,7 +215,7 @@ void dns_session_free(struct dns_session *);
|
||||||
*/
|
*/
|
||||||
ssize_t dns_recv_nameserver(struct dns_nameserver *ns, void *data, size_t size)
|
ssize_t dns_recv_nameserver(struct dns_nameserver *ns, void *data, size_t size)
|
||||||
{
|
{
|
||||||
ssize_t ret = -1;
|
ssize_t ret = -1;
|
||||||
|
|
||||||
if (ns->dgram) {
|
if (ns->dgram) {
|
||||||
struct dgram_conn *dgram = &ns->dgram->conn;
|
struct dgram_conn *dgram = &ns->dgram->conn;
|
||||||
|
|
@ -475,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()
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
2
src/fd.c
2
src/fd.c
|
|
@ -1166,7 +1166,7 @@ int init_pollers()
|
||||||
struct poller *bp;
|
struct poller *bp;
|
||||||
|
|
||||||
/* always provide an aligned fdtab */
|
/* always provide an aligned fdtab */
|
||||||
if ((fdtab = ha_aligned_zalloc(64, 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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
65
src/h1.c
65
src/h1.c
|
|
@ -274,17 +274,17 @@ void h1_parse_connection_header(struct h1m *h1m, struct ist *value)
|
||||||
|
|
||||||
/* Parse the Upgrade: header of an HTTP/1 request.
|
/* Parse the Upgrade: header of an HTTP/1 request.
|
||||||
* If "websocket" is found, set H1_MF_UPG_WEBSOCKET flag
|
* If "websocket" is found, set H1_MF_UPG_WEBSOCKET flag
|
||||||
* If "h2c" or "h2" found, 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);
|
||||||
|
|
|
||||||
29
src/h1_htx.c
29
src/h1_htx.c
|
|
@ -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;
|
||||||
|
|
|
||||||
97
src/h3.c
97
src/h3.c
|
|
@ -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
|
||||||
*
|
*
|
||||||
|
|
|
||||||
170
src/haproxy.c
170
src/haproxy.c
|
|
@ -1713,7 +1713,7 @@ void haproxy_init_args(int argc, char **argv)
|
||||||
oldpids_sig = SIGTERM; /* terminate immediately */
|
oldpids_sig = SIGTERM; /* terminate immediately */
|
||||||
while (argc > 1 && argv[1][0] != '-') {
|
while (argc > 1 && argv[1][0] != '-') {
|
||||||
char * endptr = NULL;
|
char * endptr = NULL;
|
||||||
oldpids = realloc(oldpids, 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);
|
||||||
|
|
|
||||||
17
src/haterm.c
17
src/haterm.c
|
|
@ -12,7 +12,7 @@
|
||||||
#include <haproxy/istbuf.h>
|
#include <haproxy/istbuf.h>
|
||||||
#include <haproxy/pipe.h>
|
#include <haproxy/pipe.h>
|
||||||
#include <haproxy/pool.h>
|
#include <haproxy/pool.h>
|
||||||
#include <haproxy/proxy.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
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
22
src/hlua.c
22
src/hlua.c
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
22
src/htx.c
22
src/htx.c
|
|
@ -681,7 +681,7 @@ struct htx_blk *htx_replace_blk_value(struct htx *htx, struct htx_blk *blk,
|
||||||
}
|
}
|
||||||
else { /* Do a defrag first (it is always an expansion) */
|
else { /* Do a defrag first (it is always an expansion) */
|
||||||
struct htx_blk tmpblk;
|
struct htx_blk tmpblk;
|
||||||
struct buffer *chunk = alloc_trash_chunk_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);
|
||||||
|
|
|
||||||
50
src/jwe.c
50
src/jwe.c
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
12
src/jws.c
12
src/jws.c
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
10
src/mux_h1.c
10
src/mux_h1.c
|
|
@ -1897,7 +1897,7 @@ static void h1_append_chunk_crlf(struct buffer *buf)
|
||||||
*/
|
*/
|
||||||
static void h1_set_tunnel_mode(struct h1s *h1s)
|
static void h1_set_tunnel_mode(struct h1s *h1s)
|
||||||
{
|
{
|
||||||
struct h1c __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))) {
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
35
src/mux_h2.c
35
src/mux_h2.c
|
|
@ -1281,7 +1281,7 @@ void _h2_trace_header(const struct ist hn, const struct ist hv,
|
||||||
const struct h2c *h2c, const struct h2s *h2s)
|
const struct h2c *h2c, const struct h2s *h2s)
|
||||||
{
|
{
|
||||||
struct ist n_ist, v_ist;
|
struct ist n_ist, v_ist;
|
||||||
const char __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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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)) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
67
src/proxy.c
67
src/proxy.c
|
|
@ -69,7 +69,7 @@
|
||||||
#include <haproxy/uri_auth.h>
|
#include <haproxy/uri_auth.h>
|
||||||
|
|
||||||
/* Lock to ensure multiple backends deletion concurrently is safe */
|
/* Lock to ensure multiple backends deletion concurrently is safe */
|
||||||
__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 },
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,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. */
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
@ -1420,9 +1430,9 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
|
||||||
len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE,
|
len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE,
|
||||||
&offset, 0);
|
&offset, 0);
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
goto invalid_resp;
|
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;
|
||||||
|
|
@ -1456,8 +1466,11 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
|
||||||
offset = 0;
|
offset = 0;
|
||||||
len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
|
len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
|
||||||
|
|
||||||
if (len == 0)
|
if (len == 0) {
|
||||||
goto invalid_resp;
|
pool_free(resolv_answer_item_pool, answer_record);
|
||||||
|
answer_record = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(answer_record->name, tmpname, len);
|
memcpy(answer_record->name, tmpname, len);
|
||||||
answer_record->name[len] = 0;
|
answer_record->name[len] = 0;
|
||||||
|
|
@ -1484,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 */
|
||||||
|
|
@ -1584,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;
|
||||||
|
|
@ -1839,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 */
|
||||||
|
|
@ -2688,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);
|
||||||
|
|
@ -3192,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;
|
||||||
|
|
||||||
|
|
@ -3398,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3470,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;
|
||||||
|
|
@ -3562,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;
|
||||||
}
|
}
|
||||||
|
|
@ -3574,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;
|
||||||
}
|
}
|
||||||
|
|
@ -3666,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;
|
||||||
|
|
@ -3711,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3749,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3773,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
112
src/server.c
112
src/server.c
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -356,10 +356,8 @@ int ssl_sock_generate_certificate(const char *servername, struct bind_conf *bind
|
||||||
ssl_ctx = (SSL_CTX *)lru->data;
|
ssl_ctx = (SSL_CTX *)lru->data;
|
||||||
if (!ssl_ctx && lru) {
|
if (!ssl_ctx && lru) {
|
||||||
ssl_ctx = ssl_sock_do_create_cert(servername, bind_conf, ssl);
|
ssl_ctx = ssl_sock_do_create_cert(servername, bind_conf, ssl);
|
||||||
if (!ssl_ctx) {
|
if (!ssl_ctx)
|
||||||
HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
lru64_commit(lru, ssl_ctx, cacert, 0, (void (*)(void *))SSL_CTX_free);
|
lru64_commit(lru, ssl_ctx, cacert, 0, (void (*)(void *))SSL_CTX_free);
|
||||||
}
|
}
|
||||||
SSL_set_SSL_CTX(ssl, ssl_ctx);
|
SSL_set_SSL_CTX(ssl, ssl_ctx);
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue