mirror of
https://github.com/haproxy/haproxy.git
synced 2026-06-11 01:41:49 -04:00
Compare commits
No commits in common. "master" and "v3.4-dev12" have entirely different histories.
master
...
v3.4-dev12
165 changed files with 946 additions and 3759 deletions
18
.cirrus.yml
Normal file
18
.cirrus.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
FreeBSD_task:
|
||||
freebsd_instance:
|
||||
matrix:
|
||||
image_family: freebsd-14-3
|
||||
only_if: $CIRRUS_BRANCH =~ 'master|next'
|
||||
install_script:
|
||||
- pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua54 socat pcre2
|
||||
script:
|
||||
- sudo sysctl kern.corefile=/tmp/%N.%P.core
|
||||
- sudo sysctl kern.sugid_coredump=1
|
||||
- scripts/build-vtest.sh
|
||||
- gmake CC=clang V=1 ERR=1 TARGET=freebsd USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_OPENSSL=1 USE_LUA=1 LUA_INC=/usr/local/include/lua54 LUA_LIB=/usr/local/lib LUA_LIB_NAME=lua-5.4
|
||||
- ./haproxy -vv
|
||||
- ldd haproxy
|
||||
test_script:
|
||||
- env VTEST_PROGRAM=../vtest/vtest gmake reg-tests REGTESTS_TYPES=default,bug,devel
|
||||
on_failure:
|
||||
debug_script: (for folder in /tmp/*regtest*/vtc.*; do cat $folder/INFO $folder/LOG; done && ls /tmp/haproxy.*.core && gdb -ex 'thread apply all bt full' ./haproxy /tmp/haproxy.*.core)
|
||||
38
.github/workflows/freebsd.yml
vendored
38
.github/workflows/freebsd.yml
vendored
|
|
@ -1,38 +0,0 @@
|
|||
name: FreeBSD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
clang:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
|
||||
steps:
|
||||
- name: "Checkout repository"
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: "Build and test on FreeBSD"
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
release: "14.3"
|
||||
prepare: |
|
||||
pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua54 socat pcre2 python3
|
||||
run: |
|
||||
sysctl kern.corefile=/tmp/%N.%P.core
|
||||
sysctl kern.sugid_coredump=1
|
||||
scripts/build-vtest.sh
|
||||
gmake CC=clang V=1 ERR=1 TARGET=freebsd USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_OPENSSL=1 USE_LUA=1 LUA_INC=/usr/local/include/lua54 LUA_LIB=/usr/local/lib LUA_LIB_NAME=lua-5.4
|
||||
./haproxy -vv
|
||||
ldd haproxy
|
||||
if ! env VTEST_PROGRAM=../vtest/vtest gmake reg-tests REGTESTS_TYPES=default,bug,devel; then
|
||||
for folder in /tmp/*regtest*/vtc.*; do cat $folder/INFO $folder/LOG; done
|
||||
ls /tmp/haproxy.*.core 2>/dev/null && gdb -ex 'thread apply all bt full' ./haproxy /tmp/haproxy.*.core
|
||||
exit 1
|
||||
fi
|
||||
2
.github/workflows/illumos.yml
vendored
2
.github/workflows/illumos.yml
vendored
|
|
@ -2,7 +2,7 @@ name: Illumos
|
|||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 3 * * 1"
|
||||
- cron: "0 0 25 * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
|
|
|||
260
CHANGELOG
260
CHANGELOG
|
|
@ -1,266 +1,6 @@
|
|||
ChangeLog :
|
||||
===========
|
||||
|
||||
2026/06/03 : 3.5-dev0
|
||||
- MINOR: version: mention that it's development again
|
||||
|
||||
2026/06/03 : 3.4.0
|
||||
- BUG/MINOR: tcpcheck: Check LDAP response to not read more data than available
|
||||
- BUG/MINOR: ssl-gencert: validate SNI characters to prevent SAN certificate injection
|
||||
- BUG/MINOR: mux-h1: H2 preface rejection doesn't update stick-table glitches
|
||||
- BUG/MEDIUM: cpu-topo: Enforce thread-hard-limit on policy
|
||||
- BUG/MEDIUM: qmux: do not crash on too large record
|
||||
- BUG/MEDIUM: qmux: do not crash on receiving an invalid first frame
|
||||
- BUG/MINOR: qmux: reject too large initial record
|
||||
- Revert "BUG/MEDIUM: dns: fix long loops in additional records parse on name failure"
|
||||
- BUG/MINOR: qpack: Fix index calculation in debug functions
|
||||
- BUG/MINOR: qpack: fix potential null-pointer dereference in qpack_dht_insert()
|
||||
- CLEANUP: qpack: fix copy-paste typo in value Huffman debug string
|
||||
- BUG/MINOR: qpack: fix sign bit mask in qpack_decode_fs_pfx()
|
||||
- CLEANUP: qpack: fix copy-paste typo in value Huffman debug string for WLN
|
||||
- BUG/MINOR: qpack: fix huff_dec() error handling in qpack_decode_fs()
|
||||
- CLEANUP: qpack: move encoded macros to qpack-t.h to avoid duplication
|
||||
- BUG/MEDIUM: quic: handle ECONNREFUSED on RX side
|
||||
- BUG/MINOR: quic: Fix memory leak in quic_deallocate_dghdlrs()
|
||||
- BUG/MEDIUM: lua: defer Lua VM initialisation to the first Lua config keyword
|
||||
- REGTESTS: lua: fix tune.lua.openlibs in Lua reg-tests
|
||||
- BUG/MINOR: mux-h2: Count padding for connection flow control on error path
|
||||
- BUILD: addons: convert 51d addon to EXTRA_MAKE
|
||||
- BUILD: addons: convert deviceatlas addon to EXTRA_MAKE
|
||||
- BUILD: addons: convert WURFL addon to EXTRA_MAKE
|
||||
- MINOR: mux_quic/flags: add missing flags
|
||||
- BUG/MINOR: mux_quic: open an idle QCS on reset on BE side
|
||||
- BUG/MINOR: mux_quic: fix BE conn removal on app shutdown
|
||||
- BUG/MINOR: mux_quic: prevent BE reuse with an errored conn
|
||||
- BUG/MINOR: quic: fix ack range node pool_free call passing wrong pointer type
|
||||
- MEDIUM: quic: optimize HKDF operations by reusing per-thread contexts
|
||||
- BUG/MEDIUM: quic: reset cwnd in slow_start on persistent congestion (cubic)
|
||||
- BUG/MEDIUM: quic: reset consecutive_losses on exit from recovery period (cubic)
|
||||
- BUG/MINOR: quic: update drs->lost before calling on_ack_recv
|
||||
- Revert "MEDIUM: quic: optimize HKDF operations by reusing per-thread contexts"
|
||||
- BUG/MEDIUM: lua: register hlua_init() as a pre-check to fix crash without Lua config
|
||||
- REGTESTS: quic: disable quic/ocsp_auto_update for now
|
||||
- BUG/MINOR: threads: set at least grp_max when mtpg is too small
|
||||
- BUG/MEDIUM: threads: ignore max-threads-per-group when thread-groups is set
|
||||
- CLEANUP: thread: indicate when max-threads-per-group is ignored
|
||||
- MINOR: cpu-topo: notify when cpu-policy is ignored due to other settings
|
||||
- MINOR: thread: report when thread-groups or nbthread results in less threads
|
||||
- BUILD: makefile: include EXTRA_MAKE in the .build_opts construction
|
||||
- BUG/MINOR: quic: Fix another buffer overflow with sockaddr_in46
|
||||
- MINOR: quic: Copy sin6_flowinfo and sin6_scope_id too
|
||||
- BUILD: Makefile: put EXTRA_MAKE help at the right place
|
||||
- BUG/MINOR: cache: fix cache tree iteration
|
||||
- BUG/MEDIUM: resolvers: Wait a bit before calling the xprt prepare_srv
|
||||
- CLEANUP: addons/51degrees: initialize variables
|
||||
- MINOR: addons/51degrees: handle memory allocation failures
|
||||
- CLEANUP: ncbmbuf: improve handling of memory allocation errors in unit tests
|
||||
- CLEANUP: admin/halog: improve handling of memory allocation errors
|
||||
- DOC: internals: clarify ambiguous wording in core-principles
|
||||
- DOC: internals: add a threat model definition
|
||||
- DOC: add security.txt describing how to report security issues
|
||||
- DOC: security: also add a note to exclude dev/ and admin/
|
||||
- BUG/MEDIUM: qmux: Close connection on invalid frame
|
||||
- CLEANUP: fix comment typo
|
||||
- BUG/MEDIUM: h3: fix MAX_PUSH_ID handling
|
||||
- BUG/MINOR: cache: Fix copy of value when parsing maxage
|
||||
- BUG/MEDIUM: mux-h1: Dup connection/upgrade value to parse it when making headers
|
||||
- BUG/MEDIUM: htx: Fix headers rollback on partial copy in htx_xfer()
|
||||
- MINOR: deinit: release the in-memory copy of shared libs
|
||||
- MINOR: debug: add -dA to dump an archive of all dependencies
|
||||
- BUG/MEDIUM: ssl: Make sure the alpn length is small enough
|
||||
- BUG/MINOR: applet: Commit changes into input buffer after sending HTX data
|
||||
- BUG/MINOR: mux-spop: Fix possible off-by-one OOB read in spop_get_varint()
|
||||
- BUG/MEDIUM: leastconn: Unlock the write lock on allocation failure
|
||||
- BUG/MINOR: tasks: Increase the right niced_task counter
|
||||
- BUILD: makefile: search for Lua 5.5 as well
|
||||
- DEV: dev/gdb: improve ebtree pointer handling
|
||||
- DEV: dev/gdb: add simple task dump
|
||||
- DEV: dev/gdb: add simple thread dump
|
||||
- DEV: dev/gdb: add fdtab dump
|
||||
- DOC: config: add a few more explanation in http-reusee regarding sni-auto
|
||||
- REGTESTS: add basic QMux tests
|
||||
- BUG/MINOR: http-act: Properly handle final evaluation in pause action
|
||||
- BUILD: makefile/lua: use the system's default library before all other variants
|
||||
- BUG/MINOR: startup: unbreak chroot with CAP_SYS_CHROOT
|
||||
- BUG/MINOR: haterm: do not try to bind QUIC when not supported
|
||||
- BUG/MINOR: haterm: also apply the tcp-bind-opts to clear TCP "bind" lines
|
||||
- CLEANUP: haterm: do not try to bind to SSL when not built in
|
||||
- MINOR: haterm: enable ktls on the SSL bind line when supported
|
||||
- CI: github: replace cirrus by a vmactions/freebsd-vm job
|
||||
- BUILD: makefile: fix build error with GNU make 4.2.1 and /bin/dash
|
||||
- BUG/MEDIUM: channel: Fix condition to know if a channel may send
|
||||
- BUG/MEDIUM: vars: Properly eval set-var-fmt action for emtpy log-format string
|
||||
- CI: github: run illumos job weekly on Mondays at 03:00 instead of monthly
|
||||
- BUG/MEDIUM: stream: Don't use small buffer on queuing with a request data filter
|
||||
- BUG/MINOR: jwe: don't write randoms past MAX_DECRYPTED_CEK_LEN in RSA_PKCS1_PADDING
|
||||
- BUG/MEDIUM: chunk: do not rely on small trash by default for expressions
|
||||
- CLEANUP: map: always test pat->ref in sample_conv_map_key()
|
||||
- DEV: patchbot: prepare for new version 3.5-dev
|
||||
- MINOR: version: mention that it's 3.4 LTS now.
|
||||
|
||||
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
|
||||
- SCRIPTS: announce-release: add a link to the OpenTelemetry filter
|
||||
- BUG/MEDIUM: servers: Only requeue servers if they are up
|
||||
|
|
|
|||
14
INSTALL
14
INSTALL
|
|
@ -11,7 +11,7 @@ this task seriously and are doing a good job at backporting important fixes.
|
|||
|
||||
If for any reason you would prefer a different version than the one packaged
|
||||
for your system, you want to be certain to have all the fixes or to get some
|
||||
commercial support, other choices are available at https://www.haproxy.com/.
|
||||
commercial support, other choices are available at http://www.haproxy.com/.
|
||||
|
||||
|
||||
Areas covered in this document
|
||||
|
|
@ -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
|
||||
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
|
||||
may sometimes break due to compiler regressions or behaviour changes. The
|
||||
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
|
||||
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
|
||||
(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
|
||||
|
|
@ -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
|
||||
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
|
||||
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
|
||||
|
|
@ -426,9 +426,9 @@ Lua is an embedded programming language supported by HAProxy to provide more
|
|||
advanced scripting capabilities. Only versions 5.3 and above are supported.
|
||||
In order to enable Lua support, please specify "USE_LUA=1" on the command line.
|
||||
Some systems provide this library under various names to avoid conflicts with
|
||||
previous versions. By default, HAProxy looks for "lua5.5", "lua55", "lua5.4",
|
||||
"lua54", "lua5.3", "lua53", "lua". If your system uses a different naming, you
|
||||
may need to set the library name in the "LUA_LIB_NAME" variable.
|
||||
previous versions. By default, HAProxy looks for "lua5.4", "lua54", "lua5.3",
|
||||
"lua53", "lua". If your system uses a different naming, you may need to set the
|
||||
library name in the "LUA_LIB_NAME" variable.
|
||||
|
||||
If Lua is not provided on your system, it can be very simply built locally. It
|
||||
can be downloaded from https://www.lua.org/, extracted and built, for example :
|
||||
|
|
|
|||
81
Makefile
81
Makefile
|
|
@ -44,7 +44,6 @@
|
|||
# USE_CLOSEFROM : enable use of closefrom() on *bsd, solaris. Automatic.
|
||||
# USE_PRCTL : enable use of prctl(). 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_SLZ : enable slz library instead of zlib (default=enabled)
|
||||
# USE_CPU_AFFINITY : enable pinning processes to CPU on Linux. Automatic.
|
||||
|
|
@ -61,6 +60,7 @@
|
|||
# USE_OBSOLETE_LINKER : use when the linker fails to emit __start_init/__stop_init
|
||||
# USE_THREAD_DUMP : use the more advanced thread state dump system. Automatic.
|
||||
# USE_OT : enable the OpenTracing filter
|
||||
# EXTRA_MAKE : space-separated list of external addons using a Makefile.inc
|
||||
# USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only.
|
||||
# USE_LIBATOMIC : force to link with/without libatomic. Automatic.
|
||||
# USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours
|
||||
|
|
@ -94,7 +94,6 @@
|
|||
# SILENT_DEFINE may be used to specify other defines which will not be
|
||||
# reported by "haproxy -vv".
|
||||
# EXTRA is used to force building or not building some extra tools.
|
||||
# EXTRA_MAKE space-separated list of external addons using a Makefile.inc
|
||||
# DESTDIR is not set by default and is used for installation only.
|
||||
# It might be useful to set DESTDIR if you want to install haproxy
|
||||
# in a sandbox.
|
||||
|
|
@ -124,7 +123,7 @@
|
|||
# LUA_LIB : force the lib path to lua
|
||||
# LUA_INC : force the include path to lua
|
||||
# LUA_LIB_NAME : force the lib name (or automatically evaluated, by order of
|
||||
# priority: lua5.5, lua55, lua5.4, lua54, lua5.3, lua53, lua).
|
||||
# priority : lua5.4, lua54, lua5.3, lua53, lua).
|
||||
# OT_DEBUG : compile the OpenTracing filter in debug mode
|
||||
# OT_INC : force the include path to libopentracing-c-wrapper
|
||||
# OT_LIB : force the lib path to libopentracing-c-wrapper
|
||||
|
|
@ -344,7 +343,7 @@ use_opts = USE_EPOLL USE_KQUEUE USE_NETFILTER USE_POLL \
|
|||
USE_TPROXY USE_LINUX_TPROXY USE_LINUX_CAP \
|
||||
USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_ENGINE \
|
||||
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_CPU_AFFINITY USE_TFO USE_NS USE_DL USE_RT USE_LIBATOMIC \
|
||||
USE_MATH USE_DEVICEATLAS USE_51DEGREES \
|
||||
|
|
@ -367,9 +366,6 @@ $(warn_unknown_options)
|
|||
# on the make command line.
|
||||
USE_POLL = default
|
||||
|
||||
# traces are always enabled
|
||||
USE_TRACE = default
|
||||
|
||||
# SLZ is always supported unless explicitly disabled by passing USE_SLZ=""
|
||||
# or disabled by enabling ZLIB using USE_ZLIB=1
|
||||
ifeq ($(USE_ZLIB:0=),)
|
||||
|
|
@ -684,15 +680,15 @@ OPTIONS_OBJS += src/quic_openssl_compat.o
|
|||
endif
|
||||
|
||||
ifneq ($(USE_LUA:0=),)
|
||||
check_lua_inc = $(shell if [ ! -e /usr/include/lua.h -a -e $(2)$(1)/lua.h ]; then echo $(2)$(1); fi;)
|
||||
LUA_INC := $(firstword $(foreach lib,lua5.5 lua55 lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_inc,$(lib),"/usr/include/")))
|
||||
check_lua_inc = $(shell if [ -d $(2)$(1) ]; then echo $(2)$(1); fi;)
|
||||
LUA_INC := $(firstword $(foreach lib,lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_inc,$(lib),"/usr/include/")))
|
||||
|
||||
check_lua_lib = $(shell echo "int main(){}" | $(CC) $(if $(LUA_INC),-I$(LUA_INC)) -o /dev/null -x c - $(2) -l$(1) 2>/dev/null && echo $(1))
|
||||
check_lua_lib = $(shell echo "int main(){}" | $(CC) -o /dev/null -x c - $(2) -l$(1) 2>/dev/null && echo $(1))
|
||||
LUA_LD_FLAGS := -Wl,$(if $(EXPORT_SYMBOL),$(EXPORT_SYMBOL),--export-dynamic) $(if $(LUA_LIB),-L$(LUA_LIB))
|
||||
|
||||
# Try to automatically detect the Lua library if not set
|
||||
ifeq ($(LUA_LIB_NAME),)
|
||||
LUA_LIB_NAME := $(firstword $(foreach lib,lua lua5.5 lua55 lua5.4 lua54 lua5.3 lua53,$(call check_lua_lib,$(lib),$(LUA_LD_FLAGS))))
|
||||
LUA_LIB_NAME := $(firstword $(foreach lib,lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_lib,$(lib),$(LUA_LD_FLAGS))))
|
||||
endif
|
||||
|
||||
# Lua lib name must be set now (forced/detected above)
|
||||
|
|
@ -722,15 +718,70 @@ ifneq ($(USE_PROMEX:0=),)
|
|||
endif
|
||||
|
||||
ifneq ($(USE_DEVICEATLAS:0=),)
|
||||
EXTRA_MAKE += addons/deviceatlas
|
||||
# Use DEVICEATLAS_SRC and possibly DEVICEATLAS_INC and DEVICEATLAS_LIB to force path
|
||||
# to DeviceAtlas headers and libraries if needed. In this context, DEVICEATLAS_NOCACHE
|
||||
# can be used to disable the cache support if needed (this also removes the necessity of having
|
||||
# a C++ toolchain installed).
|
||||
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
|
||||
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
|
||||
include addons/deviceatlas/Makefile.inc
|
||||
OPTIONS_OBJS += addons/deviceatlas/da.o
|
||||
endif
|
||||
|
||||
# Use 51DEGREES_SRC and possibly 51DEGREES_INC and 51DEGREES_LIB to force path
|
||||
# to 51degrees v3/v4 headers and libraries if needed. Note that the SRC/INC/
|
||||
# LIB/CFLAGS/LDFLAGS variables names all use 51DEGREES as the prefix,
|
||||
# regardless of the version since they are mutually exclusive. The version
|
||||
# (51DEGREES_VER) must be either 3 or 4, and defaults to 3 if not set.
|
||||
51DEGREES_INC = $(51DEGREES_SRC)
|
||||
51DEGREES_LIB = $(51DEGREES_SRC)
|
||||
51DEGREES_VER = 3
|
||||
|
||||
ifneq ($(USE_51DEGREES:0=),)
|
||||
EXTRA_MAKE += addons/51degrees
|
||||
ifeq ($(51DEGREES_VER),4) # v4 here
|
||||
_51DEGREES_SRC = $(shell find $(51DEGREES_LIB) -maxdepth 2 -name '*.c')
|
||||
OPTIONS_OBJS += $(_51DEGREES_SRC:%.c=%.o)
|
||||
51DEGREES_CFLAGS += -DUSE_51DEGREES_V4
|
||||
ifeq ($(USE_THREAD:0=),)
|
||||
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING -DFIFTYONE_DEGREES_NO_THREADING
|
||||
endif
|
||||
USE_LIBATOMIC = implicit
|
||||
endif # 51DEGREES_VER==4
|
||||
|
||||
ifeq ($(51DEGREES_VER),3) # v3 here
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/../cityhash/city.o
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/51Degrees.o
|
||||
ifeq ($(USE_THREAD:0=),)
|
||||
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING
|
||||
else
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/../threading.o
|
||||
endif
|
||||
else
|
||||
ifneq ($(51DEGREES_VER),4)
|
||||
$(error 51Degrees version (51DEGREES_VER) must be either 3 or 4)
|
||||
endif
|
||||
endif # 51DEGREES_VER==3
|
||||
|
||||
OPTIONS_OBJS += addons/51degrees/51d.o
|
||||
51DEGREES_CFLAGS += $(if $(51DEGREES_INC),-I$(51DEGREES_INC))
|
||||
51DEGREES_LDFLAGS += $(if $(51DEGREES_LIB),-L$(51DEGREES_LIB))
|
||||
USE_MATH = implicit
|
||||
endif # USE_51DEGREES
|
||||
|
||||
ifneq ($(USE_WURFL:0=),)
|
||||
EXTRA_MAKE += addons/wurfl
|
||||
# Use WURFL_SRC and possibly WURFL_INC and WURFL_LIB to force path
|
||||
# to WURFL headers and libraries if needed.
|
||||
WURFL_INC = $(WURFL_SRC)
|
||||
WURFL_LIB = $(WURFL_SRC)
|
||||
OPTIONS_OBJS += addons/wurfl/wurfl.o
|
||||
WURFL_CFLAGS = $(if $(WURFL_INC),-I$(WURFL_INC))
|
||||
ifneq ($(WURFL_DEBUG),)
|
||||
WURFL_CFLAGS += -DWURFL_DEBUG
|
||||
endif
|
||||
ifneq ($(WURFL_HEADER_WITH_DETAILS),)
|
||||
WURFL_CFLAGS += -DWURFL_HEADER_WITH_DETAILS
|
||||
endif
|
||||
WURFL_LDFLAGS = $(if $(WURFL_LIB),-L$(WURFL_LIB)) -lwurfl
|
||||
endif
|
||||
|
||||
ifneq ($(USE_PCRE:0=)$(USE_STATIC_PCRE:0=)$(USE_PCRE_JIT:0=),)
|
||||
|
|
@ -1003,7 +1054,7 @@ IGNORE_OPTS=help install install-man install-doc install-bin \
|
|||
|
||||
ifneq ($(TARGET),)
|
||||
ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),)
|
||||
build_opts = $(shell rm -f .build_opts.new; echo \'$(TARGET) $(BUILD_OPTIONS) $(EXTRA_MAKE) $(VERBOSE_CFLAGS) $(WARN_CFLAGS) $(NOWARN_CFLAGS) $(DEBUG)\' > .build_opts.new; if cmp -s .build_opts .build_opts.new; then rm -f .build_opts.new; else mv -f .build_opts.new .build_opts; fi)
|
||||
build_opts = $(shell rm -f .build_opts.new; echo \'$(TARGET) $(BUILD_OPTIONS) $(VERBOSE_CFLAGS) $(WARN_CFLAGS) $(NOWARN_CFLAGS) $(DEBUG)\' > .build_opts.new; if cmp -s .build_opts .build_opts.new; then rm -f .build_opts.new; else mv -f .build_opts.new .build_opts; fi)
|
||||
.build_opts: $(build_opts)
|
||||
else
|
||||
.build_opts:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
[](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml)
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml)
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml)
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/freebsd.yml)
|
||||
[](https://cirrus-ci.com/github/haproxy/haproxy/)
|
||||
[](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml)
|
||||
|
||||

|
||||
|
|
|
|||
2
VERDATE
2
VERDATE
|
|
@ -1,2 +1,2 @@
|
|||
$Format:%ci$
|
||||
2026/06/03
|
||||
2026/05/13
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
3.5-dev0
|
||||
3.4-dev12
|
||||
|
|
|
|||
|
|
@ -127,16 +127,7 @@ static int _51d_property_name_list(char **args, int section_type, struct proxy *
|
|||
|
||||
while (*(args[cur_arg])) {
|
||||
name = calloc(1, sizeof(*name));
|
||||
if (!name) {
|
||||
memprintf(err, "'%s' failed to allocate memory.", args[0]);
|
||||
return -1;
|
||||
}
|
||||
name->name = strdup(args[cur_arg]);
|
||||
if (!name->name) {
|
||||
free(name);
|
||||
memprintf(err, "'%s' failed to allocate memory.", args[0]);
|
||||
return -1;
|
||||
}
|
||||
LIST_APPEND(&global_51degrees.property_names, &name->list);
|
||||
++cur_arg;
|
||||
}
|
||||
|
|
@ -312,7 +303,6 @@ static void _51d_init_device_offsets(fiftyoneDegreesDeviceOffsets *offsets) {
|
|||
|
||||
static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets)
|
||||
{
|
||||
struct buffer *temp = get_trash_chunk();
|
||||
struct channel *chn;
|
||||
struct htx *htx;
|
||||
struct http_hdr_ctx ctx;
|
||||
|
|
@ -334,15 +324,7 @@ static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOff
|
|||
|
||||
if (http_find_header(htx, name, &ctx, 1)) {
|
||||
(offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i);
|
||||
/* Copy value into trash and NUL-terminate before passing to the
|
||||
* 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->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, ctx.value.ptr);
|
||||
offsets->size++;
|
||||
}
|
||||
}
|
||||
|
|
@ -937,10 +919,6 @@ static int init_51degrees(void)
|
|||
list_for_each_entry(name, &global_51degrees.property_names, list)
|
||||
++i;
|
||||
_51d_property_list = calloc(i, sizeof(*_51d_property_list));
|
||||
if (!_51d_property_list) {
|
||||
ha_alert("51Degrees: Failed to allocate property list.\n");
|
||||
return (ERR_FATAL | ERR_ALERT);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(name, &global_51degrees.property_names, list)
|
||||
|
|
@ -1075,7 +1053,7 @@ static int init_51degrees(void)
|
|||
|
||||
static void deinit_51degrees(void)
|
||||
{
|
||||
struct _51d_property_names *_51d_prop_name = NULL, *_51d_prop_nameb = NULL;
|
||||
struct _51d_property_names *_51d_prop_name, *_51d_prop_nameb;
|
||||
|
||||
#if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED)
|
||||
free(global_51degrees.header_names);
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
# Use 51DEGREES_SRC and possibly 51DEGREES_INC and 51DEGREES_LIB to force path
|
||||
# to 51degrees v3/v4 headers and libraries if needed. Note that the SRC/INC/
|
||||
# LIB/CFLAGS/LDFLAGS variables names all use 51DEGREES as the prefix,
|
||||
# regardless of the version since they are mutually exclusive. The version
|
||||
# (51DEGREES_VER) must be either 3 or 4, and defaults to 3 if not set.
|
||||
51DEGREES_INC = $(51DEGREES_SRC)
|
||||
51DEGREES_LIB = $(51DEGREES_SRC)
|
||||
51DEGREES_VER = 3
|
||||
|
||||
ifeq ($(51DEGREES_VER),4) # v4 here
|
||||
_51DEGREES_SRC = $(shell find $(51DEGREES_LIB) -maxdepth 2 -name '*.c')
|
||||
OPTIONS_OBJS += $(_51DEGREES_SRC:%.c=%.o)
|
||||
51DEGREES_CFLAGS += -DUSE_51DEGREES_V4
|
||||
ifeq ($(USE_THREAD:0=),)
|
||||
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING -DFIFTYONE_DEGREES_NO_THREADING
|
||||
endif
|
||||
USE_LIBATOMIC = implicit
|
||||
endif # 51DEGREES_VER==4
|
||||
|
||||
ifeq ($(51DEGREES_VER),3) # v3 here
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/../cityhash/city.o
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/51Degrees.o
|
||||
ifeq ($(USE_THREAD:0=),)
|
||||
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING
|
||||
else
|
||||
OPTIONS_OBJS += $(51DEGREES_LIB)/../threading.o
|
||||
endif
|
||||
else
|
||||
ifneq ($(51DEGREES_VER),4)
|
||||
$(error 51Degrees version (51DEGREES_VER) must be either 3 or 4)
|
||||
endif
|
||||
endif # 51DEGREES_VER==3
|
||||
|
||||
OPTIONS_OBJS += addons/51degrees/51d.o
|
||||
51DEGREES_CFLAGS += $(if $(51DEGREES_INC),-I$(51DEGREES_INC))
|
||||
51DEGREES_LDFLAGS += $(if $(51DEGREES_LIB),-L$(51DEGREES_LIB))
|
||||
USE_MATH = implicit
|
||||
|
|
@ -1,13 +1,6 @@
|
|||
# DEVICEATLAS_SRC : DeviceAtlas API source root path
|
||||
|
||||
|
||||
# Use DEVICEATLAS_SRC and possibly DEVICEATLAS_INC and DEVICEATLAS_LIB to force path
|
||||
# to DeviceAtlas headers and libraries if needed. In this context, DEVICEATLAS_NOCACHE
|
||||
# can be used to disable the cache support if needed (this also removes the necessity of having
|
||||
# a C++ toolchain installed).
|
||||
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
|
||||
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
|
||||
|
||||
CXX := c++
|
||||
CXXLIB := -lstdc++
|
||||
|
||||
|
|
@ -35,7 +28,5 @@ OPTIONS_OBJS += $(DEVICEATLAS_SRC)/dadwcurl.o
|
|||
OPTIONS_OBJS += $(DEVICEATLAS_SRC)/Os/daunix.o
|
||||
endif
|
||||
|
||||
OPTIONS_OBJS += addons/deviceatlas/da.o
|
||||
|
||||
addons/deviceatlas/dummy/%.o: addons/deviceatlas/dummy/%.cpp
|
||||
$(cmd_CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# Use WURFL_SRC and possibly WURFL_INC and WURFL_LIB to force path
|
||||
# to WURFL headers and libraries if needed.
|
||||
WURFL_INC = $(WURFL_SRC)
|
||||
WURFL_LIB = $(WURFL_SRC)
|
||||
OPTIONS_OBJS += addons/wurfl/wurfl.o
|
||||
WURFL_CFLAGS = $(if $(WURFL_INC),-I$(WURFL_INC))
|
||||
ifneq ($(WURFL_DEBUG),)
|
||||
WURFL_CFLAGS += -DWURFL_DEBUG
|
||||
endif
|
||||
ifneq ($(WURFL_HEADER_WITH_DETAILS),)
|
||||
WURFL_CFLAGS += -DWURFL_HEADER_WITH_DETAILS
|
||||
endif
|
||||
WURFL_LDFLAGS = $(if $(WURFL_LIB),-L$(WURFL_LIB)) -lwurfl
|
||||
|
|
@ -1801,8 +1801,6 @@ void filter_count_ip(const char *source_field, const char *accept_field, const c
|
|||
*/
|
||||
if (unlikely(!ustat))
|
||||
ustat = calloc(1, sizeof(*ustat));
|
||||
if (!ustat)
|
||||
return;
|
||||
|
||||
ustat->nb_err = err;
|
||||
ustat->nb_req = 1;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ end
|
|||
# returns $node filled with the first node of ebroot $arg0
|
||||
define ebtree_first
|
||||
# browse ebtree left until encountering leaf
|
||||
set $node = (struct eb_node *)((struct eb_root*)$arg0)->b[0]
|
||||
set $node = (struct eb_node *)$arg0->b[0]
|
||||
while 1
|
||||
_ebtree_set_tag_node $node
|
||||
if $tag == 0
|
||||
|
|
@ -41,7 +41,7 @@ end
|
|||
# finds next ebtree node after $arg0, and returns it in $node
|
||||
define ebtree_next
|
||||
# get parent
|
||||
set $node = (struct eb_root *)((struct eb_node *)$arg0)->leaf_p
|
||||
set $node = (struct eb_root *)$arg0->leaf_p
|
||||
# Walking up from right branch, so we cannot be below root
|
||||
# while (eb_gettag(t) != EB_LEFT) // #define EB_LEFT 0
|
||||
while 1
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
# list info about open FD
|
||||
define fd_dump
|
||||
set $f = 0
|
||||
while $f < $g.maxsock
|
||||
if fdtab[$f].owner != 0
|
||||
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx cb=%p ownr=%p st=%#x refc=%#x tkov=%u gen=%u\n", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].iocb, fdtab[$f].owner, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation
|
||||
end
|
||||
set $f = $f + 1
|
||||
end
|
||||
end
|
||||
|
||||
# only those attached to a listener
|
||||
define fd_dump_listener
|
||||
set $f = 0
|
||||
while $f < $g.maxsock
|
||||
if fdtab[$f].owner != 0 && fdtab[$f].iocb == &sock_accept_iocb
|
||||
set $c = (struct listener *)fdtab[$f].owner
|
||||
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx st=%#x refc=%#x tkov=%u gen=%u listener=%p(%s): flg=%#x state=%d fe=%p(%s) acc=%p\n", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation, fdtab[$f].owner, $c->name, $c->flags, $c->state, $c->bind_conf.frontend, $c->bind_conf.frontend.id, $c->bind_conf.accept
|
||||
end
|
||||
set $f = $f + 1
|
||||
end
|
||||
end
|
||||
|
||||
# only those attached to a connection
|
||||
define fd_dump_conn
|
||||
set $f = 0
|
||||
while $f < $g.maxsock
|
||||
if fdtab[$f].owner != 0 && fdtab[$f].iocb == &sock_conn_iocb
|
||||
set $c = (struct connection *)fdtab[$f].owner
|
||||
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx st=%#x refc=%#x tkov=%u gen=%u conn=%p: flg=%#x err=%#x ctrl=%p xprt=%p mux=%p", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation, fdtab[$f].owner, $c->flags, $c->err_code, $c->ctrl, $c->xprt, $c->mux
|
||||
if *$c->target == OBJ_TYPE_LISTENER
|
||||
set $s = (struct session *)$c->owner
|
||||
printf " sess=%p: fe=%p id=%s age=%dms", $s, $s->fe, $s->fe->id, (*global_now_ns - $s->accept_ts) / 1000000
|
||||
end
|
||||
printf "\n"
|
||||
end
|
||||
set $f = $f + 1
|
||||
end
|
||||
end
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# lists all tasks in the wait queue whose ebroot pointed to by $arg0
|
||||
# e.g.
|
||||
# task_dump_wq &ha_tgroup_ctx[0].timers
|
||||
# task_dump_wq &ha_thread_ctx[0].timers
|
||||
#
|
||||
define task_dump_rq
|
||||
set $tot=0
|
||||
ebtree_first ($arg0)
|
||||
while ($node != 0)
|
||||
set $tot = $tot+1
|
||||
set $p = (struct task *)((void*)$node-(long)&((struct task*)0).rq)
|
||||
printf "task %p ",$p
|
||||
p -pretty off -- /a *$p
|
||||
ebtree_next $node
|
||||
end
|
||||
printf "Total: %d tasks.\n",$tot
|
||||
end
|
||||
|
||||
define task_dump_wq
|
||||
set $tot=0
|
||||
ebtree_first ($arg0)
|
||||
while ($node != 0)
|
||||
set $tot = $tot+1
|
||||
set $p = (struct task *)((void*)$node-(long)&((struct task*)0).wq)
|
||||
printf "task %p ",$p
|
||||
p -pretty off -- /a *$p
|
||||
ebtree_next $node
|
||||
end
|
||||
printf "Total: %d tasks.\n",$tot
|
||||
end
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
# list info about current threads (ptr, now_ms, queue, current)
|
||||
define thread_dump
|
||||
set $t = 0
|
||||
while $t < $g.nbthread
|
||||
set $i = $ti[$t].pth_id
|
||||
set $h = $tc[$t].current
|
||||
printf "Tid %4d: pth=%p mono=%llu now_ms=%u fl=0x%02x rq=%d cq=%d current=%p\n", $t, $i, $tc[$t].curr_mono_time, (unsigned)(($tc[$t].curr_mono_time + now_offset)/1000000), $tc[$t].flags, $tc[$t].current_queue, $tc[$t].rq_total, $h
|
||||
set $t = $t + 1
|
||||
end
|
||||
end
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
BEGININPUT
|
||||
BEGINCONTEXT
|
||||
|
||||
HAProxy's development cycle consists in one development branch, and multiple
|
||||
maintenance branches.
|
||||
|
||||
All the development is made into the development branch exclusively. This
|
||||
includes mostly new features, doc updates, cleanups and or course, fixes.
|
||||
|
||||
The maintenance branches, also called stable branches, never see any
|
||||
development, and only receive ultra-safe fixes for bugs that affect them,
|
||||
that are picked from the development branch.
|
||||
|
||||
Branches are numbered in 0.1 increments. Every 6 months, upon a new major
|
||||
release, the development branch enters maintenance and a new development branch
|
||||
is created with a new, higher version. The current development branch is
|
||||
3.5-dev, and maintenance branches are 3.4 and below.
|
||||
|
||||
Fixes created in the development branch for issues that were introduced in an
|
||||
earlier branch are applied in descending order to each and every version till
|
||||
that branch that introduced the issue: 3.4 first, then 3.2, then 3.1, then 3.0
|
||||
and so on. This operation is called "backporting". A fix for an issue is never
|
||||
backported beyond the branch that introduced the issue. An important point is
|
||||
that the project maintainers really aim at zero regression in maintenance
|
||||
branches, so they're never willing to take any risk backporting patches that
|
||||
are not deemed strictly necessary.
|
||||
|
||||
Fixes consist of patches managed using the Git version control tool and are
|
||||
identified by a Git commit ID and a commit message. For this reason we
|
||||
indistinctly talk about backporting fixes, commits, or patches; all mean the
|
||||
same thing. When mentioning commit IDs, developers always use a short form
|
||||
made of the first 8 characters only, and expect the AI assistant to do the
|
||||
same.
|
||||
|
||||
It seldom happens that some fixes depend on changes that were brought by other
|
||||
patches that were not in some branches and that will need to be backported as
|
||||
well for the fix to work. In this case, such information is explicitly provided
|
||||
in the commit message by the patch's author in natural language.
|
||||
|
||||
Developers are serious and always indicate if a patch needs to be backported.
|
||||
Sometimes they omit the exact target branch, or they will say that the patch is
|
||||
"needed" in some older branch, but it means the same. If a commit message
|
||||
doesn't mention any backport instructions, it means that the commit does not
|
||||
have to be backported. And patches that are not strictly bug fixes nor doc
|
||||
improvements are normally not backported. For example, fixes for design
|
||||
limitations, architectural improvements and performance optimizations are
|
||||
considered too risky for a backport. Finally, all bug fixes are tagged as
|
||||
"BUG" at the beginning of their subject line. Patches that are not tagged as
|
||||
such are not bugs, and must never be backported unless their commit message
|
||||
explicitly requests so.
|
||||
|
||||
ENDCONTEXT
|
||||
|
||||
A developer is reviewing the development branch, trying to spot which commits
|
||||
need to be backported to maintenance branches. This person is already expert
|
||||
on HAProxy and everything related to Git, patch management, and the risks
|
||||
associated with backports, so he doesn't want to be told how to proceed nor to
|
||||
review the contents of the patch.
|
||||
|
||||
The goal for this developer is to get some help from the AI assistant to save
|
||||
some precious time on this tedious review work. In order to do a better job, he
|
||||
needs an accurate summary of the information and instructions found in each
|
||||
commit message. Specifically he needs to figure if the patch fixes a problem
|
||||
affecting an older branch or not, if it needs to be backported, if so to which
|
||||
branches, and if other patches need to be backported along with it.
|
||||
|
||||
The indented text block below after an "id" line and starting with a Subject line
|
||||
is a commit message from the HAProxy development branch that describes a patch
|
||||
applied to that branch, starting with its subject line, please read it carefully.
|
||||
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
|
||||
ENDINPUT
|
||||
BEGININSTRUCTION
|
||||
|
||||
You are an AI assistant that follows instruction extremely well. Help as much
|
||||
as you can, responding to a single question using a single response.
|
||||
|
||||
The developer wants to know if he needs to backport the patch above to fix
|
||||
maintenance branches, for which branches, and what possible dependencies might
|
||||
be mentioned in the commit message. Carefully study the commit message and its
|
||||
backporting instructions if any (otherwise it should probably not be backported),
|
||||
then provide a very concise and short summary that will help the developer decide
|
||||
to backport it, or simply to skip it.
|
||||
|
||||
Start by explaining in one or two sentences what you recommend for this one and why.
|
||||
Finally, based on your analysis, give your general conclusion as "Conclusion: X"
|
||||
where X is a single word among:
|
||||
- "yes", if you recommend to backport the patch right now either because
|
||||
it explicitly states this or because it's a fix for a bug that affects
|
||||
a maintenance branch (3.4 or lower);
|
||||
- "wait", if this patch explicitly mentions that it must be backported, but
|
||||
only after waiting some time.
|
||||
- "no", if nothing clearly indicates a necessity to backport this patch (e.g.
|
||||
lack of explicit backport instructions, or it's just an improvement);
|
||||
- "uncertain" otherwise for cases not covered above
|
||||
|
||||
ENDINSTRUCTION
|
||||
|
||||
Explanation:
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
HAProxy
|
||||
Configuration Manual
|
||||
----------------------
|
||||
version 3.5
|
||||
2026/06/03
|
||||
version 3.4
|
||||
2026/05/13
|
||||
|
||||
|
||||
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
|
||||
"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
|
||||
dropping privileges. This increases the security level in case an unknown
|
||||
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>
|
||||
is both empty and non-writable to anyone. When the process is started with
|
||||
superuser privileges, the chroot() is performed directly. On Linux, when
|
||||
started unprivileged, haproxy attempts to perform it from inside a new
|
||||
user namespace created with unshare(CLONE_NEWUSER); if that mechanism is
|
||||
unavailable the chroot() will fail with the usual error.
|
||||
|
||||
As a special case, <jail dir> may be set to "auto", in which case haproxy
|
||||
creates an anonymous temporary directory, unlinks it, and chroots into it.
|
||||
The resulting jail has no name in the filesystem and is empty and read-only,
|
||||
removing the need to prepare a dedicated jail directory.
|
||||
|
||||
When starting with superuser privileges, a warning will be displayed if no
|
||||
chroot is used, in order to encourage users to always use the mechanism. If
|
||||
for any reason there is a compelling reason not to use chroot (e.g. access to
|
||||
a server via a UNIX socket with an unconvenient path), it remains possible to
|
||||
silence the warning by adding an explicit "chroot /", which has the benefit
|
||||
of being visible in a configuration.
|
||||
attacker to exploit the system. This only works when the process is started
|
||||
with superuser privileges. It is important to ensure that <jail_dir> is both
|
||||
empty and non-writable to anyone.
|
||||
|
||||
close-spread-time <time>
|
||||
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",
|
||||
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
|
||||
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
|
||||
|
|
@ -3345,7 +3330,7 @@ shm-stats-file <name>
|
|||
|
||||
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
|
||||
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
|
||||
|
|
@ -4940,8 +4925,8 @@ tune.lua.openlibs [all | none | <lib>[,<lib>...]]
|
|||
tune.lua.openlibs string,math,table,utf8 # safe subset, no I/O or OS
|
||||
tune.lua.openlibs all # default, load everything
|
||||
|
||||
This setting must be set before any "lua-load", "lua-load-per-thread" or
|
||||
"lua-prepend-path" directive, otherwise a parse error is returned.
|
||||
This setting must be set before any "lua-load" or "lua-load-per-thread"
|
||||
directive, otherwise a parse error is returned.
|
||||
|
||||
tune.lua.service-timeout <timeout>
|
||||
This is the execution timeout for the Lua services. This is useful for
|
||||
|
|
@ -5321,26 +5306,17 @@ tune.quic.frontend.stream-data-ratio <0..100, in percent> (deprecated)
|
|||
|
||||
tune.quic.be.stream.max-concurrent <number>
|
||||
tune.quic.fe.stream.max-concurrent <number>
|
||||
On frontend side, this is used as the value for the advertised
|
||||
initial_max_streams_bidi transport parameter. This is enforced as the maximum
|
||||
number of bidirectional streams that the remote peer will be authorized to
|
||||
open concurrently during the connection lifetime. This effectively limits the
|
||||
number of concurrent HTTP/3 client requests.
|
||||
Sets the QUIC initial_max_streams_bidi transport parameter either on frontend
|
||||
or backend side. This is the maximum number of bidirectional streams that the
|
||||
remote peer will be authorized to open concurrently during the connection
|
||||
lifetime. On frontend side, this limits the number of concurrent HTTP/3
|
||||
client requests.
|
||||
|
||||
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
|
||||
upload throughput. It can be corrected by increasing the QUIC stream rxbuf
|
||||
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",
|
||||
"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
|
||||
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,
|
||||
provides a practical indicator of a sustainable target load and helps avoid
|
||||
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
|
||||
useful to manually hash the key using a converter for that purpose
|
||||
and let haproxy use the result directly. The operation will
|
||||
convert the key to a string if it is not already, and parse it as
|
||||
an integer whose value will be used as the key. Some input key
|
||||
types might not be relevant here (e.g. IP addresses).
|
||||
and let haproxy use the result directly.
|
||||
|
||||
<modifier> indicates an optional method applied after hashing the key :
|
||||
|
||||
|
|
@ -9105,9 +9068,7 @@ http-reuse { never | safe | aggressive | always }
|
|||
- proxy protocol
|
||||
- TOS and mark socket options
|
||||
- connection name, determined either by the result of the evaluation of the
|
||||
"pool-conn-name" expression if present, otherwise by the "sni" expression,
|
||||
which defaults to "req.hdr(host),field(1,:)", i.e. uses the incoming
|
||||
request's "Host" header field without the colon nor the port number.
|
||||
"pool-conn-name" expression if present, otherwise by the "sni" expression
|
||||
|
||||
In some occasions, connection lookup or reuse is not performed due to extra
|
||||
restrictions. This is determined by the reuse strategy specified via the
|
||||
|
|
@ -9181,26 +9142,6 @@ http-reuse { never | safe | aggressive | always }
|
|||
too few connections are kept open. It may be desirable in this case to adjust
|
||||
such thresholds or simply to increase the global "maxconn" value.
|
||||
|
||||
In some rare cases, when the host name is used to distinguish outgoing TLS
|
||||
connections (e.g. forward proxy), where most request target different hosts,
|
||||
the reuse rate will be very low, and the automatic eviction of rarely used
|
||||
connections will kick in before connections have a chance to be reused,
|
||||
because the mechanism continuously measures the average number of connections
|
||||
needed to deliver the service without exhausting resources. In such
|
||||
situations, setting "pool-low-conn" to a value close to the average expected
|
||||
number of idle connections may help preserve more connections by encouraging
|
||||
threads to setup their own instead of trying to pick other threads' and
|
||||
shrinking the pool of available connections.
|
||||
|
||||
If a locally hosted server uses a single certificate (with multiple host
|
||||
names or wildcards) and operates multiple sites, it may be more effective to
|
||||
just use "no-sni-auto" on the "server" line to avoid reserving a connection
|
||||
to a single Host name. This will significantly increase the reuse rate. Some
|
||||
servers might perform excessive checks between Host and SNI though, resulting
|
||||
in rejecting subsequent requests, so this option requires preliminary
|
||||
validation. The default behavior ("sni-auto") is to be safe even with such
|
||||
servers.
|
||||
|
||||
When thread groups are explicitly enabled, it is important to understand that
|
||||
idle connections are only usable between threads from a same group. As such
|
||||
it may happen that unfair load between groups leads to more idle connections
|
||||
|
|
@ -9984,7 +9925,7 @@ no option accept-unsafe-violations-in-http-request
|
|||
When this option is set, the following rules are observed:
|
||||
|
||||
* 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;
|
||||
|
||||
|
|
@ -10049,11 +9990,8 @@ no option accept-unsafe-violations-in-http-response
|
|||
|
||||
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
|
||||
will not be rejected; however the header will be dropped.
|
||||
will be accepted;
|
||||
|
||||
* In H1 only, NULL character in header value will be accepted;
|
||||
|
||||
|
|
@ -18909,21 +18847,6 @@ hash-key <key>
|
|||
better only use values comprised between 1 and this value to
|
||||
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
|
||||
available, or else fall back on "id".
|
||||
|
||||
|
|
@ -18953,13 +18876,9 @@ healthcheck <name>
|
|||
id <value>
|
||||
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
|
||||
and unique for the proxy. An unused ID will automatically be assigned if
|
||||
unset. The first assigned value will be 1. This ID is currently only returned
|
||||
in statistics, and is used to place LB nodes when using consistent hash
|
||||
algorithms when "hash-key" is set to "id" (the default). In this case, only
|
||||
the 28 lowest bits of the value are used (i.e. (id % 268435356)), so better
|
||||
only use values comprised between 1 and this value to avoid overlap.
|
||||
Set a persistent ID for the server. This ID must be positive and unique for
|
||||
the proxy. An unused ID will automatically be assigned if unset. The first
|
||||
assigned value will be 1. This ID is currently only returned in statistics.
|
||||
|
||||
idle-ping <delay>
|
||||
May be used in the following contexts: tcp, http, log
|
||||
|
|
@ -19053,7 +18972,7 @@ downinter <delay>
|
|||
"inter" setting will have a very limited effect as it will not be able to
|
||||
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 sections : defaults | frontend | listen | backend
|
||||
|
|
@ -19061,25 +18980,20 @@ init-state { fully-up | up | down | fully-down | none }
|
|||
|
||||
The "init-state" option sets the initial state of the server:
|
||||
- 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
|
||||
the DOWN state when ALL health checks fail.
|
||||
- when set to 'up', the server is considered immediately available and, if
|
||||
health checks are enabled for this server, it will be turned to the DOWN
|
||||
state immediately if the next health check fails.
|
||||
- when set to 'down', the server initially is considered unavailable and,
|
||||
if health checks are enabled for this server, it can be turned to the UP
|
||||
state if the next health check succeeds.
|
||||
and can turn to the DOWN state when ALL health checks fail.
|
||||
- when set to 'up' (the default), the server is considered immediately
|
||||
available and will initiate a health check that can turn it to the DOWN
|
||||
state immediately if it fails.
|
||||
- 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 it succeeds.
|
||||
- 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
|
||||
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.
|
||||
and can turn to the UP state when ALL health checks succeed.
|
||||
|
||||
The server's init-state is considered when the HAProxy instance is
|
||||
(re)started, a new server is detected (for example via service discovery /
|
||||
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:
|
||||
# pass client traffic ONLY to Redis "master" node
|
||||
|
|
@ -19921,13 +19835,11 @@ sni-auto
|
|||
May be used in the following contexts: tcp, http, log, peers, ring
|
||||
|
||||
The "sni-auto" parameter enables the automatic SNI selection, if no value was
|
||||
already set. It sets the "sni" expression to "req.hdr(host),field(1,:)",
|
||||
which means that an SNI will be presented with the Host name of the request
|
||||
that is being sent to the server, but dropping the port number. It is enabled
|
||||
by default but this parameter may be used as "server" setting to reset any
|
||||
"no-sni-auto" setting which would have been inherited from "default-server"
|
||||
directive as default value. It may also be used as "default-server" setting
|
||||
to reset any previous "default-server" "no-sni-auto" setting.
|
||||
already set. It is enabled by default but this parameter may be used as
|
||||
"server" setting to reset any "no-sni-auto" setting which would have been
|
||||
inherited from "default-server" directive as default value. It may also be
|
||||
used as "default-server" setting to reset any previous "default-server"
|
||||
"no-sni-auto" setting.
|
||||
|
||||
For HTTPS connections, the selected SNI is based on the request host header
|
||||
value, if found. Otherwise it remains unset. For other protocols, the option
|
||||
|
|
@ -20277,11 +20189,7 @@ a cache of previous answers, an answer will be considered obsolete after
|
|||
|
||||
|
||||
resolvers <resolvers id>
|
||||
Creates a new name server list labeled <resolvers id>. As mentioned above,
|
||||
the special name "default" always exists and will be automatically created if
|
||||
not explicitly declared; this will be the one internal services such as
|
||||
httpclient rely on. Declaring a "default" entry will affect how such services
|
||||
perform their name resolution.
|
||||
Creates a new name server list labeled <resolvers id>
|
||||
|
||||
A resolvers section accept the following parameters:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,238 +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 is always the same size as long, but its underlying type is often
|
||||
uint on 32-bit and ulong on 64-bit. This is a frequent source of build
|
||||
errors on 32-bit platforms (e.g. passing a size_t where a long* is
|
||||
expected, or printing one with "%lu"); always cast in printf() (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: memory obtained from one
|
||||
must not be released using the other's free function.
|
||||
- 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. ncbuf mandates holes of at least 8 bytes, while ncbmbuf 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 rotating thread-local trash chunks. Since almost
|
||||
any function may itself call get_trash_chunk(), a returned chunk is only
|
||||
guaranteed valid until the next call into another function and must not be
|
||||
held across such a call. The rotation lets a single function safely use up
|
||||
to 3 distinct chunks at once for its own data manipulation.
|
||||
- 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 is available on some platforms but requires an equivalent
|
||||
fallback on the others (possibly a more complex operation, e.g. emulation
|
||||
using two or more CAS).
|
||||
- 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 against 0 the return of functions that yield an integer
|
||||
which is not a boolean (e.g. strcmp), unless they 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.
|
||||
|
|
@ -1,233 +0,0 @@
|
|||
HAProxy Threat Model & Trust Boundaries
|
||||
|
||||
This document defines the security boundaries of HAProxy, explicitly outlining
|
||||
what does and does not constitute a security vulnerability. Its purpose is to
|
||||
give reporters, developers and reviewers a single, predictable basis for
|
||||
judging an issue's real-world impact.
|
||||
|
||||
The project's strong preference is to fix issues quickly and in the open.
|
||||
Public handling gets fixes to users sooner and spares the ecosystem
|
||||
(distributions in particular) the heavy cost of embargo coordination, which in
|
||||
practice has rarely served users. Private, coordinated disclosure is reserved
|
||||
for the few cases whose real-world impact genuinely warrants it, judged from
|
||||
the severity ordering (section 6) and the mitigations (section 5). An issue
|
||||
that is technically in scope but contained in practice does not, by itself,
|
||||
call for an embargo.
|
||||
|
||||
These boundaries apply strictly to officially supported, documented builds
|
||||
running under a sane, production-ready configuration. Security guarantees are
|
||||
explicitly voided when using opt-in unsafe knobs, undocumented behavior, or
|
||||
experimental features. A configuration that merely lacks a recommended
|
||||
hardening step (for instance, no chroot) does not by itself move a
|
||||
client-triggered bug out of scope; the missing mitigation only widens the
|
||||
blast radius (sections 5 and 6).
|
||||
|
||||
1. ASSETS TO PROTECT
|
||||
HAProxy sits on the critical path of the services it fronts, so its
|
||||
availability and the integrity and confidentiality of the configuration and
|
||||
secrets it holds are all essential to protect. The assets below are not
|
||||
ranked here; their relative severity is ranked in section 6.
|
||||
- Integrity and confidentiality of the host and configuration: a compromise
|
||||
of the network-facing worker must not extend to the filesystem, nor to the
|
||||
configuration and its dependencies (private keys, Lua scripts, maps,
|
||||
crt-lists, ACLs). On a properly configured system the default structural
|
||||
mitigations prevent this, leaving only a compromise of the master process
|
||||
as a residual path (see section 5).
|
||||
- Confidentiality of long-lived secrets: TLS private keys and certificates
|
||||
above all. Unlike transient client data, their disclosure is permanent and
|
||||
systemic (impersonation and traffic decryption until every key is rotated
|
||||
and revoked).
|
||||
- Availability of the proxied service: being on the critical path, keeping
|
||||
HAProxy serving is paramount. A small, cheap amount of attacker input
|
||||
must neither consume a disproportionate amount of CPU or memory
|
||||
(asymmetric DoS, see section 3) nor crash or stall the process.
|
||||
- Confidentiality and isolation of client data: data belonging to one
|
||||
connection, stream or client must never leak to another, and process
|
||||
memory (including uninitialized memory) must never leak to a client.
|
||||
- Process integrity (memory safety): no RCE, memory corruption or undefined
|
||||
behaviour (UB) reachable from untrusted input.
|
||||
- Correct enforcement of the configured policy: access controls, routing and
|
||||
header manipulations decided by the configuration must not be bypassable
|
||||
by crafted input.
|
||||
|
||||
2. ATTACKER AND ENTRY POINTS
|
||||
- The reference attacker is an untrusted client able to send arbitrary
|
||||
bytes to a frontend: raw TCP payloads, HTTP/1, HTTP/2 and HTTP/3 (QUIC)
|
||||
traffic, and arbitrary TLS handshake records.
|
||||
- Entry points in scope are therefore the listeners and everything that
|
||||
parses or transforms client-supplied data: TLS, the HTTP muxes, HTX,
|
||||
header/URL processing, sample fetches and converters acting on request
|
||||
data, stick-tables fed by client data, the cache, and the QUIC/H3 stack.
|
||||
- A secondary untrusted source is the DNS resolver path: even though
|
||||
nameservers are configured, their answers arrive over UDP and can be
|
||||
spoofed by an off-path attacker, so the response parser handles
|
||||
attacker-influenced input.
|
||||
|
||||
3. WHAT QUALIFIES AS A SECURITY BUG (IN SCOPE)
|
||||
- Memory-safety issues (overflow, out-of-bounds, use-after-free, type
|
||||
confusion, UB) reachable from untrusted client input.
|
||||
- Cross-client or cross-stream effects: HTTP request smuggling, response
|
||||
splitting, cache poisoning, and any mixing of data between concurrent
|
||||
streams or connections (notably in the H2/H3 multiplexers).
|
||||
- Disclosure of process memory or of another client's data to a client.
|
||||
- Bypass of a policy that the configuration is meant to enforce (e.g.
|
||||
defeating an http-request deny/acl through request crafting).
|
||||
- Asymmetric / algorithmic denial of service: a single or a few cheap
|
||||
requests causing disproportionate CPU or memory usage (hash-collision
|
||||
flooding, catastrophic regex backtracking, quadratic parsing, unbounded
|
||||
allocation, etc). This is distinct from volumetric DoS (see 4).
|
||||
- Misuse of a third-party library on untrusted input: feeding malformed
|
||||
client data into OpenSSL, PCRE, Lua, zlib, etc. in a way that corrupts
|
||||
memory or crashes the process is in scope. A vulnerability inside the
|
||||
library itself is handled by that library's project, not here.
|
||||
- Mishandling of spoofable DNS responses: memory corruption, crashes or
|
||||
cache/state poisoning in the resolver caused by a crafted DNS answer are
|
||||
in scope, despite nameservers being nominally trusted (see section 2).
|
||||
|
||||
4. WHAT DOES NOT QUALIFY (OUT OF SCOPE)
|
||||
The following do not fall into the security-bug category.
|
||||
|
||||
Trusted peers, servers and protocols:
|
||||
- attacks that require a non-compliant or malicious server: in a reverse
|
||||
proxy, servers are trusted, or ejected. This covers server-to-client
|
||||
attacks in general.
|
||||
- attacks on protocols only used with trusted peers: peers, PROXY protocol,
|
||||
CIP (NetScaler Client-IP insertion), SOCKS, a local server reached over
|
||||
an ABNS or UNIX socket, an FCGI server, etc., as well as TLS servers
|
||||
contacted by the internal httpclient.
|
||||
- malfunction of a trusted auxiliary service (log server, ring output,
|
||||
CLI API consumer, etc.).
|
||||
|
||||
Privileged or local access (the actor is already trusted):
|
||||
- any problem triggered through admin access to the CLI.
|
||||
- anything requiring access to the master CLI.
|
||||
- anything requiring access to the command line.
|
||||
- anything requiring write access to the configuration file or any of its
|
||||
dependencies (Lua scripts, certificates, crt-list, acl, map, etc.).
|
||||
- anything requiring a configuration running as root, or chrooted to "/"
|
||||
(i.e. with no effective chroot).
|
||||
|
||||
Opt-in unsafe or experimental knobs (the operator disabled a safety):
|
||||
- anything requiring "experimental-mode on" on the CLI.
|
||||
- anything requiring "insecure-fork-wanted".
|
||||
- anything requiring "accept-unsafe-violations-*".
|
||||
- anything requiring "expose-experimental-directives".
|
||||
|
||||
Misconfiguration:
|
||||
- anything requiring a configuration that emits warnings at boot.
|
||||
- anything requiring a nonsensical configuration, e.g. a server looping back
|
||||
to the frontend, non-standard header processing or URL rewriting, or an
|
||||
excessively large number of headers or excessively large header/body
|
||||
sizes.
|
||||
|
||||
Volumetric or otherwise detectable activity:
|
||||
- anything requiring such a high and sustained level of activity that it
|
||||
would be detected and blocked in production (e.g. billions of requests or
|
||||
connections). This is volumetric DoS, as opposed to the asymmetric DoS of
|
||||
section 3.
|
||||
|
||||
Inherent protocol limitations:
|
||||
- anything that is a limitation of a standard protocol rather than an
|
||||
implementation flaw. For example, HTTP/1 has no way to abort a single
|
||||
transfer without closing the connection, so a client aborting a transfer
|
||||
will necessarily cause the corresponding server-side connection to be
|
||||
closed; this is by design of the protocol, not a vulnerability.
|
||||
|
||||
Features that are not security boundaries:
|
||||
- the stats page, including its admin mode, relies on HTTP basic
|
||||
authentication and was never meant to be a security boundary. Exposing a
|
||||
public-facing, admin-enabled stats page is therefore not covered.
|
||||
- configuring a listener to accept the PROXY protocol or CIP from senders
|
||||
that are not restricted to trusted ones is a misconfiguration: these
|
||||
headers are believed on trust, so the listener must be reachable only by
|
||||
the trusted L4/L7 component that prepends them.
|
||||
|
||||
Side channels:
|
||||
- cryptographic and micro-architectural side channels (timing, cache,
|
||||
speculative execution, etc.) are out of scope. Constant-time handling of
|
||||
secrets is pursued on a best-effort basis as ordinary hardening where it
|
||||
clearly matters, but observable timing or resource variations are not
|
||||
handled as security bugs.
|
||||
|
||||
Log integrity:
|
||||
- escaping of data emitted to logs is a configuration responsibility.
|
||||
Injection of control characters or forged fields through logged client
|
||||
data (e.g. when default escaping is disabled, or when a downstream log
|
||||
consumer mis-parses) is not covered.
|
||||
|
||||
5. DEFENSE IN DEPTH (DEFAULT HARDENING)
|
||||
A correctly deployed HAProxy combines several built-in mitigations that
|
||||
bound the impact of a successful compromise. These are deliberately taken
|
||||
into account when assessing the real-world severity of an issue and the
|
||||
handling it deserves: when one of them contains the practical impact of a
|
||||
bug, that bug rarely warrants a coordinated embargo and is usually better
|
||||
fixed quickly and in the open, where users get the fix sooner. They lower
|
||||
severity, not the obligation to fix: an exploitable memory-safety bug
|
||||
reachable from client input is still corrected as a bug.
|
||||
- No fork()/exec() in the worker: the worker never forks nor runs external
|
||||
programs, so an attacker who achieves code execution has little ability
|
||||
to spawn a shell or launch persistent background code. ("insecure-fork-
|
||||
wanted" deliberately disables this and is itself out of scope, see
|
||||
section 4.)
|
||||
- chroot and privilege drop: in the sane configuration this document
|
||||
assumes, the worker drops to an unprivileged user/group and chroots into
|
||||
an empty, unwritable directory. Injected code therefore has no filesystem
|
||||
access and very limited means to act on the host.
|
||||
- Activity watchdog: a thread that stops making progress, e.g. hijacked
|
||||
into an attacker-controlled loop or otherwise stuck, no longer services
|
||||
the event loop; the watchdog detects this lack of activity and kills the
|
||||
process after a few seconds rather than letting it be silently held.
|
||||
- Master/worker separation: only the worker is exposed to the network and
|
||||
runs the parsers reachable by clients, and it is the unprivileged,
|
||||
chrooted process. The master keeps privileges and filesystem access but
|
||||
has no network exposure. The master must therefore be protected as the
|
||||
trusted, more privileged component; an attacker is assumed to face only
|
||||
the worker. The master must under no circumstances be reachable from the
|
||||
worker (e.g. a master CLI bound to a TCP socket such as localhost is
|
||||
trivially reachable from compromised worker code and defeats this critical
|
||||
separation).
|
||||
|
||||
6. SEVERITY ORDERING
|
||||
The worst-case outcomes below are ranked by their realistic impact on a
|
||||
standard configuration, from most to least severe, and the effort spent
|
||||
guarding against each is proportional to that severity. The ranking reflects
|
||||
the master/worker privilege split and the containment provided by the
|
||||
section-5 mitigations.
|
||||
1. Remote code execution in the master process. The master is privileged
|
||||
and has filesystem access, so compromising it defeats every
|
||||
containment, leaks every secret, and can subvert or take down the
|
||||
whole service.
|
||||
2. Chosen disclosure of long-lived secrets, TLS private keys and
|
||||
certificates above all. Unlike an outage the damage is permanent and
|
||||
silent: stolen keys allow impersonation, interception and, absent
|
||||
forward secrecy, decryption of captured traffic, until every affected
|
||||
key is rotated and revoked across the ecosystem; a restart does not
|
||||
undo it. "Chosen" sets this rank, not scope: any disclosure of process
|
||||
memory or of another client's data to a client is in scope (section 3);
|
||||
this top rank is reserved for a targeted exfiltration, where the
|
||||
attacker steers the read to a known secret. A leak that cannot be
|
||||
steered toward a specific secret is still an in-scope disclosure bug,
|
||||
but ranks far lower - often no worse than the crash such a read tends
|
||||
to cause first.
|
||||
3. Crash of the master process. It brings the entire service down and
|
||||
prevents workers from being respawned: a full but recoverable outage.
|
||||
4. Crash of the worker process. A transient outage: in-flight connections
|
||||
are lost and traffic is interrupted for the fraction of a second it
|
||||
takes to respawn.
|
||||
5. Remote code execution in the worker process. Contained by no-fork,
|
||||
chroot, privilege drop and the watchdog, its availability impact is
|
||||
usually below a worker crash, except in the unlikely case where it
|
||||
unlocks the chosen disclosure of level 2, which is hard to reach
|
||||
through the internals from injected code.
|
||||
6. Policy bypass. Serious, but with no direct availability impact.
|
||||
|
||||
7. SECURITY-RELEVANT INVARIANTS AND DEFAULTS
|
||||
The values below define the conditions HAProxy is designed to operate
|
||||
within, and may be relied upon by parsing and processing code. A suspected
|
||||
vulnerability that can only be triggered by conditions outside them
|
||||
(typically values pushed beyond the stated limits) does not qualify as
|
||||
security-relevant:
|
||||
- trash buffers and struct buffer storage are always at least a few kB.
|
||||
- default buffer size is 16 kB (15 kB max input, as 1 kB is reserved for
|
||||
rewrites), tunable up to <256 MB.
|
||||
- default log line is 1 kB, tunable up to <=64 kB.
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
-----------------------
|
||||
HAProxy Starter Guide
|
||||
-----------------------
|
||||
version 3.5
|
||||
version 3.4
|
||||
|
||||
|
||||
This document is an introduction to HAProxy for all those who don't know it, as
|
||||
|
|
@ -97,9 +97,6 @@ to the mailing list whose responses are present in these documents.
|
|||
protocol which is implemented by HAProxy and a number of third party
|
||||
products.
|
||||
|
||||
- security.txt : how to report a security issue, and what does and does not
|
||||
qualify as a vulnerability.
|
||||
|
||||
- README : how to build HAProxy from sources
|
||||
|
||||
|
||||
|
|
@ -1689,7 +1686,15 @@ information you might later regret. Since the issue tracker presents itself as
|
|||
a very long thread, please avoid pasting very long dumps (a few hundreds lines
|
||||
or more) and attach them instead.
|
||||
|
||||
If you believe you may have found a security issue, please refer to the file
|
||||
doc/security.txt. It explains what does and does not qualify as a vulnerability
|
||||
in HAProxy, and how to report a genuine one privately. Most suspected issues
|
||||
turn out to be ordinary bugs that are better reported as described above.
|
||||
If you've found what you're absolutely certain can be considered a critical
|
||||
security issue that would put many users in serious trouble if discussed in a
|
||||
public place, then you can send it with the reproducer to security@haproxy.org.
|
||||
A small team of trusted developers will receive it and will be able to propose
|
||||
a fix. We usually don't use embargoes and once a fix is available it gets
|
||||
merged. In some rare circumstances it can happen that a release is coordinated
|
||||
with software vendors. Please note that this process usually messes up with
|
||||
everyone's work, and that rushed up releases can sometimes introduce new bugs,
|
||||
so it's best avoided unless strictly necessary; as such, there is often little
|
||||
consideration for reports that needlessly cause such extra burden, and the best
|
||||
way to see your work credited usually is to provide a working fix, which will
|
||||
appear in changelogs.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
------------------------
|
||||
HAProxy Management Guide
|
||||
------------------------
|
||||
version 3.5
|
||||
version 3.4
|
||||
|
||||
|
||||
This document describes how to start, stop, manage, and troubleshoot HAProxy,
|
||||
|
|
@ -215,18 +215,6 @@ list of options is :
|
|||
in foreground and to show incoming and outgoing events. It must never be
|
||||
used in an init script.
|
||||
|
||||
-dA[file] : dump an archive of all dependencies detected at boot time in the
|
||||
designated file in tar format, immediately after the configuration is done
|
||||
loading. This is equivalent to "set-dumpable libs", but instead of keeping
|
||||
the libs in memory, it dumps them into a file. This may be used after a
|
||||
core dump, in order to provide all necessary libraries to developers to
|
||||
permit them to exploit the core. This may not be available on all operating
|
||||
systems. It is highly recommended to use this with the regular
|
||||
configuration files, and optionally with "-c" when used manually, to make
|
||||
haproxy immediately exit after the dump, without starting. Example:
|
||||
|
||||
$ haproxy -dA/tmp/libs.tar -c -f /etc/haproxy/haproxy.cfg
|
||||
|
||||
-dC[key] : dump the configuration file. It is performed after the lines are
|
||||
tokenized, so comments are stripped and indenting is forced. If a non-zero
|
||||
key is specified, lines are truncated before sensitive/confidential fields,
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
Reporting security issues in HAProxy
|
||||
------------------------------------
|
||||
|
||||
Before reporting anything, please read doc/internals/threat-model.txt. It
|
||||
defines precisely what is and is not considered a security vulnerability in
|
||||
HAProxy. A fair number of suspected issues (and most automated or LLM-assisted
|
||||
findings) fall outside that boundary: they are ordinary bugs, and are best
|
||||
reported and fixed in public through the usual channels described in the
|
||||
"Contacts" section of doc/intro.txt.
|
||||
|
||||
If, after reading the threat model, you are confident you have found a genuine
|
||||
security issue that would put many users at risk if discussed in the open, the
|
||||
security team can be reached at security@haproxy.org, a private list read by a
|
||||
handful of security officers; anything shared there remains private. Please
|
||||
include a reproducer, and ideally a proposed and tested patch, as well as a
|
||||
valid name under which the report can be credited.
|
||||
|
||||
Auxiliary tools in dev/ and admin/ are not intended for production use and are
|
||||
by nature out of the security scope. Please report bugs affecting them via the
|
||||
regular channels.
|
||||
|
||||
We usually don't use embargoes: once a fix is available it simply gets merged.
|
||||
In rare circumstances a release may be coordinated with software vendors, but
|
||||
this disrupts everyone's work and rushed releases can introduce new bugs, so it
|
||||
is avoided unless strictly necessary. As a result, reports that needlessly cause
|
||||
such extra burden get little consideration, and the most effective and best
|
||||
credited way to report an issue is to provide a working fix, which will appear
|
||||
in the changelogs.
|
||||
|
||||
Findings produced with the help of AI MUST be accompanied by a working, tested
|
||||
patch. Such tools routinely report issues that are out of scope (see the
|
||||
threat model above) or simply not real, and reviewing them by hand wastes the
|
||||
very time and trust this process depends on. A model-generated report that
|
||||
arrives without a verified reproducer and a fix will generally not be
|
||||
processed.
|
||||
|
||||
See also:
|
||||
- doc/internals/threat-model.txt : what qualifies as a vulnerability
|
||||
- doc/internals/core-principles.txt : the project's design principles
|
||||
- doc/intro.txt : general contacts and bug reporting
|
||||
|
|
@ -422,7 +422,7 @@ static inline int channel_is_rewritable(const struct channel *chn)
|
|||
*/
|
||||
static inline int channel_may_send(const struct channel *chn)
|
||||
{
|
||||
return chn_cons(chn)->state >= SC_ST_REQ;
|
||||
return chn_cons(chn)->state == SC_ST_EST;
|
||||
}
|
||||
|
||||
/* HTX version of channel_may_recv(). Returns non-zero if the channel can still
|
||||
|
|
|
|||
|
|
@ -165,7 +165,9 @@ static forceinline struct buffer *alloc_small_trash_chunk(void)
|
|||
*/
|
||||
static forceinline struct buffer *alloc_trash_chunk_sz(size_t size)
|
||||
{
|
||||
if (size <= pool_head_trash->size)
|
||||
if (pool_head_small_trash && size <= pool_head_small_trash->size)
|
||||
return alloc_small_trash_chunk();
|
||||
else if (size <= pool_head_trash->size)
|
||||
return alloc_trash_chunk();
|
||||
else if (pool_head_large_trash && size <= pool_head_large_trash->size)
|
||||
return alloc_large_trash_chunk();
|
||||
|
|
|
|||
|
|
@ -179,8 +179,6 @@ enum {
|
|||
/* 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_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 */
|
||||
|
||||
|
|
@ -675,12 +673,11 @@ struct connection {
|
|||
};
|
||||
|
||||
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_side side;
|
||||
const struct mux_ops *mux;
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
|
||||
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);
|
||||
|
||||
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,
|
||||
const struct mux_ops *force_mux_ops);
|
||||
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_str(struct connection *c);
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
list_for_each_entry(item, &mux_proto_list.list, list) {
|
||||
if (isteq(proto, item->mux_proto))
|
||||
if (isteq(proto, item->token))
|
||||
return item;
|
||||
}
|
||||
return NULL;
|
||||
|
|
@ -672,7 +668,6 @@ void list_mux_proto(FILE *out);
|
|||
*/
|
||||
static inline const struct mux_proto_list *conn_get_best_mux_entry(
|
||||
const struct ist mux_proto,
|
||||
const struct ist alpn,
|
||||
int proto_side, int proto_is_quic, int proto_mode)
|
||||
{
|
||||
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) {
|
||||
if (!(item->side & proto_side) || !(item->mode & proto_mode) || ((proto_is_quic != 0) != ((item->mux->flags & MX_FL_FRAMED) != 0)))
|
||||
continue;
|
||||
if (istlen(mux_proto) && isteq(mux_proto, item->mux_proto)) {
|
||||
if (istlen(mux_proto) && isteq(mux_proto, item->token)) {
|
||||
return item;
|
||||
}
|
||||
else if (istlen(alpn) && item->alpn &&
|
||||
strlen(item->alpn) == istlen(alpn) + 1 &&
|
||||
!memcmp(alpn.ptr, item->alpn + 1, istlen(alpn)))
|
||||
return item;
|
||||
else if (!istlen(item->mux_proto)) {
|
||||
else if (!istlen(item->token)) {
|
||||
if (!fallback || (item->mode == proto_mode && fallback->mode != proto_mode))
|
||||
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,
|
||||
const struct ist mux_proto,
|
||||
const struct ist alpn,
|
||||
int proto_side, int proto_mode)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ extern struct cfgfile fileless_cfg;
|
|||
/* storage for collected libs */
|
||||
extern void *lib_storage;
|
||||
extern size_t lib_size;
|
||||
extern char *lib_output_file;
|
||||
|
||||
struct proxy;
|
||||
struct server;
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ enum h1m_state {
|
|||
#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_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)
|
||||
/* 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);
|
||||
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_calculate_ws_output_key(const char *key, char *result);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#include <haproxy/buf-t.h>
|
||||
#include <haproxy/mux_quic-t.h>
|
||||
|
||||
/* H3 unidirectional stream types
|
||||
/* H3 unidirecational stream types
|
||||
* Emitted as the first byte on the stream to differentiate it.
|
||||
*/
|
||||
#define H3_UNI_S_T_CTRL 0x00
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ struct httpclient {
|
|||
struct buffer buf; /* input buffer, raw HTTP */
|
||||
} res;
|
||||
struct {
|
||||
/* callbacks used to send the request, */
|
||||
/* callbacks used to send the request, */
|
||||
void (*req_payload)(struct httpclient *hc); /* send a payload */
|
||||
|
||||
/* 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 */
|
||||
} ops;
|
||||
struct sockaddr_storage *dst; /* destination address */
|
||||
struct appctx *appctx; /* HTTP client appctx */
|
||||
struct appctx *appctx; /* HTTPclient appctx */
|
||||
int timeout_server; /* server timeout in ms */
|
||||
void *caller; /* ptr of the caller */
|
||||
unsigned int flags; /* other flags */
|
||||
|
|
@ -50,7 +50,7 @@ struct httpclient {
|
|||
#define HTTPCLIENT_FS_ENDED 0x00020000 /* the httpclient is stopped */
|
||||
|
||||
/* 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 */
|
||||
|
||||
/* States of the HTTP Client Appctx */
|
||||
|
|
@ -65,4 +65,4 @@ enum {
|
|||
|
||||
#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);
|
||||
}
|
||||
|
||||
#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_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_UPG_HDR 0x00008000 /* non-empty Upgrapde header found */
|
||||
|
||||
/* 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
|
||||
|
|
@ -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_BODYLESS, _(HTX_SL_F_HAS_SCHM, _(HTX_SL_F_SCHM_HTTP,
|
||||
_(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_NOT_HTTP, _(HTX_SL_F_UPG_HDR))))))))))))))));
|
||||
_(HTX_SL_F_NORMALIZED_URI, _(HTX_SL_F_CONN_UPG)))))))))))));
|
||||
/* epilogue */
|
||||
_(~0U);
|
||||
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_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_HDRS_ONLY 0x00000004 /* Only Transfer header blocks (start-line, header and EOH) */
|
||||
#define HTX_XFER_NO_METADATA 0x00000008 /* <count> don't include meta-data, only payload */
|
||||
|
||||
#define HTX_XFER_HDRS_ONLY 0x00000003 /* Only Transfer header blocks (start-line, header and EOH) */
|
||||
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
|
||||
|
|
|
|||
|
|
@ -76,56 +76,6 @@ static inline unsigned int div64_32(unsigned long long o1, unsigned int o2)
|
|||
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 */
|
||||
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 */
|
||||
|
||||
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 cl_bidi_r; /* total count of closed remote bidi stream since last MAX_STREAMS emission */
|
||||
|
||||
|
|
@ -286,11 +285,9 @@ static forceinline char *qcc_show_flags(char *buf, size_t len, const char *delim
|
|||
/* flags */
|
||||
_(QC_CF_ERRL,
|
||||
_(QC_CF_ERRL_DONE,
|
||||
_(QC_CF_IS_BACK,
|
||||
_(QC_CF_CONN_FULL,
|
||||
_(QC_CF_CONN_SHUT,
|
||||
_(QC_CF_ERR_CONN,
|
||||
_(QC_CF_WAIT_HS)))))));
|
||||
_(QC_CF_WAIT_HS)))));
|
||||
/* epilogue */
|
||||
_(~0U);
|
||||
return buf;
|
||||
|
|
@ -333,8 +330,7 @@ static forceinline char *qcs_show_flags(char *buf, size_t len, const char *delim
|
|||
_(QC_SF_HREQ_RECV,
|
||||
_(QC_SF_TO_STOP_SENDING,
|
||||
_(QC_SF_UNKNOWN_PL_LENGTH,
|
||||
_(QC_SF_RECV_RESET,
|
||||
_(QC_SF_EOI_SUSPENDED)))))))))))));
|
||||
_(QC_SF_RECV_RESET))))))))))));
|
||||
/* epilogue */
|
||||
_(~0U);
|
||||
return buf;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include <haproxy/connection.h>
|
||||
#include <haproxy/list.h>
|
||||
#include <haproxy/mux_quic-t.h>
|
||||
#include <haproxy/quic_tune.h>
|
||||
#include <haproxy/stconn.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);
|
||||
qcs->flags |= QC_SF_HREQ_RECV;
|
||||
++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);
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ void proxy_capture_error(struct proxy *proxy, int is_back,
|
|||
void (*show)(struct buffer *, const struct error_snapshot *));
|
||||
void proxy_adjust_all_maxconn(void);
|
||||
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);
|
||||
void free_stick_rules(struct list *rules);
|
||||
void free_server_rules(struct list *srules);
|
||||
|
|
|
|||
|
|
@ -43,14 +43,6 @@
|
|||
#define QPACK_DEC_INST_SCCL 0x40 // Stream Cancellation
|
||||
#define QPACK_DEC_INST_SACK 0x80 // Section Acknowledgment
|
||||
|
||||
/* Encoded field line bitmasks (shared between encoder and decoder) */
|
||||
#define QPACK_EFL_BITMASK 0xf0
|
||||
#define QPACK_LFL_WPBNM 0x00 // Literal field line with post-base name reference
|
||||
#define QPACK_IFL_WPBI 0x10 // Indexed field line with post-based index
|
||||
#define QPACK_LFL_WLN_BIT 0x20 // Literal field line with literal name
|
||||
#define QPACK_LFL_WNR_BIT 0x40 // Literal field line with name reference
|
||||
#define QPACK_IFL_BIT 0x80 // Indexed field line
|
||||
|
||||
/* RFC 9204 6. Error Handling */
|
||||
enum qpack_err {
|
||||
QPACK_ERR_DECOMPRESSION_FAILED = 0x200,
|
||||
|
|
|
|||
|
|
@ -93,30 +93,24 @@ static inline struct ist qpack_get_value(const struct qpack_dht *dht, const stru
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* takes an absolute idx (including static table offset), returns the associated name */
|
||||
/* takes an idx, returns the associated name */
|
||||
static inline struct ist qpack_idx_to_name(const struct qpack_dht *dht, uint32_t idx)
|
||||
{
|
||||
const struct qpack_dte *dte;
|
||||
|
||||
if (idx < QPACK_SHT_SIZE)
|
||||
return ist("### ERR ###"); /* static table entries not accessible via dht */
|
||||
|
||||
dte = qpack_get_dte(dht, idx - QPACK_SHT_SIZE);
|
||||
dte = qpack_get_dte(dht, idx);
|
||||
if (!dte)
|
||||
return ist("### ERR ###"); // error
|
||||
|
||||
return qpack_get_name(dht, dte);
|
||||
}
|
||||
|
||||
/* takes an absolute idx (including static table offset), returns the associated value */
|
||||
/* takes an idx, returns the associated value */
|
||||
static inline struct ist qpack_idx_to_value(const struct qpack_dht *dht, uint32_t idx)
|
||||
{
|
||||
const struct qpack_dte *dte;
|
||||
|
||||
if (idx < QPACK_SHT_SIZE)
|
||||
return ist("### ERR ###"); /* static table entries not accessible via dht */
|
||||
|
||||
dte = qpack_get_dte(dht, idx - QPACK_SHT_SIZE);
|
||||
dte = qpack_get_dte(dht, idx);
|
||||
if (!dte)
|
||||
return ist("### ERR ###"); // error
|
||||
|
||||
|
|
|
|||
|
|
@ -135,8 +135,6 @@ static inline void in46un_to_addr(const union sockaddr_in46 *src,
|
|||
in6->sin6_family = AF_INET6;
|
||||
in6->sin6_addr = src->in6.sin6_addr;
|
||||
in6->sin6_port = src->in6.sin6_port;
|
||||
in6->sin6_flowinfo = src->in6.sin6_flowinfo;
|
||||
in6->sin6_scope_id = src->in6.sin6_scope_id;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* include/haproxy/resolvers-t.h
|
||||
* include/haproxy/dns-t.h
|
||||
* This file provides structures and types for DNS.
|
||||
*
|
||||
* 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 */
|
||||
int16_t type; /* question type */
|
||||
int16_t class; /* query class */
|
||||
uint32_t ttl; /* response TTL */
|
||||
int32_t ttl; /* response TTL */
|
||||
int16_t priority; /* SRV type priority */
|
||||
uint16_t weight; /* SRV type weight */
|
||||
uint16_t port; /* SRV type port */
|
||||
|
|
@ -281,7 +281,7 @@ enum {
|
|||
* matching preference was 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.
|
||||
*/
|
||||
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
|
||||
*
|
||||
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
|
||||
|
|
|
|||
|
|
@ -111,8 +111,7 @@ enum srv_initaddr {
|
|||
* at start up time.
|
||||
*/
|
||||
enum srv_init_state {
|
||||
SRV_INIT_STATE_NONE = 0,
|
||||
SRV_INIT_STATE_FULLY_DOWN, /* the server should initially be considered DOWN until it passes all health checks. Please keep set to zero. */
|
||||
SRV_INIT_STATE_FULLY_DOWN = 0, /* 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_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. */
|
||||
|
|
@ -249,9 +248,7 @@ struct pid_list {
|
|||
|
||||
/* srv methods of computing chash keys */
|
||||
enum srv_hash_key {
|
||||
SRV_HASH_KEY_ID = 0, /* derived from server puid, 28 LSB used */
|
||||
SRV_HASH_KEY_ID32, /* derived from server puid, 32 bits used */
|
||||
SRV_HASH_KEY_GUID, /* derived from server guid */
|
||||
SRV_HASH_KEY_ID = 0, /* derived from server puid */
|
||||
SRV_HASH_KEY_ADDR, /* derived from server address */
|
||||
SRV_HASH_KEY_ADDR_PORT /* derived from server address and port */
|
||||
};
|
||||
|
|
@ -279,8 +276,6 @@ struct srv_per_thread {
|
|||
struct ceb_root *idle_conns; /* Shareable idle connections */
|
||||
struct ceb_root *safe_conns; /* Safe idle connections */
|
||||
struct ceb_root *avail_conns; /* Connections in use, but with still new streams available */
|
||||
struct server *srv; /* Back-pointer to the server */
|
||||
struct eb32_node idle_node; /* When to next do cleanup in the idle connections */
|
||||
#ifdef USE_QUIC
|
||||
struct ist quic_retry_token;
|
||||
#endif
|
||||
|
|
@ -331,7 +326,6 @@ enum renegotiate_mode {
|
|||
struct path_parameters {
|
||||
__decl_thread(HA_RWLOCK_T param_lock);
|
||||
char nego_alpn[MAX_ALPN_SIZE];
|
||||
int64_t srv_hash;
|
||||
#ifdef USE_QUIC
|
||||
struct quic_early_transport_params tps;
|
||||
#endif
|
||||
|
|
@ -403,6 +397,7 @@ struct server {
|
|||
* thread, and generally at the same time.
|
||||
*/
|
||||
THREAD_ALIGN();
|
||||
struct eb32_node idle_node; /* When to next do cleanup in the idle connections */
|
||||
unsigned int curr_idle_conns; /* Current number of orphan idling connections, both the idle and the safe lists */
|
||||
unsigned int curr_idle_nb; /* Current number of connections in the idle list */
|
||||
unsigned int curr_safe_nb; /* Current number of connections in the safe list */
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@
|
|||
#include <haproxy/tools.h>
|
||||
|
||||
|
||||
__decl_thread(extern HA_SPINLOCK_T idle_conn_srv_lock);
|
||||
extern struct idle_conns idle_conns[MAX_THREADS];
|
||||
extern struct task *idle_conn_task[MAX_THREADS];
|
||||
extern struct eb_root idle_conn_srv[MAX_THREADS];
|
||||
extern struct task *idle_conn_task;
|
||||
extern struct mt_list servers_list;
|
||||
extern struct dict server_key_dict;
|
||||
|
||||
|
|
@ -278,35 +278,6 @@ static inline void srv_adm_set_ready(struct server *s)
|
|||
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. */
|
||||
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; /* Number of actual references to this certificate_ocsp (SSL_CTXs mostly) */
|
||||
struct buffer response;
|
||||
unsigned long expire;
|
||||
long expire;
|
||||
X509 *issuer;
|
||||
STACK_OF(X509) *chain;
|
||||
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_*() */
|
||||
};
|
||||
|
||||
/* 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 */
|
||||
|
|
|
|||
|
|
@ -1154,8 +1154,6 @@ void *get_sym_curr_addr(const char *name);
|
|||
void *get_sym_next_addr(const char *name);
|
||||
int dump_libs(struct buffer *output, int with_addr);
|
||||
void collect_libs(void);
|
||||
void free_collected_libs(void);
|
||||
int copy_libs_to_file(void);
|
||||
|
||||
/* Note that this may result in opening libgcc() on first call, so it may need
|
||||
* to have been called once before chrooting.
|
||||
|
|
@ -1290,27 +1288,11 @@ static inline void _ha_aligned_free(void *ptr)
|
|||
int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz);
|
||||
|
||||
/* PRNG */
|
||||
struct uint64_pair _ha_random64_pair_hashed(void);
|
||||
|
||||
void ha_generate_uuid_v4(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_thread(void);
|
||||
void ha_random_jump128(uint32_t dist);
|
||||
void ha_random_jump192(uint32_t dist);
|
||||
void ha_random_jump96(uint32_t dist);
|
||||
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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,8 +34,6 @@
|
|||
#define _TRC_LOC(f,l) __TRC_LOC(f, ":", 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.
|
||||
* The first one (a0) is always ignored.
|
||||
*/
|
||||
|
|
@ -141,23 +139,8 @@
|
|||
&trace_no_cb, ist2(_msg, _msg_len)); \
|
||||
} \
|
||||
} 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_ERROR(msg, mask, args...) TRACE_ERROR(msg, mask, ##args)
|
||||
# define DBG_TRACE_USER(msg, mask, args...) TRACE_USER(msg, mask, ##args)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
#ifdef CONFIG_PRODUCT_BRANCH
|
||||
#define PRODUCT_BRANCH CONFIG_PRODUCT_BRANCH
|
||||
#else
|
||||
#define PRODUCT_BRANCH "3.5"
|
||||
#define PRODUCT_BRANCH "3.4"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PRODUCT_STATUS
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ syslog S3 -level notice {
|
|||
|
||||
syslog S4 -level notice {
|
||||
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
|
||||
|
||||
server s1 {
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
varnishtest "Health-checks: some external check tests"
|
||||
feature ignore_unknown_macro
|
||||
#REGTEST_TYPE=slow
|
||||
|
||||
server s1 {
|
||||
rxreq
|
||||
expect req.method == GET
|
||||
expect req.url == /health
|
||||
expect req.proto == HTTP/1.1
|
||||
txresp
|
||||
} -start
|
||||
|
||||
syslog S1 -level notice {
|
||||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded, reason: External check passed, code: 0"
|
||||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded, reason: External check passed, code: 0"
|
||||
} -start
|
||||
|
||||
syslog S2 -level notice {
|
||||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded.*code: 200"
|
||||
} -start
|
||||
|
||||
haproxy h1 -conf {
|
||||
global
|
||||
.if feature(THREAD)
|
||||
thread-groups 1
|
||||
.endif
|
||||
external-check
|
||||
insecure-fork-wanted
|
||||
|
||||
healthcheck http-health
|
||||
type httpchk
|
||||
http-check send meth GET uri /health ver HTTP/1.1
|
||||
|
||||
defaults
|
||||
mode http
|
||||
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
option log-health-checks
|
||||
|
||||
backend be1
|
||||
log ${S1_addr}:${S1_port} len 2048 local0
|
||||
option external-check
|
||||
external-check command /bin/true
|
||||
server srv ${h1_li1_addr}:${h1_li1_port} check inter 100ms rise 1 fall 1
|
||||
|
||||
defaults
|
||||
mode http
|
||||
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
option external-check
|
||||
external-check command /bin/true
|
||||
option log-health-checks
|
||||
|
||||
backend be2
|
||||
log ${S1_addr}:${S1_port} len 2048 local0
|
||||
server srv ${h1_li1_addr}:${h1_li1_port} check inter 100ms rise 1 fall 1
|
||||
|
||||
backend be3
|
||||
log ${S2_addr}:${S2_port} len 2048 local0
|
||||
option external-check
|
||||
external-check command /bin/true
|
||||
server srv ${s1_addr}:${s1_port} check inter 100ms rise 1 fall 1 healthcheck http-health
|
||||
|
||||
listen li1
|
||||
mode http
|
||||
bind "fd@${li1}"
|
||||
http-request return status 200
|
||||
|
||||
} -start
|
||||
|
||||
syslog S1 -wait
|
||||
syslog S2 -wait
|
||||
|
|
@ -144,7 +144,7 @@ syslog S2 -level notice {
|
|||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9] succeeded"
|
||||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||
recv
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ feature ignore_unknown_macro
|
|||
|
||||
syslog S1 -level notice {
|
||||
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
|
||||
|
||||
syslog S2 -level notice {
|
||||
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
|
||||
|
||||
server s1 {
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ client c1 -connect ${hap_fe1_sock} {
|
|||
} -run
|
||||
|
||||
# missing websocket key
|
||||
client c2_1 -connect ${hap_fe1_sock} {
|
||||
client c2 -connect ${hap_fe1_sock} {
|
||||
txreq \
|
||||
-req "GET" \
|
||||
-url "/" \
|
||||
|
|
@ -158,19 +158,6 @@ client c2_1 -connect ${hap_fe1_sock} {
|
|||
expect resp.status == 400
|
||||
} -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
|
||||
client c3 -connect ${hap_fe2_sock} {
|
||||
txreq \
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ haproxy h1 -conf {
|
|||
thread-groups 1
|
||||
.endif
|
||||
|
||||
tune.lua.openlibs string
|
||||
tune.lua.openlibs none
|
||||
tune.lua.bool-sample-conversion normal
|
||||
lua-load ${testdir}/h_txn_get_priv.lua
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ haproxy h1 -conf {
|
|||
thread-groups 1
|
||||
.endif
|
||||
|
||||
tune.lua.openlibs string
|
||||
tune.lua.openlibs none
|
||||
tune.lua.bool-sample-conversion normal
|
||||
lua-load ${testdir}/lua_httpclient.lua
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ haproxy h1 -conf {
|
|||
thread-groups 1
|
||||
.endif
|
||||
|
||||
tune.lua.openlibs string
|
||||
tune.lua.openlibs none
|
||||
tune.lua.bool-sample-conversion normal
|
||||
lua-load ${testdir}/lua_socket.lua
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ haproxy h1 -conf {
|
|||
thread-groups 1
|
||||
.endif
|
||||
|
||||
tune.lua.openlibs string,table
|
||||
tune.lua.openlibs none
|
||||
tune.lua.bool-sample-conversion normal
|
||||
lua-load ${testdir}/txn_get_priv.lua
|
||||
lua-load ${testdir}/txn_get_priv-print_r.lua
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@ client c1 -connect ${h1_feS_sock} {
|
|||
|
||||
haproxy h1 -cli {
|
||||
# non existent backend
|
||||
send "add backend be from def"
|
||||
send "experimental-mode on; add backend be from def"
|
||||
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."
|
||||
|
||||
send "add server be/srv ${hsrv_fe_addr}:${hsrv_fe_port}"
|
||||
|
|
|
|||
|
|
@ -40,29 +40,29 @@ haproxy h1 -conf {
|
|||
} -start
|
||||
|
||||
haproxy h1 -cli {
|
||||
send "del backend other"
|
||||
send "experimental-mode on; del backend other"
|
||||
expect ~ "No such backend."
|
||||
|
||||
send "del backend li"
|
||||
send "experimental-mode on; del backend li"
|
||||
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."
|
||||
|
||||
send "show stat be 2 -1"
|
||||
expect ~ "be,BACKEND,"
|
||||
|
||||
send "del backend be"
|
||||
send "experimental-mode on; del backend be"
|
||||
expect ~ "Backend must be unpublished prior to its deletion."
|
||||
|
||||
send "unpublish backend be;"
|
||||
expect ~ ".*"
|
||||
send "del backend be"
|
||||
send "experimental-mode on; del backend be"
|
||||
expect ~ "Only a backend without server can be deleted."
|
||||
|
||||
send "del server be/s1"
|
||||
expect ~ ".*"
|
||||
send "del backend be"
|
||||
send "experimental-mode on; del backend be"
|
||||
expect ~ "Backend deleted."
|
||||
|
||||
send "show stat be 2 -1"
|
||||
|
|
@ -75,7 +75,7 @@ haproxy h1 -cli {
|
|||
|
||||
send "unpublish backend be_unnamed_def_ref;"
|
||||
expect ~ ".*"
|
||||
send "del backend be_unnamed_def_ref"
|
||||
send "experimental-mode on; del backend be_unnamed_def_ref"
|
||||
expect ~ "Backend deleted."
|
||||
|
||||
send "show stat be_unnamed_def_ref 2 -1"
|
||||
|
|
@ -83,6 +83,6 @@ haproxy h1 -cli {
|
|||
|
||||
send "unpublish backend be_unnamed_def_ref2;"
|
||||
expect ~ ".*"
|
||||
send "del backend be_unnamed_def_ref2"
|
||||
send "experimental-mode on; del backend be_unnamed_def_ref2"
|
||||
expect ~ "Backend deleted."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
../ssl/certs
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
varnishtest "HTTP/3 over QMux"
|
||||
feature ignore_unknown_macro
|
||||
|
||||
# TODO to adjust once QMux compilation is QUIC/SSL free
|
||||
feature cmd "$HAPROXY_PROGRAM -cc 'feature(QUIC) && !feature(QUIC_OPENSSL_COMPAT) && !feature(OPENSSL_WOLFSSL) && ssllib_name_startswith(OpenSSL) && openssl_version_atleast(1.1.1)'"
|
||||
|
||||
haproxy h1 -conf {
|
||||
global
|
||||
.if feature(THREAD)
|
||||
thread-groups 1
|
||||
.endif
|
||||
expose-experimental-directives
|
||||
ssl-server-verify none
|
||||
|
||||
defaults
|
||||
mode http
|
||||
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
|
||||
frontend fterm
|
||||
bind "fd@${fterm}" ssl crt ${testdir}/certs/common.pem alpn h3
|
||||
http-request return status 200 hdr x-alpn %[ssl_fc_alpn] hdr x-ver %[req.ver]
|
||||
|
||||
frontend fpub
|
||||
bind "fd@${fpub}" proto h1
|
||||
use_backend be
|
||||
|
||||
backend be
|
||||
server hap ${h1_fterm_addr}:${h1_fterm_port} ssl alpn h3
|
||||
} -start
|
||||
|
||||
client c1 -connect ${h1_fpub_sock} {
|
||||
txreq
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-alpn == "h3"
|
||||
expect resp.http.x-ver == "3.0"
|
||||
} -run
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
varnishtest "HTTP/3 over clear QMux"
|
||||
feature ignore_unknown_macro
|
||||
|
||||
# TODO to adjust once QMux compilation is QUIC/SSL free
|
||||
feature cmd "$HAPROXY_PROGRAM -cc 'feature(QUIC) && !feature(QUIC_OPENSSL_COMPAT) && !feature(OPENSSL_WOLFSSL) && ssllib_name_startswith(OpenSSL) && openssl_version_atleast(1.1.1)'"
|
||||
feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(3.4-dev14)'"
|
||||
|
||||
haproxy h1 -conf {
|
||||
global
|
||||
.if feature(THREAD)
|
||||
thread-groups 1
|
||||
.endif
|
||||
expose-experimental-directives
|
||||
|
||||
defaults
|
||||
mode http
|
||||
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
|
||||
frontend fterm
|
||||
bind "fd@${fterm}" proto qmux
|
||||
http-request return status 200 hdr x-ver %[req.ver]
|
||||
|
||||
frontend fpub
|
||||
bind "fd@${fpub}" proto h1
|
||||
use_backend be
|
||||
|
||||
backend be
|
||||
server hap ${h1_fterm_addr}:${h1_fterm_port} proto qmux
|
||||
} -start
|
||||
|
||||
client c1 -connect ${h1_fpub_sock} {
|
||||
txreq
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-ver == "3.0"
|
||||
} -run
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
#REGTEST_TYPE=broken
|
||||
#REGTEST_TYPE=slow
|
||||
# reg-test is around ~2.5s
|
||||
# It is incompatible with ssl/ocsp_auto_update.vtc running in parallel because
|
||||
# both start a server on the same port, whose URL is specified in the test
|
||||
# certificates. Given that the test is essentially about testing OCSP update,
|
||||
# let's just use the more generic SSL one.
|
||||
|
||||
# broken with BoringSSL.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
#REGTEST_TYPE=bug
|
||||
varnishtest "Test for ECDSA/RSA selection and crt-list filters"
|
||||
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(QUIC_OPENSSL_COMPAT) && !feature(OPENSSL_WOLFSSL)'"
|
||||
feature cmd "$HAPROXY_PROGRAM -cc 'feature(QUIC)'"
|
||||
# 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
|
||||
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
|
||||
# SNI, and that the negative filters are correctly excluded
|
||||
#
|
||||
|
|
|
|||
|
|
@ -6,10 +6,6 @@ haproxy h1 -conf {
|
|||
thread-groups 1
|
||||
.endif
|
||||
|
||||
.if feature(QUIC_OPENSSL_COMPAT)
|
||||
limited-quic
|
||||
.endif
|
||||
|
||||
stats socket "${tmpdir}/h1/stats" level admin
|
||||
issuers-chain-path "${testdir}/certs/issuers-chain-path/ca/"
|
||||
crt-base "${testdir}/certs/issuers-chain-path"
|
||||
|
|
|
|||
25
src/acme.c
25
src/acme.c
|
|
@ -1532,7 +1532,7 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||
|
||||
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"))) {
|
||||
istfree(&ctx->nonce);
|
||||
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;
|
||||
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 */
|
||||
if (ssl_sock_load_pem_into_ckch(ctx->store->path, hc->res.buf.area, ctx->store->data , errmsg) != 0)
|
||||
goto error;
|
||||
|
|
@ -1745,7 +1735,7 @@ int acme_res_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||
|
||||
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"))) {
|
||||
istfree(&ctx->nonce);
|
||||
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);
|
||||
|
||||
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||
istfree(&ctx->nonce);
|
||||
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;
|
||||
|
||||
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||
istfree(&ctx->nonce);
|
||||
ctx->nonce = istdup(hdr->v);
|
||||
|
|
@ -2028,7 +2018,6 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||
/* if auth is already valid we need to skip solving challenges */
|
||||
if (strncasecmp("valid", trash.area, trash.data) == 0) {
|
||||
auth->validated = 1;
|
||||
auth->ready = ctx->cfg->cond_ready; /* no challenge needed, satisfy all readiness conditions */
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -2290,7 +2279,7 @@ int acme_res_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||
|
||||
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"))) {
|
||||
istfree(&ctx->nonce);
|
||||
ctx->nonce = istdup(hdr->v);
|
||||
|
|
@ -2469,7 +2458,7 @@ int acme_res_account(struct task *task, struct acme_ctx *ctx, int newaccount, ch
|
|||
|
||||
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"))) {
|
||||
istfree(&ctx->kid);
|
||||
ctx->kid = istdup(hdr->v);
|
||||
|
|
@ -2536,7 +2525,7 @@ int acme_nonce(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||
|
||||
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"))) {
|
||||
istfree(&ctx->nonce);
|
||||
ctx->nonce = istdup(hdr->v);
|
||||
|
|
|
|||
|
|
@ -80,10 +80,7 @@ static const char *const memprof_methods[MEMPROF_METH_METHODS] = {
|
|||
struct memprof_stats memprof_stats[MEMPROF_HASH_BUCKETS + 1] = { };
|
||||
|
||||
/* used to detect recursive calls */
|
||||
#define MEMPROF_IN_INIT (1U << 0)
|
||||
#define MEMPROF_IN_HANDLER (1U << 1)
|
||||
|
||||
static THREAD_LOCAL uint in_memprof = 0; // arithmetic OR of MEMPROF_IN_*
|
||||
static THREAD_LOCAL int in_memprof = 0;
|
||||
|
||||
/* These ones are used by glibc and will be called early. They are in charge of
|
||||
* initializing the handlers with the original functions.
|
||||
|
|
@ -140,7 +137,7 @@ static __attribute__((noreturn)) void memprof_die(const char *msg)
|
|||
*/
|
||||
static void memprof_init()
|
||||
{
|
||||
in_memprof |= MEMPROF_IN_INIT;
|
||||
in_memprof++;
|
||||
memprof_malloc_handler = get_sym_next_addr("malloc");
|
||||
if (!memprof_malloc_handler)
|
||||
memprof_die("FATAL: malloc() function not found.\n");
|
||||
|
|
@ -171,7 +168,7 @@ static void memprof_init()
|
|||
memprof_aligned_alloc_handler = get_sym_next_addr("aligned_alloc");
|
||||
memprof_posix_memalign_handler = get_sym_next_addr("posix_memalign");
|
||||
|
||||
in_memprof &= ~MEMPROF_IN_INIT;
|
||||
in_memprof--;
|
||||
}
|
||||
|
||||
/* the initial handlers will initialize all regular handlers and will call the
|
||||
|
|
@ -180,7 +177,7 @@ static void memprof_init()
|
|||
*/
|
||||
static void *memprof_malloc_initial_handler(size_t size)
|
||||
{
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
if (in_memprof) {
|
||||
/* it's likely that dlsym() needs malloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -191,7 +188,7 @@ static void *memprof_malloc_initial_handler(size_t size)
|
|||
|
||||
static void *memprof_calloc_initial_handler(size_t nmemb, size_t size)
|
||||
{
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
if (in_memprof) {
|
||||
/* it's likely that dlsym() needs calloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -201,7 +198,7 @@ static void *memprof_calloc_initial_handler(size_t nmemb, size_t size)
|
|||
|
||||
static void *memprof_realloc_initial_handler(void *ptr, size_t size)
|
||||
{
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
if (in_memprof) {
|
||||
/* it's likely that dlsym() needs realloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -212,7 +209,7 @@ static void *memprof_realloc_initial_handler(void *ptr, size_t size)
|
|||
|
||||
static char *memprof_strdup_initial_handler(const char *s)
|
||||
{
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
if (in_memprof) {
|
||||
/* probably that dlsym() needs strdup(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -231,7 +228,7 @@ static void memprof_free_initial_handler(void *ptr)
|
|||
|
||||
static char *memprof_strndup_initial_handler(const char *s, size_t n)
|
||||
{
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
if (in_memprof) {
|
||||
/* probably that dlsym() needs strndup(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -242,7 +239,7 @@ static char *memprof_strndup_initial_handler(const char *s, size_t n)
|
|||
|
||||
static void *memprof_valloc_initial_handler(size_t sz)
|
||||
{
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
if (in_memprof) {
|
||||
/* probably that dlsym() needs valloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -253,7 +250,7 @@ static void *memprof_valloc_initial_handler(size_t sz)
|
|||
|
||||
static void *memprof_pvalloc_initial_handler(size_t sz)
|
||||
{
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
if (in_memprof) {
|
||||
/* probably that dlsym() needs pvalloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -264,7 +261,7 @@ static void *memprof_pvalloc_initial_handler(size_t sz)
|
|||
|
||||
static void *memprof_memalign_initial_handler(size_t al, size_t sz)
|
||||
{
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
if (in_memprof) {
|
||||
/* probably that dlsym() needs memalign(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -275,7 +272,7 @@ static void *memprof_memalign_initial_handler(size_t al, size_t sz)
|
|||
|
||||
static void *memprof_aligned_alloc_initial_handler(size_t al, size_t sz)
|
||||
{
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
if (in_memprof) {
|
||||
/* probably that dlsym() needs aligned_alloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -286,7 +283,7 @@ static void *memprof_aligned_alloc_initial_handler(size_t al, size_t sz)
|
|||
|
||||
static int memprof_posix_memalign_initial_handler(void **ptr, size_t al, size_t sz)
|
||||
{
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
if (in_memprof) {
|
||||
/* probably that dlsym() needs posix_memalign(), let's fail */
|
||||
return ENOMEM;
|
||||
}
|
||||
|
|
@ -347,13 +344,11 @@ void *malloc(size_t size)
|
|||
struct memprof_stats *bin;
|
||||
void *ret;
|
||||
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
return memprof_malloc_handler(size);
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
ret = memprof_malloc_handler(size);
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MALLOC);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
|
|
@ -376,13 +371,11 @@ void *calloc(size_t nmemb, size_t size)
|
|||
struct memprof_stats *bin;
|
||||
void *ret;
|
||||
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
return memprof_calloc_handler(nmemb, size);
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
ret = memprof_calloc_handler(nmemb, size);
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_CALLOC);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
|
|
@ -408,14 +401,12 @@ void *realloc(void *ptr, size_t size)
|
|||
size_t size_before;
|
||||
void *ret;
|
||||
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
return memprof_realloc_handler(ptr, size);
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size_before = malloc_usable_size(ptr);
|
||||
ret = memprof_realloc_handler(ptr, size);
|
||||
size = malloc_usable_size(ret);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
/* only count the extra link for new allocations */
|
||||
if (!ptr)
|
||||
|
|
@ -448,13 +439,11 @@ char *strdup(const char *s)
|
|||
size_t size;
|
||||
char *ret;
|
||||
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
return memprof_strdup_handler(s);
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
ret = memprof_strdup_handler(s);
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRDUP);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
|
|
@ -480,15 +469,13 @@ void free(void *ptr)
|
|||
struct memprof_stats *bin;
|
||||
size_t size_before;
|
||||
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || !ptr || (in_memprof & MEMPROF_IN_HANDLER))) {
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || !ptr)) {
|
||||
memprof_free_handler(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size_before = malloc_usable_size(ptr) + sizeof(void *);
|
||||
memprof_free_handler(ptr);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_FREE);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
|
|
@ -508,13 +495,10 @@ char *strndup(const char *s, size_t size)
|
|||
return NULL;
|
||||
|
||||
ret = memprof_strndup_handler(s, size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRNDUP);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
@ -532,13 +516,10 @@ void *valloc(size_t size)
|
|||
return NULL;
|
||||
|
||||
ret = memprof_valloc_handler(size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_VALLOC);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
@ -556,13 +537,10 @@ void *pvalloc(size_t size)
|
|||
return NULL;
|
||||
|
||||
ret = memprof_pvalloc_handler(size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_PVALLOC);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
@ -580,13 +558,10 @@ void *memalign(size_t align, size_t size)
|
|||
return NULL;
|
||||
|
||||
ret = memprof_memalign_handler(align, size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MEMALIGN);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
@ -604,13 +579,10 @@ void *aligned_alloc(size_t align, size_t size)
|
|||
return NULL;
|
||||
|
||||
ret = memprof_aligned_alloc_handler(align, size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_ALIGNED_ALLOC);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
@ -628,16 +600,13 @@ int posix_memalign(void **ptr, size_t align, size_t size)
|
|||
return ENOMEM;
|
||||
|
||||
ret = memprof_posix_memalign_handler(ptr, align, size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
return ret;
|
||||
|
||||
if (ret != 0) // error
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(*ptr) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_POSIX_MEMALIGN);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
|
|||
10
src/applet.c
10
src/applet.c
|
|
@ -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))
|
||||
goto end;
|
||||
|
||||
if (!count)
|
||||
goto end;
|
||||
|
||||
if (!appctx_get_buf(appctx, &appctx->outbuf)) {
|
||||
TRACE_STATE("waiting for appctx outbuf allocation", APPLET_EV_RECV|APPLET_EV_BLK, appctx);
|
||||
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)
|
||||
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)
|
||||
applet_fl_clr(appctx, APPCTX_FL_OUTBLK_FULL);
|
||||
|
||||
|
|
@ -606,12 +608,12 @@ size_t appctx_htx_snd_buf(struct appctx *appctx, struct buffer *buf, size_t coun
|
|||
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)) {
|
||||
appctx_htx->flags |= (buf_htx->flags & HTX_FL_EOM);
|
||||
}
|
||||
|
||||
htx_to_buf(appctx_htx, &appctx->inbuf);
|
||||
htx_to_buf(appctx_htx, &appctx->outbuf);
|
||||
htx_to_buf(buf_htx, buf);
|
||||
ret -= buf_htx->data;
|
||||
end:
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ check_user(struct userlist *ul, const char *user, const char *pass)
|
|||
fprintf(stderr, ", crypt=%s\n", ((ep) ? ep : ""));
|
||||
#endif
|
||||
|
||||
if (ep && u->pass && strcmp(ep, u->pass) == 0)
|
||||
if (ep && strcmp(ep, u->pass) == 0)
|
||||
return 1;
|
||||
else
|
||||
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);
|
||||
break;
|
||||
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;
|
||||
|
||||
|
|
@ -370,11 +370,11 @@ struct server *get_server_ph_post(struct stream *s, const struct server *avoid)
|
|||
len -= plen + 1;
|
||||
|
||||
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.
|
||||
* 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;
|
||||
return NULL; /* oh, no; this is not uri-encoded.
|
||||
* 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)
|
||||
goto hash_done;
|
||||
|
||||
/* Note that if the hash-type doesn't hash the key, we must provide it
|
||||
* as a string representing a number as it will be parsed by read_int64().
|
||||
* Otherwise it's binary. The difference happens on samples returing
|
||||
* ints (e.g. rand()) as well as IP addresses, which, when turned to
|
||||
* binary, are just binary-encoded and cannot be parsed.
|
||||
*/
|
||||
smp = sample_fetch_as_type(px, s->sess, s, SMP_OPT_DIR_REQ | SMP_OPT_FINAL, px->lbprm.expr,
|
||||
((px->lbprm.algo & BE_LB_HASH_FUNC) == BE_LB_HFCN_NONE) ? SMP_T_STR : SMP_T_BIN);
|
||||
smp = sample_fetch_as_type(px, s->sess, s, SMP_OPT_DIR_REQ | SMP_OPT_FINAL, px->lbprm.expr, SMP_T_BIN);
|
||||
if (!smp)
|
||||
return NULL;
|
||||
|
||||
|
|
@ -1818,10 +1811,7 @@ int connect_server(struct stream *s)
|
|||
{
|
||||
struct connection *cli_conn = objt_conn(strm_orig(s));
|
||||
struct connection *srv_conn = NULL;
|
||||
const struct mux_proto_list *mux_proto = NULL;
|
||||
struct server *srv;
|
||||
struct ist name = IST_NULL;
|
||||
struct sample *name_smp;
|
||||
int reuse_mode;
|
||||
int reuse __maybe_unused = 0;
|
||||
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)
|
||||
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))
|
||||
goto skip_reuse;
|
||||
|
||||
|
|
@ -1865,7 +1844,20 @@ int connect_server(struct stream *s)
|
|||
}
|
||||
else {
|
||||
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,
|
||||
s->target, not_first_req);
|
||||
if (err == SF_ERR_INTERNAL)
|
||||
|
|
@ -2087,7 +2079,7 @@ int connect_server(struct stream *s)
|
|||
if (IS_HTX_STRM(s) && srv->use_ssl &&
|
||||
(srv->ssl_ctx.alpn_str || srv->ssl_ctx.npn_str)) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
if (may_start_mux_now) {
|
||||
/* Delay MUX init if an XPRT handshake is required prior. */
|
||||
mux_proto = conn_select_mux_be(srv_conn);
|
||||
if (mux_proto && mux_proto->init_xprt)
|
||||
may_start_mux_now = 0;
|
||||
if (srv && srv->mux_proto && isteq(srv->mux_proto->token, ist("qmux"))) {
|
||||
srv_conn->flags |= (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
|
||||
may_start_mux_now = 0;
|
||||
}
|
||||
|
||||
#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.
|
||||
|
|
|
|||
|
|
@ -125,9 +125,6 @@ int base64dec(const char *in, size_t ilen, char *out, size_t olen) {
|
|||
signed char b;
|
||||
int convlen = 0, i = 0, pad = 0;
|
||||
|
||||
if (!ilen)
|
||||
return 0;
|
||||
|
||||
if (ilen % 4)
|
||||
return -1;
|
||||
|
||||
|
|
|
|||
32
src/cache.c
32
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.
|
||||
*/
|
||||
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;
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
if (entry && entry->expire <= date.tv_sec) {
|
||||
if (delete_expired) {
|
||||
|
|
@ -949,8 +943,8 @@ int http_calc_maxage(struct stream *s, struct cache *cache, int *true_maxage)
|
|||
if (value) {
|
||||
struct buffer *chk = get_trash_chunk();
|
||||
|
||||
chunk_memcat(chk, value, ctx.value.len - (8 + 1));
|
||||
*(b_tail(chk)) = '\0';
|
||||
chunk_memcat(chk, value, ctx.value.len - 8 + 1);
|
||||
chunk_memcat(chk, "", 1);
|
||||
offset = (*chk->area == '"') ? 1 : 0;
|
||||
smaxage = strtol(chk->area + offset, &endptr, 10);
|
||||
if (unlikely(smaxage < 0 || endptr == chk->area + offset))
|
||||
|
|
@ -961,8 +955,8 @@ int http_calc_maxage(struct stream *s, struct cache *cache, int *true_maxage)
|
|||
if (value) {
|
||||
struct buffer *chk = get_trash_chunk();
|
||||
|
||||
chunk_memcat(chk, value, ctx.value.len - (7 + 1));
|
||||
*(b_tail(chk)) = '\0';
|
||||
chunk_memcat(chk, value, ctx.value.len - 7 + 1);
|
||||
chunk_memcat(chk, "", 1);
|
||||
offset = (*chk->area == '"') ? 1 : 0;
|
||||
maxage = strtol(chk->area + offset, &endptr, 10);
|
||||
if (unlikely(maxage < 0 || endptr == chk->area + offset))
|
||||
|
|
@ -1309,7 +1303,7 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
|
|||
if (old) {
|
||||
if (vary_signature)
|
||||
old = get_secondary_entry(cache_tree, old,
|
||||
txn->cache_hash, txn->cache_secondary_hash, 1);
|
||||
txn->cache_secondary_hash, 1);
|
||||
if (old) {
|
||||
if (!old->complete) {
|
||||
/* 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)) {
|
||||
cache_rdlock(cache_tree);
|
||||
sec_entry = get_secondary_entry(cache_tree, res,
|
||||
s->txn.http->cache_hash,
|
||||
s->txn.http->cache_secondary_hash,
|
||||
0);
|
||||
if (!sec_entry) {
|
||||
/* Secondary key miss: release the retained primary entry
|
||||
* and reattach the detached row before returning.
|
||||
*/
|
||||
release_entry(cache_tree, res, 0);
|
||||
shctx_wrlock(shctx);
|
||||
if (detached)
|
||||
shctx_row_reattach(shctx, entry_block);
|
||||
shctx_wrunlock(shctx);
|
||||
}
|
||||
else if (sec_entry != res) {
|
||||
if (sec_entry && sec_entry != res) {
|
||||
/* The wrong row was added to the hot list. */
|
||||
release_entry(cache_tree, res, 0);
|
||||
retain_entry(sec_entry);
|
||||
|
|
@ -3047,7 +3030,6 @@ static int cli_io_handler_show_cache(struct appctx *appctx)
|
|||
node = eb32_lookup_ge(&cache_tree->entries, next_key);
|
||||
if (!node) {
|
||||
ctx->next_key = 0;
|
||||
next_key = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1629,6 +1629,11 @@ static int cfg_parse_global_shm_stats_file(char **args, int section_type,
|
|||
struct proxy *curpx, const struct proxy *defpx,
|
||||
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) {
|
||||
memprintf(err, "'%s' already specified.\n", args[0]);
|
||||
return -1;
|
||||
|
|
@ -1639,6 +1644,7 @@ static int cfg_parse_global_shm_stats_file(char **args, int section_type,
|
|||
return -1;
|
||||
}
|
||||
|
||||
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
||||
global.shm_stats_file = strdup(args[1]);
|
||||
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,
|
||||
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) {
|
||||
memprintf(err, "'%s' already specified.\n", args[0]);
|
||||
return -1;
|
||||
|
|
@ -1657,6 +1668,7 @@ static int cfg_parse_global_shm_stats_file_max_objects(char **args, int section_
|
|||
return -1;
|
||||
}
|
||||
|
||||
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
||||
shm_stats_file_max_objects = atoi(args[1]);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1495,7 +1495,7 @@ static int bind_parse_tls_ticket_keys(char **args, int cur_arg, struct proxy *px
|
|||
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) {
|
||||
memprintf(err, "'%s' : allocation error", args[cur_arg+1]);
|
||||
goto fail;
|
||||
|
|
|
|||
|
|
@ -1394,7 +1394,7 @@ int parse_cfg(const struct cfgfile *cfg)
|
|||
global.cfg_curr_line = 0;
|
||||
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");
|
||||
err_code = -1;
|
||||
goto err;
|
||||
|
|
@ -1442,7 +1442,7 @@ next_line:
|
|||
char *newline;
|
||||
int newlinesize = linesize * 2;
|
||||
|
||||
newline = realloc(thisline, array_size_or_fail(sizeof(*thisline), newlinesize));
|
||||
newline = realloc(thisline, sizeof(*thisline) * newlinesize);
|
||||
if (newline == NULL) {
|
||||
ha_alert("parsing [%s:%d]: line too long, cannot allocate memory.\n",
|
||||
file, linenum);
|
||||
|
|
@ -2486,17 +2486,16 @@ init_proxies_list_stage1:
|
|||
/* At this point, target names have already been resolved. */
|
||||
/***********************************************************/
|
||||
|
||||
for (i = 0; i < global.nbthread; i++) {
|
||||
idle_conn_srv[i] = EB_ROOT;
|
||||
idle_conn_task[i] = task_new_on(i);
|
||||
if (!idle_conn_task[i]) {
|
||||
ha_alert("parsing : failed to allocate global idle connection task.\n");
|
||||
cfgerr++;
|
||||
}
|
||||
else {
|
||||
idle_conn_task[i]->process = srv_cleanup_idle_conns;
|
||||
idle_conn_task[i]->context = NULL;
|
||||
idle_conn_task = task_new_anywhere();
|
||||
if (!idle_conn_task) {
|
||||
ha_alert("parsing : failed to allocate global idle connection task.\n");
|
||||
cfgerr++;
|
||||
}
|
||||
else {
|
||||
idle_conn_task->process = srv_cleanup_idle_conns;
|
||||
idle_conn_task->context = NULL;
|
||||
|
||||
for (i = 0; i < global.nbthread; i++) {
|
||||
idle_conns[i].cleanup_task = task_new_on(i);
|
||||
if (!idle_conns[i].cleanup_task) {
|
||||
ha_alert("parsing : failed to allocate idle connection tasks for thread '%d'.\n", i);
|
||||
|
|
|
|||
28
src/check.c
28
src/check.c
|
|
@ -232,9 +232,6 @@ static void check_trace(enum trace_level level, uint64_t mask,
|
|||
chunk_appendf(&trace_buf, " sc=%p(0x%08x)", check->sc, check->sc->flags);
|
||||
}
|
||||
|
||||
if (check->type != PR_O2_TCPCHK_CHK)
|
||||
return;
|
||||
|
||||
if (mask & CHK_EV_TCPCHK) {
|
||||
const char *type;
|
||||
|
||||
|
|
@ -815,7 +812,7 @@ void chk_report_conn_err(struct check *check, int errno_bck, int expired)
|
|||
}
|
||||
|
||||
errno = unclean_errno(errno_bck);
|
||||
if (conn && !errno)
|
||||
if (conn && errno)
|
||||
retrieve_errno_from_socket(conn);
|
||||
|
||||
TRACE_ENTER(CHK_EV_HCHK_END|CHK_EV_HCHK_ERR, check, 0, 0, (size_t[]){expired});
|
||||
|
|
@ -1407,20 +1404,7 @@ struct task *process_chk_conn(struct task *t, void *context, unsigned int state)
|
|||
|
||||
check_release_buf(check, &check->bi);
|
||||
check_release_buf(check, &check->bo);
|
||||
|
||||
if (unlikely(LIST_INLIST(&check->check_queue))) {
|
||||
/*
|
||||
* If that check is still queued, and we're about to
|
||||
* purge it, then remove it from the queue, as it is
|
||||
* about to be freed.
|
||||
* This can happen if a server is deleted while the check
|
||||
* is queued.
|
||||
*/
|
||||
if (check->state & CHK_ST_PURGE)
|
||||
LIST_DEL_INIT(&check->check_queue);
|
||||
}
|
||||
else
|
||||
_HA_ATOMIC_DEC(&th_ctx->running_checks);
|
||||
_HA_ATOMIC_DEC(&th_ctx->running_checks);
|
||||
_HA_ATOMIC_DEC(&th_ctx->active_checks);
|
||||
check->state &= ~(CHK_ST_INPROGRESS|CHK_ST_IN_ALLOC|CHK_ST_OUT_ALLOC);
|
||||
check->state &= ~CHK_ST_READY;
|
||||
|
|
@ -1579,7 +1563,6 @@ void free_check(struct check *check)
|
|||
ha_free(&check->tcpcheck);
|
||||
}
|
||||
|
||||
LIST_DEL_INIT(&check->check_queue);
|
||||
pool_free(pool_head_uniqueid, istptr(check->unique_id));
|
||||
check->unique_id = IST_NULL;
|
||||
ha_free(&check->pool_conn_name);
|
||||
|
|
@ -1699,7 +1682,7 @@ static int start_checks()
|
|||
for (px = proxies_list; px; px = px->next) {
|
||||
for (s = px->srv; s; s = s->next) {
|
||||
if ((px->options2 & PR_O2_USE_SBUF_CHECK) &&
|
||||
(s->check.tcpcheck->rs && s->check.tcpcheck->rs->flags & TCPCHK_RULES_MAY_USE_SBUF))
|
||||
(s->check.tcpcheck->rs->flags & TCPCHK_RULES_MAY_USE_SBUF))
|
||||
s->check.state |= CHK_ST_USE_SMALL_BUFF;
|
||||
|
||||
if (s->check.state & CHK_ST_CONFIGURED) {
|
||||
|
|
@ -1816,9 +1799,6 @@ int init_srv_check(struct server *srv)
|
|||
if (!srv->do_check || !(srv->proxy->cap & PR_CAP_BE))
|
||||
goto out;
|
||||
|
||||
if (!srv->check.type && (srv->proxy->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK)
|
||||
goto init;
|
||||
|
||||
check_type = srv->check.tcpcheck->rs->flags & TCPCHK_RULES_PROTO_CHK;
|
||||
|
||||
if (!(srv->flags & SRV_F_DYNAMIC)) {
|
||||
|
|
@ -1959,7 +1939,7 @@ int init_srv_check(struct server *srv)
|
|||
}
|
||||
|
||||
init:
|
||||
err = init_check(&srv->check, srv->check.type ? srv->check.type : (srv->proxy->options2 & PR_O2_CHK_ANY));
|
||||
err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
|
||||
if (err) {
|
||||
ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
|
||||
proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
|
||||
|
|
|
|||
|
|
@ -145,15 +145,14 @@ struct buffer *get_small_trash_chunk(void)
|
|||
|
||||
/* Returns a trash chunk accordingly to the requested size. This function may
|
||||
* fail if the requested size is too big or if the large chunks are not
|
||||
* configured. Note that requesting a size larger than the largest available
|
||||
* buffer will result in NULL being returned, so better be conservative when
|
||||
* requesting the size and plan to use get_larger_trash_chunk() later if not
|
||||
* sufficient.
|
||||
* configured.
|
||||
*/
|
||||
struct buffer *get_trash_chunk_sz(size_t size)
|
||||
{
|
||||
if (likely(size <= trash_size))
|
||||
if (likely(size > small_trash_size && size <= trash_size))
|
||||
return get_trash_chunk();
|
||||
else if (small_trash_size && size <= small_trash_size)
|
||||
return get_small_trash_chunk();
|
||||
else if (large_trash_size && size <= large_trash_size)
|
||||
return get_large_trash_chunk();
|
||||
else
|
||||
|
|
|
|||
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 (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
|
||||
* Otherwise, the payload is empty, just
|
||||
*/
|
||||
if (b_data(buf) > len)
|
||||
b_sub(buf, len+1);
|
||||
else
|
||||
b_sub(buf, len);
|
||||
/* end of payload was reached, rewind before the previous \n and replace it by a \0 */
|
||||
b_sub(buf, strlen(appctx->cli_ctx.payload_pat) + 2);
|
||||
*b_tail(buf) = '\0';
|
||||
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
|
||||
* 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) {
|
||||
ha_warning("Failed to allocate memory to transfer socket information\n");
|
||||
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
|
||||
* 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);
|
||||
if (ret < 0)
|
||||
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 bind_conf *bind_conf = __objt_listener(conn->target)->bind_conf;
|
||||
struct ist alpn = IST_NULL;
|
||||
const struct mux_ops *old_mux, *new_mux;
|
||||
void *old_mux_ctx;
|
||||
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) {
|
||||
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;
|
||||
|
||||
/* No mux found */
|
||||
|
|
@ -319,29 +318,6 @@ int conn_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
|
|||
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
|
||||
* <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.
|
||||
|
|
@ -354,14 +330,14 @@ int conn_install_mux_fe(struct connection *conn, void *ctx)
|
|||
if (bind_conf->mux_proto)
|
||||
mux_ops = bind_conf->mux_proto->mux;
|
||||
else {
|
||||
struct ist alpn;
|
||||
struct ist mux_proto;
|
||||
const char *alpn_str = NULL;
|
||||
int alpn_len = 0;
|
||||
int 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);
|
||||
mux_ops = conn_get_best_mux(conn, IST_NULL, alpn, PROTO_SIDE_FE, mode);
|
||||
mux_proto = ist2(alpn_str, alpn_len);
|
||||
mux_ops = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_FE, mode);
|
||||
if (!mux_ops)
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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
|
||||
* <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>,
|
||||
|
|
@ -464,20 +380,20 @@ int conn_install_mux_be(struct connection *conn, void *ctx, struct session *sess
|
|||
mux_ops = force_mux_ops;
|
||||
}
|
||||
else {
|
||||
struct ist alpn;
|
||||
struct ist mux_proto;
|
||||
const char *alpn_str = NULL;
|
||||
int alpn_len = 0;
|
||||
int mode = conn_pr_mode_to_proto_mode(prx->mode);
|
||||
|
||||
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_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)
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -518,15 +434,15 @@ int conn_install_mux_chk(struct connection *conn, void *ctx, struct session *ses
|
|||
if (check->mux_proto)
|
||||
mux_ops = check->mux_proto->mux;
|
||||
else {
|
||||
struct ist alpn;
|
||||
struct ist mux_proto;
|
||||
const char *alpn_str = NULL;
|
||||
int alpn_len = 0;
|
||||
int 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);
|
||||
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)
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -847,43 +763,6 @@ int xprt_add_hs(struct connection *conn)
|
|||
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
|
||||
* 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.
|
||||
|
|
@ -2112,7 +1991,7 @@ void list_mux_proto(FILE *out)
|
|||
fprintf(out, "Available multiplexer protocols :\n"
|
||||
"(protocols marked as <default> cannot be specified using 'proto' keyword)\n");
|
||||
list_for_each_entry(item, &mux_proto_list.list, list) {
|
||||
proto = item->mux_proto;
|
||||
proto = item->token;
|
||||
|
||||
if (item->mode == PROTO_MODE_ANY)
|
||||
mode = "TCP|HTTP";
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
/* cpu_policy_conf flags */
|
||||
#define CPU_POLICY_ONE_THREAD_PER_CORE (1 << 0)
|
||||
#define CPU_POLICY_SET_IN_CONFIG (1 << 1)
|
||||
|
||||
/* cpu_policy_conf affinities */
|
||||
#define CPU_AFFINITY_PER_GROUP (1 << 0)
|
||||
|
|
@ -1107,23 +1106,14 @@ static int cpu_policy_first_usable_node(int policy, int tmin, int tmax, int gmin
|
|||
int grp, thr;
|
||||
int thr_count = 0;
|
||||
|
||||
if (!global.numa_cpu_mapping) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when numa-cpu-mapping is set.\n");
|
||||
if (!global.numa_cpu_mapping)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (global.nbthread) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||
if (global.nbthread)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cpu_mask_forced) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy first-numa-node is ignored when CPUs were externally restricted.\n");
|
||||
if (cpu_mask_forced)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* determine first and second nodes with usable CPUs */
|
||||
for (cpu = 0; cpu <= cpu_topo_lastcpu; cpu++) {
|
||||
|
|
@ -1515,17 +1505,11 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
|
|||
int cid;
|
||||
int div;
|
||||
|
||||
if (global.nbthread) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||
if (global.nbthread)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (global.nbtgroups) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when thread-groups is set.\n");
|
||||
if (global.nbtgroups)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ha_cpuset_zero(&visited_cl_set);
|
||||
|
||||
|
|
@ -1536,8 +1520,7 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
|
|||
div = ha_cpu_policy[policy].arg;
|
||||
div = div ? div : 1;
|
||||
|
||||
while (global.nbtgroups < MAX_TGROUPS && (global.nbthread < MAX_THREADS) &&
|
||||
(global.thread_limit == 0 || global.nbthread < global.thread_limit)) {
|
||||
while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS) {
|
||||
ha_cpuset_zero(&node_cpu_set);
|
||||
ha_cpuset_zero(&touse_tsid);
|
||||
ha_cpuset_zero(&touse_ccx);
|
||||
|
|
@ -1567,10 +1550,6 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
|
|||
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
|
||||
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
|
||||
thr_count++;
|
||||
|
||||
if (global.thread_limit != 0 &&
|
||||
thr_count + global.nbthread >= global.thread_limit)
|
||||
break;
|
||||
}
|
||||
|
||||
/* now cid = next cluster_id or -1 if none; cpu_count is the
|
||||
|
|
@ -1614,17 +1593,11 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
|
|||
int l3id;
|
||||
int div;
|
||||
|
||||
if (global.nbthread) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||
if (global.nbthread)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (global.nbtgroups) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when thread-groups is set.\n");
|
||||
if (global.nbtgroups)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ha_cpuset_zero(&visited_ccx_set);
|
||||
|
||||
|
|
@ -1635,8 +1608,7 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
|
|||
div = ha_cpu_policy[policy].arg;
|
||||
div = div ? div : 1;
|
||||
|
||||
while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS &&
|
||||
(global.thread_limit == 0 || global.nbthread < global.thread_limit)) {
|
||||
while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS) {
|
||||
ha_cpuset_zero(&node_cpu_set);
|
||||
ha_cpuset_zero(&touse_tsid);
|
||||
ha_cpuset_zero(&touse_ccx);
|
||||
|
|
@ -1666,9 +1638,6 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
|
|||
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
|
||||
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
|
||||
thr_count++;
|
||||
if (global.thread_limit != 0 &&
|
||||
global.nbthread + thr_count >= global.thread_limit)
|
||||
break;
|
||||
}
|
||||
|
||||
/* now l3id = next L3 ID or -1 if none; cpu_count is the
|
||||
|
|
@ -1703,17 +1672,8 @@ static int cpu_policy_performance(int policy, int tmin, int tmax, int gmin, int
|
|||
int cpu, cluster;
|
||||
int capa;
|
||||
|
||||
if (global.nbthread) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||
if (global.nbthread || global.nbtgroups)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (global.nbtgroups) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when thread-groups is set.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sort clusters by average reverse capacity */
|
||||
cpu_cluster_reorder_by_avg_capa(ha_cpu_clusters, cpu_topo_maxcpus);
|
||||
|
|
@ -1757,17 +1717,8 @@ static int cpu_policy_efficiency(int policy, int tmin, int tmax, int gmin, int g
|
|||
int cpu, cluster;
|
||||
int capa;
|
||||
|
||||
if (global.nbthread) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||
if (global.nbthread || global.nbtgroups)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (global.nbtgroups) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when thread-groups is set.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sort clusters by average reverse capacity */
|
||||
cpu_cluster_reorder_by_avg_capa(ha_cpu_clusters, cpu_topo_maxcpus);
|
||||
|
|
@ -1808,17 +1759,8 @@ static int cpu_policy_resource(int policy, int tmin, int tmax, int gmin, int gma
|
|||
int cpu, cluster;
|
||||
int capa;
|
||||
|
||||
if (global.nbthread) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||
if (global.nbthread || global.nbtgroups)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (global.nbtgroups) {
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when thread-groups is set.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sort clusters by reverse capacity */
|
||||
cpu_cluster_reorder_by_capa(ha_cpu_clusters, cpu_topo_maxcpus);
|
||||
|
|
@ -1853,8 +1795,6 @@ int cpu_apply_policy(int tmin, int tmax, int gmin, int gmax, char **err)
|
|||
|
||||
if (cpu_map_configured()) {
|
||||
/* nothing to do */
|
||||
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||
ha_notice("cpu-policy is ignored when cpu-map is set.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2410,7 +2350,6 @@ static int cfg_parse_cpu_policy(char **args, int section_type, struct proxy *cur
|
|||
for (i = 0; ha_cpu_policy[i].name; i++) {
|
||||
if (strcmp(args[1], ha_cpu_policy[i].name) == 0) {
|
||||
cpu_policy_conf.cpu_policy = i;
|
||||
cpu_policy_conf.flags |= CPU_POLICY_SET_IN_CONFIG;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
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 *de, *tree_de;
|
||||
struct dict_entry *de;
|
||||
struct ebpt_node *n;
|
||||
|
||||
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);
|
||||
n = ebis_insert(&d->values, &de->value);
|
||||
tree_de = container_of(n, struct dict_entry, value);
|
||||
if (tree_de == de)
|
||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||
else {
|
||||
/* another entry was already there, we'll return it, kill
|
||||
* ours and bump the other's refcount before returning it.
|
||||
*/
|
||||
HA_ATOMIC_INC(&tree_de->refcount);
|
||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||
if (n != &de->value) {
|
||||
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)
|
||||
return;
|
||||
|
||||
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
||||
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0) {
|
||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
||||
ebpt_delete(&de->value);
|
||||
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;
|
||||
|
||||
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) {
|
||||
ns->counters->snd_error++;
|
||||
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 ret = -1;
|
||||
ssize_t ret = -1;
|
||||
|
||||
if (ns->dgram) {
|
||||
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.addr.to = *sk;
|
||||
HA_SPIN_INIT(&dgram->conn.lock);
|
||||
ns->dgram = dgram;
|
||||
|
||||
dgram->ofs_req = ~0; /* init ring offset */
|
||||
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");
|
||||
goto out;
|
||||
}
|
||||
ns->dgram = dgram;
|
||||
return 0;
|
||||
out:
|
||||
dns_ring_free(dgram->ring_req);
|
||||
|
|
@ -913,7 +913,6 @@ static int dns_session_init(struct appctx *appctx)
|
|||
return 0;
|
||||
|
||||
error:
|
||||
sockaddr_free(&addr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1346,8 +1345,8 @@ int dns_stream_init(struct dns_nameserver *ns, struct server *srv)
|
|||
{
|
||||
struct dns_stream_server *dss = NULL;
|
||||
|
||||
dss = calloc(1, sizeof(*dss));
|
||||
if (!dss) {
|
||||
dss = calloc(1, sizeof(*dss));
|
||||
if (!dss) {
|
||||
ha_alert("memory allocation error initializing dns tcp server '%s'.\n", srv->id);
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -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 */
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -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 */
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1426,7 +1425,7 @@ int init_dns_buffers()
|
|||
if (!dns_msg_trash)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void deinit_dns_buffers()
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ static void usermsgs_put(const struct ist *msg)
|
|||
{
|
||||
/* Allocate the buffer if not already done. */
|
||||
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)
|
||||
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_SYSLOG: svmode = "syslog"; 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_SPOP: svmode = "spop"; break;
|
||||
/* 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;
|
||||
|
||||
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++;
|
||||
if (fcgi_conf)
|
||||
continue;
|
||||
|
|
|
|||
2
src/fd.c
2
src/fd.c
|
|
@ -1166,7 +1166,7 @@ int init_pollers()
|
|||
struct poller *bp;
|
||||
|
||||
/* 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);
|
||||
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.
|
||||
* 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;
|
||||
|
||||
word.ptr = value->ptr - 1; // -1 for next loop's pre-increment
|
||||
p = value->ptr;
|
||||
e = value->ptr + value->len;
|
||||
value->len = 0;
|
||||
h1m->flags &= ~(H1_MF_UPG_WEBSOCKET|H1_MF_UPG_H2C);
|
||||
|
||||
word.ptr = value.ptr - 1; // -1 for next loop's pre-increment
|
||||
e = istend(value);
|
||||
|
||||
while (++word.ptr < e) {
|
||||
/* 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")))
|
||||
h1m->flags |= H1_MF_UPG_WEBSOCKET;
|
||||
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) {
|
||||
/* 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;
|
||||
word.ptr = n;
|
||||
}
|
||||
|
||||
if (istlen(*value))
|
||||
h1m->flags |= H1_MF_UPG_HDR;
|
||||
}
|
||||
|
||||
/* 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:
|
||||
http_msg_rpcode:
|
||||
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';
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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"))) {
|
||||
ret = h1_parse_xfer_enc_header(h1m, v);
|
||||
if (ret < 0) {
|
||||
|
|
@ -1021,11 +983,7 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
|||
}
|
||||
}
|
||||
else if (isteqi(n, ist("upgrade"))) {
|
||||
h1_parse_upgrade_header(h1m, &v);
|
||||
if (!v.len) {
|
||||
/* skip it */
|
||||
break;
|
||||
}
|
||||
h1_parse_upgrade_header(h1m, v);
|
||||
}
|
||||
else if (!(h1m->flags & H1_MF_RESP) && isteqi(n, ist("host"))) {
|
||||
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])
|
||||
{
|
||||
/* generate a random websocket key */
|
||||
uint64_t rand1, rand2;
|
||||
const uint64_t rand1 = ha_random64(), rand2 = ha_random64();
|
||||
char key[16];
|
||||
|
||||
ha_random64_pair_hashed(&rand1, &rand2);
|
||||
memcpy(key, &rand1, 8);
|
||||
memcpy(&key[8], &rand2, 8);
|
||||
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)
|
||||
flags |= HTX_SL_F_CONN_UPG;
|
||||
if (h1m->flags & H1_MF_UPG_HDR)
|
||||
flags |= HTX_SL_F_UPG_HDR;
|
||||
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 */
|
||||
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_HDR)) == H1_MF_UPG_HDR) {
|
||||
flags |= h1m_htx_sl_flags(h1m);
|
||||
|
||||
/* Remove Upgrade header in problematic cases :
|
||||
* - "h2c" or "h2" token specified as token
|
||||
*/
|
||||
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_H2C)) == (H1_MF_CONN_UPG|H1_MF_UPG_H2C)) {
|
||||
int i;
|
||||
|
||||
for (i = 0; hdrs[i].n.len; i++) {
|
||||
if (isteqi(hdrs[i].n, ist("upgrade")))
|
||||
hdrs[i].v = IST_NULL;
|
||||
}
|
||||
h1m->flags &=~ (H1_MF_CONN_UPG|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);
|
||||
if (!sl || !htx_add_all_headers(htx, hdrs))
|
||||
goto error;
|
||||
|
|
|
|||
104
src/h3.c
104
src/h3.c
|
|
@ -212,33 +212,8 @@ static ssize_t h3_init_uni_stream(struct h3c *h3c, struct qcs *qcs,
|
|||
break;
|
||||
|
||||
case H3_UNI_S_T_PUSH:
|
||||
if (!conn_is_back(qcs->qcc->conn)) {
|
||||
/* RFC 9114 6.2.2. Push Streams
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
/* TODO not supported for the moment */
|
||||
h3s->type = H3S_T_PUSH;
|
||||
break;
|
||||
|
||||
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_GOAWAY:
|
||||
case H3_FT_MAX_PUSH_ID:
|
||||
/* RFC 9114 7.2.3. CANCEL_PUSH
|
||||
*
|
||||
* A CANCEL_PUSH frame is sent on the control stream. Receiving a
|
||||
|
|
@ -403,6 +379,13 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
|
|||
* control stream as a connection error of type H3_FRAME_UNEXPECTED.
|
||||
*/
|
||||
|
||||
/* RFC 9114 7.2.7. MAX_PUSH_ID
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (h3s->type != H3S_T_CTRL)
|
||||
ret = H3_ERR_FRAME_UNEXPECTED;
|
||||
else if (!(h3c->flags & H3_CF_SETTINGS_RECV))
|
||||
|
|
@ -429,33 +412,14 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
|
|||
|
||||
case H3_FT_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
|
||||
* receipt of a PUSH_PROMISE 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;
|
||||
break;
|
||||
|
||||
case H3_FT_MAX_PUSH_ID:
|
||||
/* RFC 9114 7.2.7. MAX_PUSH_ID
|
||||
*
|
||||
* 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;
|
||||
/* TODO server-side only. */
|
||||
ret = H3_ERR_FRAME_UNEXPECTED;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -1966,25 +1930,6 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
|||
h3s->st_req = H3S_ST_REQ_TRAILERS;
|
||||
}
|
||||
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:
|
||||
ret = h3_parse_goaway_frm(qcs->qcc->ctx, b, flen);
|
||||
if (ret < 0) {
|
||||
|
|
@ -1993,6 +1938,12 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
|||
goto err;
|
||||
}
|
||||
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:
|
||||
ret = h3_parse_settings_frm(qcs->qcc->ctx, b, flen);
|
||||
if (ret < 0) {
|
||||
|
|
@ -2002,25 +1953,6 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
|||
}
|
||||
h3c->flags |= H3_CF_SETTINGS_RECV;
|
||||
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:
|
||||
/* RFC 9114 Section 9. Extensions to HTTP/3
|
||||
*
|
||||
|
|
|
|||
212
src/haproxy.c
212
src/haproxy.c
|
|
@ -210,6 +210,7 @@ struct global global = {
|
|||
#endif
|
||||
/* by default allow clients which use a privileged port for TCP only */
|
||||
.clt_privileged_ports = HA_PROTO_TCP,
|
||||
.maxthrpertgroup = DEF_MAX_THREADS_PER_GROUP,
|
||||
/* others NULL OK */
|
||||
};
|
||||
|
||||
|
|
@ -274,7 +275,6 @@ unsigned int deprecated_directives_allowed = 0;
|
|||
/* mapped storage for collected libs */
|
||||
void *lib_storage = NULL;
|
||||
size_t lib_size = 0;
|
||||
char *lib_output_file = NULL;
|
||||
|
||||
int check_kw_experimental(struct cfg_keyword *kw, const char *file, int linenum,
|
||||
char **errmsg)
|
||||
|
|
@ -785,9 +785,6 @@ static void usage(char *name)
|
|||
#if defined(HA_HAVE_DUMP_LIBS)
|
||||
" -dL dumps loaded object files after config checks\n"
|
||||
#endif
|
||||
#if defined(HA_HAVE_DUMP_LIBS) && defined(HA_HAVE_DL_ITERATE_PHDR)
|
||||
" -dA[file] collects libs into a tar file at <file>\n"
|
||||
#endif
|
||||
#if defined(USE_CPU_AFFINITY)
|
||||
" -dc dumps the list of selected and evicted CPUs\n"
|
||||
#endif
|
||||
|
|
@ -1631,16 +1628,6 @@ void haproxy_init_args(int argc, char **argv)
|
|||
#if defined(HA_HAVE_DUMP_LIBS)
|
||||
else if (*flag == 'd' && flag[1] == 'L')
|
||||
arg_mode |= MODE_DUMP_LIBS;
|
||||
# if defined(HA_HAVE_DL_ITERATE_PHDR)
|
||||
else if (*flag == 'd' && flag[1] == 'A') {
|
||||
lib_output_file = flag + 2;
|
||||
if (!*lib_output_file) {
|
||||
ha_alert("-dA: missing output file name\n");
|
||||
exit(1);
|
||||
}
|
||||
arg_mode |= MODE_DUMP_LIBS; // stop on libs dump
|
||||
}
|
||||
# endif /* HA_HAVE_DL_ITERATE_PHDR */
|
||||
#endif
|
||||
else if (*flag == 'd' && flag[1] == 'K') {
|
||||
arg_mode |= MODE_DUMP_KWD;
|
||||
|
|
@ -1726,7 +1713,7 @@ void haproxy_init_args(int argc, char **argv)
|
|||
oldpids_sig = SIGTERM; /* terminate immediately */
|
||||
while (argc > 1 && argv[1][0] != '-') {
|
||||
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) {
|
||||
ha_alert("Cannot allocate old pid : out of memory.\n");
|
||||
exit(1);
|
||||
|
|
@ -1939,30 +1926,20 @@ static void dump_registered_keywords(void)
|
|||
|
||||
/* 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
|
||||
* limitations. The function prefers RAND_bytes() if available, otherwise falls
|
||||
* back to ha_random64_pair_hashed().
|
||||
* limitations.
|
||||
*/
|
||||
static void generate_random_cluster_secret()
|
||||
{
|
||||
/* used as a default random cluster-secret if none defined. */
|
||||
union {
|
||||
uint64_t by64[2];
|
||||
uchar by8[16];
|
||||
} rand;
|
||||
uint64_t rand;
|
||||
|
||||
/* The caller must not overwrite an already defined secret. */
|
||||
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));
|
||||
rand = ha_random64();
|
||||
memcpy(global.cluster_secret + sizeof(rand), &rand, sizeof(rand));
|
||||
cluster_secret_isset = 1;
|
||||
}
|
||||
|
||||
|
|
@ -2152,6 +2129,9 @@ static void step_init_1()
|
|||
if (init_acl() != 0)
|
||||
exit(1);
|
||||
|
||||
/* Initialise lua. */
|
||||
hlua_init();
|
||||
|
||||
/* set modes given from cmdline */
|
||||
global.mode |= (arg_mode & (MODE_DAEMON | MODE_MWORKER | MODE_FOREGROUND | MODE_VERBOSE
|
||||
| MODE_QUIET | MODE_CHECK | MODE_DEBUG | MODE_ZERO_WARNING
|
||||
|
|
@ -2326,17 +2306,6 @@ static void step_init_2(int argc, char** argv)
|
|||
|
||||
#if defined(HA_HAVE_DUMP_LIBS)
|
||||
if (global.mode & MODE_DUMP_LIBS && !master) {
|
||||
# if defined(HA_HAVE_DL_ITERATE_PHDR)
|
||||
if (lib_output_file) {
|
||||
/* we'll dump everything to lib_output_file */
|
||||
if (copy_libs_to_file() < 0)
|
||||
deinit_and_exit(1);
|
||||
/* release memory if no longer needed */
|
||||
if ((global.tune.options & (GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS)) !=
|
||||
(GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS))
|
||||
free_collected_libs();
|
||||
}
|
||||
# endif
|
||||
qfprintf(stdout, "List of loaded object files:\n");
|
||||
chunk_reset(&trash);
|
||||
if (dump_libs(&trash, ((arg_mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_VERBOSE)))
|
||||
|
|
@ -2810,7 +2779,6 @@ void deinit(void)
|
|||
struct cfg_postparser *pprs, *pprsb;
|
||||
char **tmp = init_env;
|
||||
int cur_fd;
|
||||
int i;
|
||||
|
||||
/* the user may want to skip this phase */
|
||||
if (global.tune.options & GTUNE_QUICK_EXIT)
|
||||
|
|
@ -2887,10 +2855,8 @@ void deinit(void)
|
|||
ha_free(&global.server_state_base);
|
||||
ha_free(&global.server_state_file);
|
||||
ha_free(&global.stats_file);
|
||||
for (i = 0; i < global.nbthread; i++) {
|
||||
task_destroy(idle_conn_task[i]);
|
||||
idle_conn_task[i] = NULL;
|
||||
}
|
||||
task_destroy(idle_conn_task);
|
||||
idle_conn_task = NULL;
|
||||
|
||||
list_for_each_entry_safe(log, logb, &global.loggers, list) {
|
||||
LIST_DEL_INIT(&log->list);
|
||||
|
|
@ -2984,7 +2950,7 @@ void deinit(void)
|
|||
free(init_env);
|
||||
}
|
||||
free(progname);
|
||||
free_collected_libs();
|
||||
|
||||
} /* end deinit() */
|
||||
|
||||
__attribute__((noreturn)) void deinit_and_exit(int status)
|
||||
|
|
@ -3122,7 +3088,6 @@ void *run_thread_poll_loop(void *data)
|
|||
ha_set_thread(data);
|
||||
set_thread_cpu_affinity();
|
||||
clock_set_local_source();
|
||||
ha_random_seed_thread();
|
||||
|
||||
#ifdef USE_THREAD
|
||||
ha_thread_info[tid].pth_id = ha_get_pthread_id(tid);
|
||||
|
|
@ -3301,123 +3266,6 @@ static void set_identity(const char *program_name)
|
|||
#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 *dir, *chroot_dir;
|
||||
int error, chroot_error;
|
||||
|
||||
error = chroot_error = 0;
|
||||
dir = chroot_dir = path;
|
||||
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";
|
||||
dir = mkdtemp(tmpdir);
|
||||
if (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));
|
||||
chroot_dir = ".";
|
||||
if (!error)
|
||||
chroot_error = chroot(".");
|
||||
} else if (strcmp(path, "/") != 0) {
|
||||
chroot_error = chroot(path);
|
||||
}
|
||||
#ifdef CLONE_NEWUSER
|
||||
/* If the chroot failed because of insufficient privileges and
|
||||
* unshare(CLONE_NEWUSER) is available, we attempt it to gain the
|
||||
* abilty to chroot as an unprivileged user. If that worked, we
|
||||
* try the chroot again.
|
||||
*/
|
||||
if (chroot_error && errno == EPERM) {
|
||||
uid_t euid = geteuid();
|
||||
gid_t egid = getegid();
|
||||
if (unshare(CLONE_NEWUSER) == 0) {
|
||||
setup_user_ns(euid, egid);
|
||||
chroot_error = chroot(chroot_dir);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!error && !chroot_error)
|
||||
error = chdir("/");
|
||||
|
||||
if (error || chroot_error) {
|
||||
ha_alert("[%s.main()] Cannot chroot(%s).\n", prog, dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct rlimit limit;
|
||||
|
|
@ -3734,34 +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]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Must chroot and setgid/setuid in the children */
|
||||
/* chroot if needed */
|
||||
if (global.chroot != NULL && do_chroot(argv[0], global.chroot) != 0) {
|
||||
if (nb_oldpids)
|
||||
tell_old_pids(SIGTTIN);
|
||||
protocol_unbind_all();
|
||||
exit(1);
|
||||
if (global.chroot != NULL) {
|
||||
if (chroot(global.chroot) == -1 || chdir("/") == -1) {
|
||||
ha_alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot);
|
||||
if (nb_oldpids)
|
||||
tell_old_pids(SIGTTIN);
|
||||
protocol_unbind_all();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
ha_free(&global.chroot);
|
||||
|
|
|
|||
17
src/haterm.c
17
src/haterm.c
|
|
@ -12,7 +12,7 @@
|
|||
#include <haproxy/istbuf.h>
|
||||
#include <haproxy/pipe.h>
|
||||
#include <haproxy/pool.h>
|
||||
#include <haproxy/proxy.h>
|
||||
#include <haproxy/proxy-t.h>
|
||||
#include <haproxy/sc_strm.h>
|
||||
#include <haproxy/stconn-t.h>
|
||||
#include <haproxy/stream.h>
|
||||
|
|
@ -788,7 +788,7 @@ static void hstream_parse_uri(struct ist uri, struct hstream *hs)
|
|||
} while (*next);
|
||||
|
||||
if (use_rand)
|
||||
result = ((long long)statistical_prng() * result) / 0xFFFFFFFFU;
|
||||
result = ((long long)ha_random64() * result) / ((long long)RAND_MAX + 1);
|
||||
|
||||
switch (*arg) {
|
||||
case 's':
|
||||
|
|
@ -1223,22 +1223,11 @@ static int hstream_build_responses(void)
|
|||
#if defined(USE_LINUX_SPLICE)
|
||||
static void hstream_init_splicing(void)
|
||||
{
|
||||
struct proxy *px;
|
||||
unsigned int pipesize = 65536;
|
||||
int haterm_used = 0;
|
||||
|
||||
if (!(global.tune.options & GTUNE_USE_SPLICE) || !global.maxpipes)
|
||||
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)
|
||||
pipesize = global.tune.pipesize;
|
||||
|
||||
|
|
@ -1268,7 +1257,7 @@ static void hstream_init_splicing(void)
|
|||
master_pipesize = 0;
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include <haproxy/chunk.h>
|
||||
#include <haproxy/errors.h>
|
||||
#include <haproxy/global.h>
|
||||
#include <haproxy/openssl-compat.h>
|
||||
#include <haproxy/version.h>
|
||||
|
||||
static int haterm_debug;
|
||||
|
|
@ -29,9 +28,7 @@ static void haterm_usage(char *name)
|
|||
" -d : enable the traces for all http protocols\n"
|
||||
" -dS : disables splice() usage even when available\n"
|
||||
" -dZ : disable zero-copy forwarding\n"
|
||||
#if defined(USE_QUIC)
|
||||
" --" QUIC_BIND_LONG_OPT " <opts> : append options to QUIC \"bind\" lines\n"
|
||||
#endif
|
||||
" --" TCP_BIND_LONG_OPT " <opts> : append options to TCP \"bind\" lines\n"
|
||||
, name);
|
||||
exit(1);
|
||||
|
|
@ -174,7 +171,7 @@ void haproxy_init_args(int argc, char **argv)
|
|||
struct hbuf fbuf = HBUF_NULL; // "frontend" section
|
||||
struct hbuf tbuf = HBUF_NULL; // "traces" section
|
||||
char *bits = NULL, *curves = NULL;
|
||||
char *quic_bind_opt __maybe_unused = NULL, *tcp_bind_opt = NULL;
|
||||
char *quic_bind_opt = NULL, *tcp_bind_opt = NULL;
|
||||
int sargc; /* saved argc */
|
||||
char **sargv; /* saved argv */
|
||||
|
||||
|
|
@ -206,7 +203,6 @@ void haproxy_init_args(int argc, char **argv)
|
|||
if (*opt == '-') {
|
||||
/* long options */
|
||||
opt++;
|
||||
#if defined(USE_QUIC)
|
||||
if (strcmp(opt, QUIC_BIND_LONG_OPT) == 0) {
|
||||
argv++; argc--;
|
||||
if (argc <= 0 || **argv == '-')
|
||||
|
|
@ -214,9 +210,7 @@ void haproxy_init_args(int argc, char **argv)
|
|||
|
||||
quic_bind_opt = *argv;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (strcmp(opt, TCP_BIND_LONG_OPT) == 0) {
|
||||
else if (strcmp(opt, TCP_BIND_LONG_OPT) == 0) {
|
||||
argv++; argc--;
|
||||
if (argc <= 0 || **argv == '-')
|
||||
haterm_usage(progname);
|
||||
|
|
@ -260,11 +254,6 @@ void haproxy_init_args(int argc, char **argv)
|
|||
else if (*opt == 'd' && *(opt+1) == 'S') {
|
||||
global.tune.options &= ~GTUNE_USE_SPLICE;
|
||||
}
|
||||
# if defined(HA_USE_KTLS)
|
||||
else if (*opt == 'd' && *(opt+1) == 'T') {
|
||||
global.tune.options |= GTUNE_NO_KTLS;
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
else if (*opt == 'd' && *(opt+1) == 'Z') {
|
||||
global.tune.no_zero_copy_fwd |= NO_ZERO_COPY_FWD;
|
||||
|
|
@ -405,30 +394,20 @@ void haproxy_init_args(int argc, char **argv)
|
|||
}
|
||||
|
||||
/* clear HTTP */
|
||||
hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread%s%s\n", ip, port1,
|
||||
tcp_bind_opt ? " " : "",
|
||||
tcp_bind_opt ? tcp_bind_opt : "");
|
||||
hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread\n", ip, port1);
|
||||
has_bind = 1;
|
||||
if (port2) {
|
||||
#if defined(USE_OPENSSL)
|
||||
has_ssl = 1;
|
||||
|
||||
/* SSL/TCP binding */
|
||||
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_ECDSA_CERT_NAME "%s%s%s\n",
|
||||
" crt " HATERM_ECDSA_CERT_NAME "%s%s\n",
|
||||
ip, port2,
|
||||
tcp_bind_opt ? " " : "",
|
||||
tcp_bind_opt ? tcp_bind_opt : "",
|
||||
# if defined(USE_LINUX_SPLICE) && defined(HA_USE_KTLS)
|
||||
" ktls on"
|
||||
# else
|
||||
"" /* no ktls */
|
||||
# endif
|
||||
);
|
||||
tcp_bind_opt ? tcp_bind_opt : "");
|
||||
|
||||
# if defined(USE_QUIC)
|
||||
/* QUIC binding */
|
||||
hbuf_appendf(&fbuf, "\tbind %s@%s:%s shards by-thread ssl"
|
||||
" crt " HATERM_RSA_CERT_NAME
|
||||
|
|
@ -436,11 +415,6 @@ void haproxy_init_args(int argc, char **argv)
|
|||
ipv6 ? "quic6" : "quic4", ip, port2,
|
||||
quic_bind_opt ? " " : "",
|
||||
quic_bind_opt ? quic_bind_opt : "");
|
||||
# endif /* USE_QUIC */
|
||||
#else /* !USE_OPENSSL */
|
||||
ha_alert("SSL support not compiled in. Rebuild with USE_OPENSSL=1.\n");
|
||||
goto leave;
|
||||
#endif /* USE_OPENSSL */
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -464,12 +438,6 @@ void haproxy_init_args(int argc, char **argv)
|
|||
}
|
||||
hbuf_appendf(&gbuf, "global\n");
|
||||
hbuf_appendf(&gbuf, "\ttune.memory.hot-size 3145728\n");
|
||||
if (has_ssl)
|
||||
hbuf_appendf(&gbuf, "\texpose-experimental-directives\n");
|
||||
#if defined(USE_LINUX_SPLICE) && defined(HA_USE_KTLS)
|
||||
if (has_ssl)
|
||||
hbuf_appendf(&gbuf, "\ttune.pipesize 262144\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* "global" section */
|
||||
|
|
|
|||
53
src/hlua.c
53
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. */
|
||||
if (nblk == 1) {
|
||||
if (len1 && blk1[len1-1] == '\n') {
|
||||
if (blk1[len1-1] == '\n') {
|
||||
len1--;
|
||||
skip_at_end++;
|
||||
if (len1 && blk1[len1-1] == '\r') {
|
||||
if (blk1[len1-1] == '\r') {
|
||||
len1--;
|
||||
skip_at_end++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (len2 && blk2[len2-1] == '\n') {
|
||||
if (blk2[len2-1] == '\n') {
|
||||
len2--;
|
||||
skip_at_end++;
|
||||
if (len2 && blk2[len2-1] == '\r') {
|
||||
if (blk2[len2-1] == '\r') {
|
||||
len2--;
|
||||
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;
|
||||
const char *value = MAY_LJMP(luaL_checklstring(L, 3, &value_len));
|
||||
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),
|
||||
ist2(value, value_len), 1));
|
||||
|
|
@ -13392,16 +13378,6 @@ static int hlua_cfg_parse_openlibs(char **args, int section_type, struct proxy *
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Reject a non-default restriction if the Lua VM is already initialised,
|
||||
* which happens when lua-load, lua-load-per-thread or lua-prepend-path
|
||||
* appeared before this directive.
|
||||
*/
|
||||
if (flags != HLUA_OPENLIBS_ALL && hlua_states[0]) {
|
||||
memprintf(err, "'%s' must appear before any 'lua-load', 'lua-load-per-thread' or 'lua-prepend-path' directive",
|
||||
args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hlua_openlibs_flags = flags;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -13502,8 +13478,6 @@ static int hlua_load(char **args, int section_type, struct proxy *curpx,
|
|||
return -1;
|
||||
}
|
||||
|
||||
hlua_init();
|
||||
|
||||
/* loading for global state */
|
||||
hlua_state_id = 0;
|
||||
ha_set_thread(NULL);
|
||||
|
|
@ -13522,8 +13496,6 @@ static int hlua_load_per_thread(char **args, int section_type, struct proxy *cur
|
|||
return -1;
|
||||
}
|
||||
|
||||
hlua_init();
|
||||
|
||||
if (per_thread_load == NULL) {
|
||||
/* allocate the first entry large enough to store the final NULL */
|
||||
per_thread_load = calloc(1, sizeof(*per_thread_load));
|
||||
|
|
@ -13612,8 +13584,6 @@ static int hlua_config_prepend_path(char **args, int section_type, struct proxy
|
|||
struct prepend_path *p = NULL;
|
||||
size_t i;
|
||||
|
||||
hlua_init();
|
||||
|
||||
if (too_many_args(2, args, err, NULL)) {
|
||||
goto err;
|
||||
}
|
||||
|
|
@ -14022,7 +13992,6 @@ int hlua_post_init()
|
|||
|
||||
hlua_body = 0;
|
||||
|
||||
|
||||
#if defined(USE_OPENSSL)
|
||||
/* Initialize SSL server. */
|
||||
if (socket_ssl->xprt->prepare_srv) {
|
||||
|
|
@ -14850,9 +14819,6 @@ void hlua_init(void) {
|
|||
};
|
||||
#endif
|
||||
|
||||
if (hlua_states[0])
|
||||
return; /* already initialised */
|
||||
|
||||
/* Init post init function list head */
|
||||
for (i = 0; i < MAX_THREADS + 1; i++)
|
||||
LIST_INIT(&hlua_init_functions[i]);
|
||||
|
|
@ -14941,14 +14907,3 @@ static void hlua_register_build_options(void)
|
|||
}
|
||||
|
||||
INITCALL0(STG_REGISTER, hlua_register_build_options);
|
||||
|
||||
/* Ensure the Lua VM is initialised even if no Lua directive appeared
|
||||
* in the configuration (e.g. no global section at all).
|
||||
*/
|
||||
static int hlua_pre_check(void)
|
||||
{
|
||||
hlua_init();
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
REGISTER_PRE_CHECK(hlua_pre_check);
|
||||
|
|
|
|||
|
|
@ -2082,9 +2082,6 @@ static enum act_return http_action_pause(struct act_rule *rule, struct proxy *px
|
|||
struct channel *chn = ((rule->from == ACT_F_HTTP_REQ) ? &s->req : &s->res);
|
||||
struct sample *key;
|
||||
|
||||
if (flags & ACT_OPT_FINAL)
|
||||
goto end;
|
||||
|
||||
if (!tick_isset(chn->analyse_exp)) {
|
||||
int time;
|
||||
|
||||
|
|
@ -2102,7 +2099,6 @@ static enum act_return http_action_pause(struct act_rule *rule, struct proxy *px
|
|||
if (tick_isset(chn->analyse_exp) && !tick_is_expired(chn->analyse_exp, now_ms))
|
||||
return ACT_RET_YIELD;
|
||||
|
||||
end:
|
||||
chn->analyse_exp = TICK_ETERNITY;
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue