mirror of
https://github.com/haproxy/haproxy.git
synced 2026-06-16 13:00:38 -04:00
Compare commits
No commits in common. "master" and "v3.4-dev13" have entirely different histories.
master
...
v3.4-dev13
153 changed files with 1603 additions and 4210 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:
|
||||
|
|
|
|||
180
CHANGELOG
180
CHANGELOG
|
|
@ -1,186 +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
|
||||
|
|
|
|||
8
INSTALL
8
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
|
||||
|
|
@ -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 :
|
||||
|
|
|
|||
75
Makefile
75
Makefile
|
|
@ -61,6 +61,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 +95,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 +124,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
|
||||
|
|
@ -684,15 +684,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 +722,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 +1058,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/20
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
3.5-dev0
|
||||
3.4-dev13
|
||||
|
|
|
|||
|
|
@ -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/20
|
||||
|
||||
|
||||
This document covers the configuration language as implemented in the version
|
||||
|
|
@ -3329,7 +3329,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 +3345,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 +4940,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
|
||||
|
|
@ -9105,9 +9105,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 +9179,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 +9962,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 +10027,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;
|
||||
|
||||
|
|
@ -19921,13 +19896,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
|
||||
|
|
@ -32637,9 +32610,9 @@ https://github.com/haproxy/wiki/wiki/ACME:--native-haproxy
|
|||
Current limitations:
|
||||
- The feature is limited to the http-01, dns-01 or dns-persist-01 challenges
|
||||
for now. http-01 is completely handled by HAProxy, but dns-01 and
|
||||
dns-persist-01 needs either the dataplaneAPI, a lua script using event_hdl or
|
||||
another 3rd party tool to talk to a DNS provider API. dns-persist-01 only
|
||||
needs the TXT entry to be set once, so it could be set manually without a tool.
|
||||
dns-persist-01 needs either the dataplaneAPI or another 3rd party
|
||||
tool to talk to a DNS provider API. dns-persist-01 only needs the TXT entry
|
||||
to be set once, so it could be set manually without a tool.
|
||||
- It is possible to start without an existing certificate on the disk. To do
|
||||
so, the certificate must configured in a crt-store.
|
||||
When using the "acme" keyword in a crt-store, a temporary key pair will be
|
||||
|
|
@ -32710,8 +32683,6 @@ challenge-ready <value>[,<value>]*
|
|||
"acme challenge_ready <crt> domain <domain>" on the master CLI or
|
||||
the stats socket. This allows an external DNS provisioning tool to
|
||||
confirm that the TXT record has been set before HAProxy proceeds.
|
||||
It is also possible to signal the "cli" readiness using the
|
||||
ACME.challenge_ready() lua function.
|
||||
|
||||
dns - perform a DNS pre-check by resolving the TXT record for
|
||||
"_acme-challenge.<domain>" using the configured "default" resolvers
|
||||
|
|
|
|||
|
|
@ -526,6 +526,12 @@ In addition, some variables are related to the global runqueue:
|
|||
unsigned int grq_total; /* total number of entries in the global run queue, atomic */
|
||||
static unsigned int global_rqueue_ticks; /* insertion count in the grq, use rq_lock */
|
||||
|
||||
And others to the global wait queue:
|
||||
struct eb_root timers; /* sorted timers tree, global, accessed under wq_lock */
|
||||
__decl_aligned_rwlock(wq_lock); /* RW lock related to the wait queue */
|
||||
struct eb_root timers; /* sorted timers tree, global, accessed under wq_lock */
|
||||
|
||||
|
||||
2022-06-14 - progress on task affinity
|
||||
==========
|
||||
|
||||
|
|
|
|||
|
|
@ -52,16 +52,6 @@ make sure to respect this ordering when adding new ones.
|
|||
cast and freed. The const char* is here to leave more freedom to use consts
|
||||
when making such options lists.
|
||||
|
||||
- void hap_register_hlua_state_init(int (*fct)())
|
||||
|
||||
This adds a call to function <fct> to the list of functions to be called each
|
||||
time a new Lua state is created by hlua_init_state(). This allows source
|
||||
files other than hlua.c to register objects and functions into the Lua API
|
||||
without modifying hlua.c directly. The function <fct> must return an ERR_*
|
||||
code. If the errmsg pointer is set on return, it is printed as an alert,
|
||||
warning, or notice depending on the error code. If ERR_ABORT or ERR_FATAL is
|
||||
returned, haproxy will exit with status 1.
|
||||
|
||||
- void hap_register_per_thread_alloc(int (*fct)())
|
||||
|
||||
This adds a call to function <fct> to the list of functions to be called when
|
||||
|
|
@ -334,18 +324,6 @@ alphanumerically ordered:
|
|||
is that it allows to register multiple <post> callbacks and to register them
|
||||
elsewhere in the code.
|
||||
|
||||
- REGISTER_HLUA_STATE_INIT(fct)
|
||||
|
||||
Registers function <fct> to be called for each new Lua state created by
|
||||
hlua_init_state(). This allows source files other than hlua.c to register
|
||||
objects and functions into the Lua API without modifying hlua.c directly.
|
||||
This is done by registering a call to hap_register_hlua_state_init(fct) at
|
||||
stage STG_REGISTER. The function <fct> must be of type
|
||||
(int (*fct)(lua_State *L, char **errmsg)) and must return an ERR_* code.
|
||||
If the errmsg pointer is set on return, it is printed as an alert, warning,
|
||||
or notice depending on the error code. If ERR_ABORT or ERR_FATAL is returned,
|
||||
haproxy will exit with status 1.
|
||||
|
||||
- REGISTER_PER_THREAD_ALLOC(fct)
|
||||
|
||||
Registers a call to register_per_thread_alloc(fct) at stage STG_REGISTER.
|
||||
|
|
|
|||
|
|
@ -21,11 +21,8 @@ HAPROXY CORE PRINCIPLES
|
|||
- 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").
|
||||
- size_t always same size as long but often declared as uint on 32-bit and
|
||||
ulong on 64-bit. Do not use in printf() without a cast (ulong with "%lu").
|
||||
- Main platforms are x86_64 and aarch64 with high thread counts (>=64).
|
||||
- Unaligned accesses are permitted for archs that support them; portable
|
||||
wrappers in net_helper.h (read_u32(), write_u32() etc).
|
||||
|
|
@ -39,8 +36,7 @@ HAPROXY CORE PRINCIPLES
|
|||
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_alloc() and malloc() are not interchangeable / compatible.
|
||||
- pool_free() semantics match free(); it is a no-op on NULL.
|
||||
- pool_free() makes the pointer invalid immediately; it must not be touched
|
||||
or passed to pool_free() again.
|
||||
|
|
@ -61,8 +57,8 @@ HAPROXY CORE PRINCIPLES
|
|||
- 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.
|
||||
in data. The former mandates holes of at least 8 bytes. The second relies
|
||||
on a bitmap of populated places.
|
||||
- another string API exists, "ist", representing a pointer and a length in a
|
||||
struct that is returned by inline functions and macros. It is described in
|
||||
doc/internals/api/ist.txt
|
||||
|
|
@ -77,11 +73,8 @@ HAPROXY CORE PRINCIPLES
|
|||
- 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.
|
||||
- get_trash_chunk() provides up to 3 rotating thread-local trash chunks (with
|
||||
a scope spanning from the call to the next function call).
|
||||
- For longer lived trash chunks, alloc_trash_chunk() is available but must be
|
||||
released using free_trash_chunk() on leaving.
|
||||
- standard doubly-linked lists (struct list) are provided via macros LIST_*.
|
||||
|
|
@ -110,9 +103,8 @@ HAPROXY CORE PRINCIPLES
|
|||
"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).
|
||||
and store). DWCAS available on some platforms but requires an equivalent
|
||||
for other ones.
|
||||
- The _HA_ATOMIC_* version (leading underscore) do not use barriers so these
|
||||
must be explicit (__ha_barrier_*).
|
||||
- Atomic loops must use CPU relaxation or exponential back-off.
|
||||
|
|
@ -186,9 +178,8 @@ HAPROXY CORE PRINCIPLES
|
|||
- 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).
|
||||
- Explicitly compare functions returning non-zero with 0 (e.g. strcmp) unless
|
||||
they explicitly return a boolean (e.g. isalnum) or a pointer (e.g. strchr).
|
||||
- Unsigned int comparisons to zero never use >0 but !=0 to avoid signedness
|
||||
mistakes.
|
||||
- turn non-zero integer to boolean using "!" or "!!".
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -893,7 +893,9 @@ Core class
|
|||
|
||||
**context**: init, task, action
|
||||
|
||||
This function returns a new object of a *httpclient* class.
|
||||
This function returns a new object of a *httpclient* class. An *httpclient*
|
||||
object must be used to process one and only one request. It must never be
|
||||
reused to process several requests.
|
||||
|
||||
:returns: A :ref:`httpclient_class` object.
|
||||
|
||||
|
|
@ -1029,12 +1031,6 @@ Core class
|
|||
Be careful when subscribing to this type since many events might be
|
||||
generated.
|
||||
|
||||
**ACME** Family:
|
||||
|
||||
* **ACME_DEPLOY**: when a dns-01 challenge TXT record must be deployed
|
||||
externally before HAProxy can proceed with the ACME challenge
|
||||
* **ACME_NEWCERT**: when a new certificate is successfully installed
|
||||
|
||||
.. Note::
|
||||
Use **SERVER** in **event_types** to subscribe to all server events types
|
||||
at once. Note that this should only be used for testing purposes since a
|
||||
|
|
@ -1052,8 +1048,7 @@ Core class
|
|||
* **event** (*string*): the event type (one of the **event_types** specified
|
||||
when subscribing)
|
||||
* **event_data**: specific to each event family (For **SERVER** family,
|
||||
a :ref:`server_event_class` object; for **ACME** family,
|
||||
a :ref:`acme_event_class` object)
|
||||
a :ref:`server_event_class` object)
|
||||
* **sub**: class to manage the subscription from within the event
|
||||
(a :ref:`event_sub_class` object)
|
||||
* **when**: timestamp corresponding to the date when the event was generated.
|
||||
|
|
@ -2585,7 +2580,10 @@ HTTPClient class
|
|||
.. js:class:: HTTPClient
|
||||
|
||||
The httpclient class allows issue of outbound HTTP requests through a simple
|
||||
API without the knowledge of HAProxy internals.
|
||||
API without the knowledge of HAProxy internals. Any instance must be used to
|
||||
process one and only one request. It must never be reused to process several
|
||||
requests.
|
||||
|
||||
.. js:function:: HTTPClient.get(httpclient, request)
|
||||
.. js:function:: HTTPClient.head(httpclient, request)
|
||||
.. js:function:: HTTPClient.put(httpclient, request)
|
||||
|
|
@ -2614,8 +2612,7 @@ HTTPClient class
|
|||
haproxy address format.
|
||||
:param integer request.timeout: Optional timeout parameter, set a
|
||||
"timeout server" on the connections.
|
||||
:returns: Lua table containing the response. If an internal error occurs (e.g.
|
||||
connection failure, timeout, etc.), the ``status`` field will be set to 0.
|
||||
:returns: Lua table containing the response
|
||||
|
||||
|
||||
.. code-block:: lua
|
||||
|
|
@ -4742,75 +4739,6 @@ CertCache class
|
|||
CertCache.set{filename="certs/localhost9994.pem.rsa", crt=crt}
|
||||
|
||||
|
||||
ACME class
|
||||
==========
|
||||
|
||||
.. js:class:: ACME
|
||||
|
||||
This class provides access to the ACME (Automatic Certificate Management
|
||||
Environment) subsystem. It allows Lua scripts to interact with ongoing ACME
|
||||
certificate challenges.
|
||||
|
||||
.. js:function:: ACME.challenge_ready(crt, dns)
|
||||
|
||||
Marks the ACME challenge for domain <dns> in certificate <crt> as ready.
|
||||
Returns the number of remaining challenges, or 0 if all challenges are ready
|
||||
and validation has been triggered. Raises a Lua error if the certificate or
|
||||
domain is not found.
|
||||
|
||||
:param string crt: The filename of the certificate.
|
||||
:param string dns: The domain name for which the challenge is ready.
|
||||
:returns: The number of remaining challenges (integer), or 0 when all
|
||||
challenges are done and validation has been triggered.
|
||||
|
||||
.. _acme_event_class:
|
||||
|
||||
AcmeEvent class
|
||||
===============
|
||||
|
||||
.. js:class:: AcmeEvent
|
||||
|
||||
This class is provided with every **ACME** event.
|
||||
|
||||
See :js:func:`core.event_sub()` for more info.
|
||||
|
||||
.. js:attribute:: AcmeEvent.crtname
|
||||
|
||||
Contains the certificate store name.
|
||||
|
||||
.. js:attribute:: AcmeEvent.domain
|
||||
|
||||
Contains the domain being challenged.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.thumbprint
|
||||
|
||||
Contains the account key JWK thumbprint.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.dns_record
|
||||
|
||||
Contains the DNS TXT record value that must be set at
|
||||
``_acme-challenge.<domain>``.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.provider
|
||||
|
||||
Contains the DNS provider name configured in the ACME section.
|
||||
Only set if a provider was configured.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.vars
|
||||
|
||||
Contains the ACME vars string configured in the ACME section.
|
||||
Only set if vars were configured.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
External Lua libraries
|
||||
======================
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -2469,8 +2457,7 @@ httpclient [--htx] <method> <URI>
|
|||
name in the URL using the "default" resolvers section, which is populated
|
||||
with the DNS servers of your /etc/resolv.conf by default. However it won't be
|
||||
able to resolve an host from /etc/hosts if you don't use a local dns daemon
|
||||
which can resolve those. If an internal error occurs (e.g. connection failure,
|
||||
timeout, etc.), the status code will be set to 0.
|
||||
which can resolve those.
|
||||
|
||||
The --htx option allow to use the haproxy internal htx representation using
|
||||
the htx_dump() function, mainly used for debugging.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
-- ACME dns-01 automation via event_hdl callbacks using the Gandi LiveDNS API v5
|
||||
--
|
||||
-- HAProxy Configuration:
|
||||
--
|
||||
-- global
|
||||
-- expose-experimental-directives
|
||||
-- tune.lua.bool-sample-conversion normal
|
||||
-- lua-load examples/lua/acme-gandi-livedns.lua
|
||||
-- log stderr local0
|
||||
--
|
||||
-- acme LE
|
||||
-- directory https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
-- contact foobar@example.com
|
||||
-- challenge dns-01
|
||||
-- challenge-ready cli,dns
|
||||
--
|
||||
-- crt-store
|
||||
-- load crt foobar.pem acme LE domains *.foobar.example.com
|
||||
--
|
||||
-- Start HAProxy with the GANDI_API_KEY variable:
|
||||
--
|
||||
-- GANDI_API_KEY=fer89wf498w4f98we74f98wwiw787f8we4f8 ./haproxy -W -f haproxy.cfg
|
||||
--
|
||||
-- Gandi Personal Access Token (https://account.gandi.net -> Security -> Personal Access Tokens).
|
||||
-- Set the GANDI_API_KEY environment variable before starting HAProxy.
|
||||
local GANDI_API_KEY = os.getenv("GANDI_API_KEY") or error("GANDI_API_KEY environment variable is not set")
|
||||
|
||||
-- Gandi LiveDNS API base URL.
|
||||
local GANDI_API_URL = "https://api.gandi.net/v5/livedns"
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Gandi LiveDNS helpers
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- Try to set the _acme-challenge TXT record for <domain> to <txt_value>.
|
||||
-- Probes each possible parent zone (longest first) until Gandi accepts one.
|
||||
-- Returns the zone and record name on success, or nil on failure.
|
||||
local function dns_set_txt(domain, txt_value)
|
||||
local labels = {}
|
||||
for label in domain:gmatch("[^.]+") do
|
||||
labels[#labels + 1] = label
|
||||
end
|
||||
|
||||
for i = 1, #labels - 1 do
|
||||
local zone = table.concat(labels, ".", i + 1)
|
||||
local name = "_acme-challenge." .. table.concat(labels, ".", 1, i)
|
||||
local url = string.format("%s/domains/%s/records/%s/TXT", GANDI_API_URL, zone, name)
|
||||
local body = string.format('{"rrset_values":["%s"],"rrset_ttl":300}', txt_value)
|
||||
|
||||
core.log(core.debug, string.format("acme: trying PUT %s", url))
|
||||
|
||||
-- Remove any stale TXT record first so the new value propagates cleanly.
|
||||
local hc_del = core.httpclient()
|
||||
hc_del:delete({
|
||||
url = url,
|
||||
headers = { ["Authorization"] = { "Bearer " .. GANDI_API_KEY } },
|
||||
})
|
||||
|
||||
local hc = core.httpclient()
|
||||
local res = hc:put({
|
||||
url = url,
|
||||
headers = {
|
||||
["Authorization"] = { "Bearer " .. GANDI_API_KEY },
|
||||
["Content-Type"] = { "application/json" },
|
||||
},
|
||||
body = body,
|
||||
})
|
||||
|
||||
if res and (res.status == 200 or res.status == 201) then
|
||||
core.log(core.notice, string.format(
|
||||
"acme: TXT record set: %s in zone %s", name, zone))
|
||||
return zone, name
|
||||
end
|
||||
end
|
||||
|
||||
core.log(core.alert, string.format(
|
||||
"acme: failed to set TXT record for _acme-challenge.%s: no valid zone found", domain))
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
-- Deletes the TXT record identified by <zone> and <name>.
|
||||
local function dns_del_txt(zone, name)
|
||||
local url = string.format("%s/domains/%s/records/%s/TXT", GANDI_API_URL, zone, name)
|
||||
|
||||
core.log(core.notice, string.format("acme: DELETE %s", url))
|
||||
|
||||
local hc = core.httpclient()
|
||||
local res = hc:delete({
|
||||
url = url,
|
||||
headers = {
|
||||
["Authorization"] = { "Bearer " .. GANDI_API_KEY },
|
||||
},
|
||||
})
|
||||
|
||||
if not res or res.status ~= 204 then
|
||||
local status = res and res.status or "nil"
|
||||
core.log(core.alert, string.format(
|
||||
"acme: Gandi DELETE failed for %s/%s (status=%s)", zone, name, status))
|
||||
return false
|
||||
end
|
||||
|
||||
core.log(core.notice, string.format(
|
||||
"acme: TXT record deleted: %s in zone %s", name, zone))
|
||||
return true
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Tasks
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- Track deployed TXT records per cert path so they can be cleaned up.
|
||||
-- deployed[crt][domain] = { zone = ..., name = ... }
|
||||
local deployed = {}
|
||||
|
||||
-- Spawn a background task per ACME_DEPLOY event to set the TXT record and
|
||||
-- signal challenge readiness. Using register_task keeps HTTP calls in a
|
||||
-- plain task context.
|
||||
core.event_sub({"ACME_DEPLOY"}, function(event, data, sub, when)
|
||||
local crt = data.crtname
|
||||
local domain = data.domain
|
||||
local record = data.dns_record
|
||||
|
||||
core.register_task(function()
|
||||
local zone, name = dns_set_txt(domain, record)
|
||||
if not zone then
|
||||
core.log(core.alert, string.format(
|
||||
"acme: aborting challenge for crt=%s domain=%s", crt, domain))
|
||||
return
|
||||
end
|
||||
|
||||
-- Remember this record for cleanup on ACME_NEWCERT.
|
||||
if not deployed[crt] then deployed[crt] = {} end
|
||||
deployed[crt][domain] = { zone = zone, name = name }
|
||||
|
||||
-- Signal HAProxy that the dns-01 challenge for this domain is ready.
|
||||
local ok, ret = pcall(ACME.challenge_ready, crt, domain)
|
||||
if not ok then
|
||||
core.log(core.alert, string.format(
|
||||
"acme: challenge_ready error for crt=%s domain=%s: %s", crt, domain, ret))
|
||||
elseif ret == 0 then
|
||||
core.log(core.notice, string.format(
|
||||
"acme: all challenges ready for crt=%s, validation starting", crt))
|
||||
else
|
||||
core.log(core.info, string.format(
|
||||
"acme: crt=%s domain=%s ready, %d challenge(s) still pending",
|
||||
crt, domain, ret))
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
-- ACME_NEWCERT: remove the TXT records that were set for this certificate.
|
||||
core.event_sub({"ACME_NEWCERT"}, function(event, data, sub, when)
|
||||
local crt = data.crtname
|
||||
if not deployed[crt] then return end
|
||||
|
||||
core.register_task(function()
|
||||
for _, rec in pairs(deployed[crt]) do
|
||||
dns_del_txt(rec.zone, rec.name)
|
||||
end
|
||||
deployed[crt] = nil
|
||||
end)
|
||||
end)
|
||||
|
|
@ -135,29 +135,6 @@ struct acme_ctx {
|
|||
#define ACME_VERB_ADVANCED 4
|
||||
#define ACME_VERB_COMPLETE 5
|
||||
|
||||
/* event data for EVENT_HDL_SUB_ACME_DEPLOY:
|
||||
* published when a dns-01 challenge TXT record must be deployed externally.
|
||||
*/
|
||||
struct event_hdl_cb_data_acme_deploy {
|
||||
struct {
|
||||
char *crtname; /* certificate store name (heap-allocated) */
|
||||
char *domain; /* domain being challenged (heap-allocated) */
|
||||
char *thumbprint; /* account key JWK thumbprint (heap-allocated) */
|
||||
char *dns_record; /* DNS TXT record value to set (heap-allocated) */
|
||||
char *provider; /* DNS provider name (heap-allocated, may be NULL) */
|
||||
char *vars; /* ACME vars string (heap-allocated, may be NULL) */
|
||||
} safe;
|
||||
};
|
||||
|
||||
/* event data for EVENT_HDL_SUB_ACME_NEWCERT:
|
||||
* published when a new certificate was successfully installed.
|
||||
*/
|
||||
struct event_hdl_cb_data_acme_newcert {
|
||||
struct {
|
||||
char *crtname; /* certificate store name (heap-allocated) */
|
||||
} safe;
|
||||
};
|
||||
|
||||
#endif /* ! HAVE_ACME */
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,14 +5,8 @@
|
|||
#include <haproxy/ssl_ckch-t.h>
|
||||
|
||||
int ckch_conf_acme_init(void *value, char *buf, struct ckch_store *s, int cli, const char *filename, int linenum, char **err);
|
||||
int acme_challenge_ready(const char *crt, const char *dns);
|
||||
EVP_PKEY *acme_gen_tmp_pkey();
|
||||
X509 *acme_gen_tmp_x509();
|
||||
|
||||
#if defined(USE_LUA)
|
||||
#include <haproxy/hlua-t.h>
|
||||
#include <haproxy/event_hdl-t.h>
|
||||
void acme_hlua_event_push_args(struct hlua *hlua, struct event_hdl_sub_type event, void *data);
|
||||
#endif /* USE_LUA */
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,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);
|
||||
|
|
|
|||
|
|
@ -101,8 +101,6 @@ void ha_warning(const char *fmt, ...)
|
|||
* These functions are reserved to output diagnostics on MODE_DIAG.
|
||||
* Use the underscore variants only if MODE_DIAG has already been checked.
|
||||
*/
|
||||
void ha_diag_notice(const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 1 ,2)));
|
||||
void _ha_vdiag_warning(const char *fmt, va_list argp);
|
||||
void _ha_diag_warning(const char *fmt, ...);
|
||||
void ha_diag_warning(const char *fmt, ...)
|
||||
|
|
|
|||
|
|
@ -290,16 +290,6 @@ struct event_hdl_sub {
|
|||
#define EVENT_HDL_SUB_PAT_REF_COMMIT EVENT_HDL_SUB_TYPE(2,4)
|
||||
#define EVENT_HDL_SUB_PAT_REF_CLEAR EVENT_HDL_SUB_TYPE(2,5)
|
||||
|
||||
/* ACME family, published in global subscription list.
|
||||
* Provides event_hdl_cb_data_acme_deploy and event_hdl_cb_data_acme_newcert
|
||||
* structs (defined in haproxy/acme-t.h).
|
||||
*/
|
||||
#define EVENT_HDL_SUB_ACME EVENT_HDL_SUB_FAMILY(3)
|
||||
/* a new certificate was successfully installed */
|
||||
#define EVENT_HDL_SUB_ACME_NEWCERT EVENT_HDL_SUB_TYPE(3,1)
|
||||
/* dns-01 challenge must be deployed externally */
|
||||
#define EVENT_HDL_SUB_ACME_DEPLOY EVENT_HDL_SUB_TYPE(3,2)
|
||||
|
||||
/* --------------------------------------- */
|
||||
|
||||
/* Please reflect changes above in event_hdl_sub_type_map defined
|
||||
|
|
|
|||
|
|
@ -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
|
||||
/* unused: 0x00040000 */
|
||||
#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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@
|
|||
#define CLASS_HTTP "HTTP"
|
||||
#define CLASS_HTTP_MSG "HTTPMessage"
|
||||
#define CLASS_HTTPCLIENT "HTTPClient"
|
||||
#define CLASS_HTTPCLIENT_REQ "HTTPClientRequest"
|
||||
#define CLASS_MAP "Map"
|
||||
#define CLASS_APPLET_TCP "AppletTCP"
|
||||
#define CLASS_APPLET_HTTP "AppletHTTP"
|
||||
|
|
@ -260,11 +259,6 @@ struct hlua_patref_iterator_context {
|
|||
struct pat_ref_gen *gen; /* the generation we are iterating over */
|
||||
};
|
||||
|
||||
struct hlua_state_init_fct {
|
||||
struct list list;
|
||||
int (*fct)(lua_State *L, char **errmsg);
|
||||
};
|
||||
|
||||
#else /* USE_LUA */
|
||||
/************************ For use when Lua is disabled ********************/
|
||||
|
||||
|
|
|
|||
|
|
@ -26,17 +26,6 @@
|
|||
|
||||
#ifdef USE_LUA
|
||||
|
||||
/* Lua uses longjmp to perform yield or throwing errors. This
|
||||
* macro is used only for identifying the function that can
|
||||
* not return because a longjmp is executed.
|
||||
* __LJMP marks a prototype of hlua file that can use longjmp.
|
||||
* WILL_LJMP() marks an lua function that will use longjmp.
|
||||
* MAY_LJMP() marks an lua function that may use longjmp.
|
||||
*/
|
||||
#define __LJMP
|
||||
#define WILL_LJMP(func) do { func; my_unreachable(); } while(0)
|
||||
#define MAY_LJMP(func) func
|
||||
|
||||
/* The following macros are used to set flags. */
|
||||
#define HLUA_SET_RUN(__hlua) do {(__hlua)->flags |= HLUA_RUN;} while(0)
|
||||
#define HLUA_CLR_RUN(__hlua) do {(__hlua)->flags &= ~HLUA_RUN;} while(0)
|
||||
|
|
@ -62,11 +51,6 @@
|
|||
|
||||
/* Lua HAProxy integration functions. */
|
||||
void hlua_yield_asap(lua_State *L);
|
||||
void hap_register_hlua_state_init(int (*fct)(lua_State *L, char **errmsg));
|
||||
|
||||
/* simplified way to register a lua_State init callback from any file */
|
||||
#define REGISTER_HLUA_STATE_INIT(fct) \
|
||||
INITCALL1(STG_REGISTER, hap_register_hlua_state_init, (fct))
|
||||
const char *hlua_traceback(lua_State *L, const char* sep);
|
||||
void hlua_ctx_destroy(struct hlua *lua);
|
||||
void hlua_init();
|
||||
|
|
@ -81,15 +65,6 @@ void hlua_pushref(lua_State *L, int ref);
|
|||
void hlua_unref(lua_State *L, int ref);
|
||||
struct hlua *hlua_gethlua(lua_State *L);
|
||||
void hlua_yieldk(lua_State *L, int nresults, lua_KContext ctx, lua_KFunction k, int timeout, unsigned int flags);
|
||||
int hlua_pusherror(lua_State *L, const char *fmt, ...);
|
||||
|
||||
__LJMP static inline void hlua_check_args(lua_State *L, int nb, char *fcn)
|
||||
{
|
||||
if (lua_gettop(L) == nb)
|
||||
return;
|
||||
WILL_LJMP(luaL_error(L, "'%s' needs %d arguments", fcn, nb));
|
||||
}
|
||||
#define check_args hlua_check_args
|
||||
|
||||
#else /* USE_LUA */
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -38,9 +38,4 @@ static inline int httpclient_started(struct httpclient *hc)
|
|||
return !!(hc->flags & HTTPCLIENT_FS_STARTED);
|
||||
}
|
||||
|
||||
#ifdef USE_LUA
|
||||
#include <haproxy/hlua-t.h>
|
||||
void hlua_httpclient_destroy_all(struct hlua *hlua);
|
||||
#endif
|
||||
|
||||
#endif /* !_HAPROXY_HTTPCLIENT_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;
|
||||
|
|
|
|||
|
|
@ -121,8 +121,8 @@ 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);
|
||||
if (mulsz_overflow(m, n, &size))
|
||||
return ~(size_t)0;
|
||||
return size;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -286,11 +286,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 +331,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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -279,8 +279,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
|
||||
|
|
@ -403,6 +401,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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) */
|
||||
|
|
|
|||
|
|
@ -255,7 +255,6 @@ struct ssl_keylog {
|
|||
#define SSL_SOCK_F_KTLS_RECV (1 << 3) /* kTLS receive is configure on that socket */
|
||||
#define SSL_SOCK_F_CTRL_SEND (1 << 4) /* We want to send a kTLS control message for that socket */
|
||||
#define SSL_SOCK_F_HAS_ALPN (1 << 5) /* An ALPN has been negotiated */
|
||||
#define SSL_SOCK_F_KTLS_ULP (1 << 6) /* TLS ULP is enabled on that socket */
|
||||
|
||||
struct ssl_sock_ctx {
|
||||
struct connection *conn;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
/* use this to check a task state or to clean it up before queueing */
|
||||
#define TASK_WOKEN_ANY (TASK_WOKEN_OTHER|TASK_WOKEN_INIT|TASK_WOKEN_TIMER| \
|
||||
TASK_WOKEN_IO|TASK_WOKEN_SIGNAL|TASK_WOKEN_MSG| \
|
||||
TASK_WOKEN_RES|TASK_WOKEN_WQ)
|
||||
TASK_WOKEN_RES)
|
||||
|
||||
#define TASK_F_TASKLET 0x00008000 /* nature of this task: 0=task 1=tasklet */
|
||||
#define TASK_F_USR1 0x00010000 /* preserved user flag 1, application-specific, def:0 */
|
||||
|
|
@ -61,8 +61,7 @@
|
|||
#define TASK_F_UEVT2 0x00040000 /* one-shot user event type 2, application specific, def:0 */
|
||||
#define TASK_F_WANTS_TIME 0x00080000 /* task/tasklet wants th_ctx->sched_call_date to be set */
|
||||
#define TASK_F_UEVT3 0x00100000 /* one-shot user event type 3, application specific, def:0 */
|
||||
#define TASK_WOKEN_WQ 0x00200000 /* The task has been waken up only to be put in the wait queue, because its expire changed */
|
||||
/* unused: 0x400000..0x80000000 */
|
||||
/* unused: 0x200000..0x80000000 */
|
||||
|
||||
/* These flags are persistent across scheduler calls */
|
||||
#define TASK_PERSISTENT (TASK_SELF_WAKING | TASK_KILLED | \
|
||||
|
|
@ -83,7 +82,7 @@ static forceinline char *task_show_state(char *buf, size_t len, const char *deli
|
|||
_(TASK_KILLED, _(TASK_HEAVY, _(TASK_WOKEN_INIT,
|
||||
_(TASK_WOKEN_TIMER, _(TASK_WOKEN_IO, _(TASK_WOKEN_SIGNAL,
|
||||
_(TASK_WOKEN_MSG, _(TASK_WOKEN_RES, _(TASK_WOKEN_OTHER,
|
||||
_(TASK_F_TASKLET, _(TASK_F_USR1, _(TASK_WOKEN_WQ)))))))))))))));
|
||||
_(TASK_F_TASKLET, _(TASK_F_USR1))))))))))))));
|
||||
/* epilogue */
|
||||
_(~0U);
|
||||
return buf;
|
||||
|
|
|
|||
|
|
@ -91,13 +91,14 @@ extern struct pool_head *pool_head_task;
|
|||
extern struct pool_head *pool_head_tasklet;
|
||||
extern struct pool_head *pool_head_notification;
|
||||
|
||||
__decl_thread(extern HA_RWLOCK_T wq_lock THREAD_ALIGNED());
|
||||
|
||||
void __tasklet_wakeup_on(struct tasklet *tl, int thr);
|
||||
struct list *__tasklet_wakeup_after(struct list *head, struct tasklet *tl);
|
||||
void task_kill(struct task *t);
|
||||
void tasklet_kill(struct tasklet *t);
|
||||
void __task_wakeup(struct task *t);
|
||||
void __task_queue(struct task *task);
|
||||
static inline void _task_queue(struct task *task, const struct ha_caller *caller);
|
||||
void __task_queue(struct task *task, struct eb_root *wq);
|
||||
|
||||
unsigned int run_tasks_from_lists(unsigned int budgets[]);
|
||||
|
||||
|
|
@ -117,7 +118,7 @@ void process_runnable_tasks(void);
|
|||
void wake_expired_tasks(void);
|
||||
|
||||
/* Checks the next timer for the current thread by looking into its own timer
|
||||
* list. It may return TICK_ETERNITY if no timer is present.
|
||||
* list and the global one. It may return TICK_ETERNITY if no timer is present.
|
||||
* Note that the next timer might very well be slightly in the past.
|
||||
*/
|
||||
int next_timer_expiry(void);
|
||||
|
|
@ -204,77 +205,6 @@ static inline uint64_t task_mono_time(void)
|
|||
return th_ctx->sched_call_date;
|
||||
}
|
||||
|
||||
#if !defined(HA_CAS_IS_8B) && !defined(HA_HAVE_CAS_DW)
|
||||
__decl_thread(extern HA_SPINLOCK_T task_state_tid);
|
||||
#endif
|
||||
|
||||
static inline int __task_set_state_and_tid(struct task *t, int expected_tid, int new_tid, unsigned int current, unsigned int wanted)
|
||||
{
|
||||
#if defined(HA_CAS_IS_8B) || defined(HA_HAVE_CAS_DW)
|
||||
uint64_t expected_value;
|
||||
uint64_t new_value;
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
expected_value = ((uint64_t)(current) << 32) | (uint32_t)expected_tid;
|
||||
#else
|
||||
expected_value = current | ((uint64_t)expected_tid << 32);
|
||||
#endif
|
||||
do {
|
||||
int tid_seen;
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
tid_seen = (expected_value & 0xffffffff);
|
||||
if (tid_seen != expected_tid)
|
||||
return 0;
|
||||
if ((expected_value >> 32) != current)
|
||||
return 0;
|
||||
new_value = ((uint64_t)wanted << 32) | (uint32_t)new_tid;
|
||||
|
||||
#else
|
||||
tid_seen = (expected_value >> 32);
|
||||
if (tid_seen != expected_tid)
|
||||
return 0;
|
||||
if ((expected_value & 0xffffffff) != current)
|
||||
return 0;
|
||||
new_value = wanted | ((uint64_t)new_tid << 32);
|
||||
#endif
|
||||
#if defined(HA_CAS_IS_8B)
|
||||
} while (!HA_ATOMIC_CAS((uint64_t *)&t->state, &expected_value, new_value) && __ha_cpu_relax());
|
||||
#elif defined(HA_HAVE_CAS_DW)
|
||||
} while (!HA_ATOMIC_DWCAS((uint64_t *)&t->state, &expected_value, &new_value) && __ha_cpu_relax());
|
||||
#endif
|
||||
return 1;
|
||||
#else /* !HA_CAS_IS_8B && !HA_HAVE_CAS_DW */
|
||||
int old_state;
|
||||
int ret = 0;
|
||||
|
||||
HA_SPIN_LOCK(OTHER_LOCK, &task_state_tid);
|
||||
if (_HA_ATOMIC_LOAD(&t->tid) == expected_tid) {
|
||||
old_state = _HA_ATOMIC_LOAD(&t->state);
|
||||
if (old_state == current && HA_ATOMIC_CAS(&t->state, &old_state, wanted)) {
|
||||
_HA_ATOMIC_STORE(&t->tid, new_tid);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
HA_SPIN_UNLOCK(OTHER_LOCK, &task_state_tid);
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int __task_get_new_tid_field(int curtid)
|
||||
{
|
||||
if (curtid >= 0 || curtid < -1)
|
||||
return curtid;
|
||||
return -2 - tid;
|
||||
}
|
||||
|
||||
static inline int __task_get_current_owner(int curtid)
|
||||
{
|
||||
if (curtid >= 0 || curtid == -1)
|
||||
return curtid;
|
||||
return ~(curtid + 1);
|
||||
}
|
||||
|
||||
/* puts the task <t> in run queue with reason flags <f>, and returns <t> */
|
||||
/* This will put the task in the local runqueue if the task is only runnable
|
||||
* by the current thread, in the global runqueue otherwies. With DEBUG_TASK,
|
||||
|
|
@ -290,9 +220,7 @@ static inline void _task_wakeup(struct task *t, unsigned int f, const struct ha_
|
|||
|
||||
state = _HA_ATOMIC_OR_FETCH(&t->state, f);
|
||||
while (!(state & (TASK_RUNNING | TASK_QUEUED))) {
|
||||
int expected_tid = _HA_ATOMIC_LOAD(&t->tid);
|
||||
|
||||
if (__task_set_state_and_tid(t, expected_tid, __task_get_new_tid_field(expected_tid), state, state | TASK_QUEUED)) {
|
||||
if (_HA_ATOMIC_CAS(&t->state, &state, state | TASK_QUEUED)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&t->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
|
|
@ -303,7 +231,6 @@ static inline void _task_wakeup(struct task *t, unsigned int f, const struct ha_
|
|||
__task_wakeup(t);
|
||||
break;
|
||||
}
|
||||
state = _HA_ATOMIC_LOAD(&t->state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -318,28 +245,14 @@ static inline void task_drop_running(struct task *t, unsigned int f)
|
|||
{
|
||||
unsigned int state, new_state;
|
||||
|
||||
state = _HA_ATOMIC_LOAD(&t->state);
|
||||
|
||||
while (1) {
|
||||
int cur_tid, new_tid;
|
||||
|
||||
state = _HA_ATOMIC_LOAD(&t->state);
|
||||
new_state = (state | f) &~ TASK_RUNNING;
|
||||
cur_tid = t->tid;
|
||||
if ((new_state & TASK_WOKEN_WQ) && __task_get_current_owner(cur_tid) == tid) {
|
||||
_task_queue(t, NULL);
|
||||
new_state &= ~TASK_WOKEN_WQ;
|
||||
}
|
||||
new_state = state | f;
|
||||
if (new_state & TASK_WOKEN_ANY)
|
||||
new_state |= TASK_QUEUED;
|
||||
|
||||
|
||||
if ((new_state & TASK_QUEUED) || cur_tid >= 0 || task_in_wq(t) ||
|
||||
__task_get_current_owner(cur_tid) != tid)
|
||||
new_tid = cur_tid;
|
||||
else
|
||||
new_tid = -1;
|
||||
|
||||
if (__task_set_state_and_tid(t, cur_tid, new_tid, state, new_state))
|
||||
if (_HA_ATOMIC_CAS(&t->state, &state, new_state & ~TASK_RUNNING))
|
||||
break;
|
||||
__ha_cpu_relax();
|
||||
}
|
||||
|
|
@ -360,21 +273,31 @@ static inline struct task *__task_unlink_wq(struct task *t)
|
|||
return t;
|
||||
}
|
||||
|
||||
/* remove a task from its wait queue, which during normal operations will be
|
||||
* the current thread's wait queue.
|
||||
/* remove a task from its wait queue. It may either be the local wait queue if
|
||||
* the task is bound to a single thread or the global queue. If the task uses a
|
||||
* shared wait queue, the global wait queue lock is used.
|
||||
*/
|
||||
static inline struct task *task_unlink_wq(struct task *t)
|
||||
{
|
||||
unsigned long locked;
|
||||
|
||||
if (likely(task_in_wq(t))) {
|
||||
BUG_ON(__task_get_current_owner(t->tid) != tid && !(global.mode & MODE_STOPPING));
|
||||
locked = t->tid < 0;
|
||||
BUG_ON(t->tid >= 0 && t->tid != tid && !(global.mode & MODE_STOPPING));
|
||||
if (locked)
|
||||
HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
__task_unlink_wq(t);
|
||||
if (locked)
|
||||
HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Place <task> into the wait queue, where it may already be. If the expiration
|
||||
* timer is infinite, do nothing and rely on wake_expired_task to clean up.
|
||||
* If the task uses a shared wait queue, it's queued into the global wait queue,
|
||||
* protected by the global wq_lock, otherwise by it necessarily belongs to the
|
||||
* current thread'sand is queued without locking.
|
||||
*/
|
||||
#define task_queue(t) \
|
||||
_task_queue(t, MK_CALLER(WAKEUP_TYPE_TASK_QUEUE, 0, 0))
|
||||
|
|
@ -393,17 +316,34 @@ static inline void _task_queue(struct task *task, const struct ha_caller *caller
|
|||
if (!tick_isset(task->expire))
|
||||
return;
|
||||
|
||||
BUG_ON(task->tid >= 0 && task->tid != tid);
|
||||
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
#ifdef USE_THREAD
|
||||
if (task->tid < 0) {
|
||||
HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
#ifdef DEBUG_TASK
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
#endif
|
||||
}
|
||||
__task_queue(task, &tg_ctx->timers);
|
||||
}
|
||||
HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
BUG_ON(task->tid != tid);
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
#ifdef DEBUG_TASK
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
#endif
|
||||
}
|
||||
__task_queue(task, &th_ctx->timers);
|
||||
}
|
||||
__task_queue(task);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -423,11 +363,6 @@ static inline void task_set_thread(struct task *t, int thr)
|
|||
/* no shared queue without threads */
|
||||
thr = 0;
|
||||
#endif
|
||||
/*
|
||||
* Nothing to do, the task is only temporarily owned
|
||||
*/
|
||||
if (thr == -1 && t->tid == -2 - tid)
|
||||
return;
|
||||
if (unlikely(task_in_wq(t))) {
|
||||
task_unlink_wq(t);
|
||||
t->tid = thr;
|
||||
|
|
@ -505,30 +440,24 @@ static inline void _tasklet_wakeup_on(struct tasklet *tl, int thr, uint f, const
|
|||
static inline void _task_instant_wakeup(struct task *t, unsigned int f, const struct ha_caller *caller)
|
||||
{
|
||||
int thr = t->tid;
|
||||
int newtid;
|
||||
unsigned int state;
|
||||
|
||||
if (thr < 0)
|
||||
thr = tid;
|
||||
|
||||
/* first, let's update the task's state with the wakeup condition */
|
||||
state = _HA_ATOMIC_OR_FETCH(&t->state, f);
|
||||
|
||||
/* next we need to make sure the task was not/will not be added to the
|
||||
* run queue because the tasklet list's mt_list uses the same storage
|
||||
* as the task's run_queue.
|
||||
*/
|
||||
do {
|
||||
state = _HA_ATOMIC_LOAD(&t->state);
|
||||
thr = t->tid;
|
||||
if (thr == -1)
|
||||
newtid = -2 - tid;
|
||||
else
|
||||
newtid = thr;
|
||||
|
||||
/* do nothing if someone else already added it */
|
||||
if (state & (TASK_QUEUED|TASK_RUNNING))
|
||||
return;
|
||||
} while (!__task_set_state_and_tid(t, thr, newtid, state, state | TASK_QUEUED));
|
||||
} while (!_HA_ATOMIC_CAS(&t->state, &state, state | TASK_QUEUED));
|
||||
|
||||
if (newtid < 0)
|
||||
thr = __task_get_current_owner(newtid);
|
||||
BUG_ON_HOT(task_in_rq(t));
|
||||
|
||||
/* at this point we're the first ones to add this task to the list */
|
||||
|
|
@ -778,11 +707,11 @@ static inline void tasklet_set_tid(struct tasklet *tl, int tid)
|
|||
|
||||
static inline void _task_schedule(struct task *task, int when, const struct ha_caller *caller)
|
||||
{
|
||||
int did_lock = 0;
|
||||
/* TODO: mthread, check if there is no task with this test */
|
||||
if (task_in_rq(task))
|
||||
return;
|
||||
|
||||
#ifdef USE_THREAD
|
||||
if (task->tid < 0) {
|
||||
/*
|
||||
* If the task is already running, then just wake it up, just
|
||||
|
|
@ -800,26 +729,44 @@ static inline void _task_schedule(struct task *task, int when, const struct ha_c
|
|||
task_wakeup(task, TASK_WOKEN_OTHER);
|
||||
return;
|
||||
}
|
||||
did_lock = 1;
|
||||
} else
|
||||
BUG_ON(task->tid != tid);
|
||||
|
||||
if (task_in_wq(task))
|
||||
when = tick_first(when, task->expire);
|
||||
/* FIXME: is it really needed to lock the WQ during the check ? */
|
||||
HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
if (task_in_wq(task))
|
||||
when = tick_first(when, task->expire);
|
||||
|
||||
task->expire = when;
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
task->expire = when;
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
#ifdef DEBUG_TASK
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
#endif
|
||||
}
|
||||
__task_queue(task, &tg_ctx->timers);
|
||||
}
|
||||
__task_queue(task);
|
||||
}
|
||||
if (did_lock)
|
||||
task_drop_running(task, 0);
|
||||
HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
BUG_ON(task->tid != tid);
|
||||
if (task_in_wq(task))
|
||||
when = tick_first(when, task->expire);
|
||||
|
||||
task->expire = when;
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
#ifdef DEBUG_TASK
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
#endif
|
||||
}
|
||||
__task_queue(task, &th_ctx->timers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* returns the string corresponding to a task type as found in the task caller
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ struct ha_rwlock {
|
|||
*/
|
||||
enum lock_label {
|
||||
TASK_RQ_LOCK,
|
||||
TASK_WQ_LOCK,
|
||||
LISTENER_LOCK,
|
||||
PROXY_LOCK,
|
||||
SERVER_LOCK,
|
||||
|
|
|
|||
|
|
@ -135,6 +135,8 @@ struct tgroup_ctx {
|
|||
ulong threads_idle; /* mask of threads idling in the poller */
|
||||
ulong stopping_threads; /* mask of threads currently stopping */
|
||||
|
||||
struct eb_root timers; /* wait queue (sorted timers tree, global, accessed under wq_lock) */
|
||||
|
||||
uint niced_tasks; /* number of niced tasks in this group's run queues */
|
||||
uint committed_extra_streams; /* sum of extra front streams committed by muxes in this group */
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ core.register_service("fakeserv", "http", function(applet)
|
|||
end)
|
||||
|
||||
local function cron()
|
||||
local httpclient = core.httpclient()
|
||||
|
||||
-- wait for until the correct port is set through the c0 request..
|
||||
while vtc_port == 0 do
|
||||
core.msleep(1)
|
||||
|
|
@ -32,16 +30,19 @@ local function cron()
|
|||
body = body .. i .. ' ABCDEFGHIJKLMNOPQRSTUVWXYZ\n'
|
||||
end
|
||||
core.Info("First httpclient request")
|
||||
local httpclient = core.httpclient()
|
||||
local response = httpclient:post{url="http://127.0.0.1:" .. vtc_port, body=body}
|
||||
core.Info("Received: " .. response.body)
|
||||
|
||||
body = response.body
|
||||
|
||||
core.Info("Second httpclient request")
|
||||
local response2 = httpclient:post{url="http://127.0.0.1:" .. vtc_port2, body=body}
|
||||
local httpclient2 = core.httpclient()
|
||||
local response2 = httpclient2:post{url="http://127.0.0.1:" .. vtc_port2, body=body}
|
||||
|
||||
core.Info("Third httpclient request")
|
||||
local response3 = httpclient:get{url="http://127.0.0.1", dst = vtc_port3, headers={ [ "Host" ] = { "foobar.haproxy.local" } }}
|
||||
local httpclient3 = core.httpclient()
|
||||
local response3 = httpclient3:get{url="http://127.0.0.1", dst = vtc_port3, headers={ [ "Host" ] = { "foobar.haproxy.local" } }}
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
297
src/acme.c
297
src/acme.c
|
|
@ -19,7 +19,6 @@
|
|||
#include <haproxy/acme-t.h>
|
||||
|
||||
#include <haproxy/acme_resolvers.h>
|
||||
#include <haproxy/event_hdl.h>
|
||||
#include <haproxy/base64.h>
|
||||
#include <haproxy/intops.h>
|
||||
#include <haproxy/cfgparse.h>
|
||||
|
|
@ -38,12 +37,6 @@
|
|||
#include <haproxy/ssl_utils.h>
|
||||
#include <haproxy/tools.h>
|
||||
#include <haproxy/trace.h>
|
||||
#ifdef USE_LUA
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <haproxy/hlua.h>
|
||||
#include <haproxy/hlua_fcn.h>
|
||||
#endif
|
||||
|
||||
#define TRACE_SOURCE &trace_acme
|
||||
|
||||
|
|
@ -1457,27 +1450,6 @@ error:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* mfree callback for EVENT_HDL_SUB_ACME_DEPLOY: frees heap-allocated fields */
|
||||
static void acme_deploy_event_mfree(const void *data)
|
||||
{
|
||||
struct event_hdl_cb_data_acme_deploy *e = (struct event_hdl_cb_data_acme_deploy *)data;
|
||||
|
||||
ha_free(&e->safe.crtname);
|
||||
ha_free(&e->safe.domain);
|
||||
ha_free(&e->safe.thumbprint);
|
||||
ha_free(&e->safe.dns_record);
|
||||
ha_free(&e->safe.provider);
|
||||
ha_free(&e->safe.vars);
|
||||
}
|
||||
|
||||
/* mfree callback for EVENT_HDL_SUB_ACME_NEWCERT: frees the heap-allocated path */
|
||||
static void acme_newcert_event_mfree(const void *data)
|
||||
{
|
||||
const struct event_hdl_cb_data_acme_newcert *e = data;
|
||||
|
||||
free(e->safe.crtname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update every certificate instances for the new store
|
||||
*
|
||||
|
|
@ -1531,15 +1503,6 @@ int acme_update_certificate(struct task *task, struct acme_ctx *ctx, char **errm
|
|||
if (dpapi)
|
||||
sink_write(dpapi, LOG_HEADER_NONE, 0, line, 3);
|
||||
|
||||
{
|
||||
struct event_hdl_cb_data_acme_newcert cb_data = { };
|
||||
|
||||
cb_data.safe.crtname = strdup(ctx->store->path);
|
||||
if (cb_data.safe.crtname)
|
||||
event_hdl_publish(NULL, EVENT_HDL_SUB_ACME_NEWCERT,
|
||||
EVENT_HDL_CB_DATA_DM(&cb_data, acme_newcert_event_mfree));
|
||||
}
|
||||
|
||||
ctx->store = NULL;
|
||||
|
||||
ret = 0;
|
||||
|
|
@ -1569,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);
|
||||
|
|
@ -1599,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;
|
||||
|
|
@ -1782,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);
|
||||
|
|
@ -1883,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);
|
||||
|
|
@ -2010,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);
|
||||
|
|
@ -2065,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;
|
||||
}
|
||||
|
||||
|
|
@ -2219,22 +2171,6 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||
dpapi = sink_find("dpapi");
|
||||
if (dpapi)
|
||||
sink_write(dpapi, LOG_HEADER_NONE, 0, line, nmsg);
|
||||
|
||||
{
|
||||
struct event_hdl_cb_data_acme_deploy cb_data = { };
|
||||
|
||||
cb_data.safe.crtname = strdup(ctx->store->path);
|
||||
cb_data.safe.domain = isttest(auth->dns) ? strndup(auth->dns.ptr, auth->dns.len) : NULL;
|
||||
cb_data.safe.thumbprint = ctx->cfg->account.thumbprint ? strdup(ctx->cfg->account.thumbprint) : NULL;
|
||||
cb_data.safe.dns_record = strndup(dns_record->area, dns_record->data);
|
||||
cb_data.safe.provider = ctx->cfg->provider ? strdup(ctx->cfg->provider) : NULL;
|
||||
cb_data.safe.vars = ctx->cfg->vars ? strdup(ctx->cfg->vars) : NULL;
|
||||
if (cb_data.safe.crtname && cb_data.safe.dns_record)
|
||||
event_hdl_publish(NULL, EVENT_HDL_SUB_ACME_DEPLOY,
|
||||
EVENT_HDL_CB_DATA_DM(&cb_data, acme_deploy_event_mfree));
|
||||
else
|
||||
acme_deploy_event_mfree(&cb_data);
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(ctx->cfg->challenge, "http-01") == 0) {
|
||||
/* only useful for http-01 */
|
||||
|
|
@ -2343,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);
|
||||
|
|
@ -2522,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);
|
||||
|
|
@ -2589,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);
|
||||
|
|
@ -3559,64 +3495,16 @@ err:
|
|||
return cli_dynerr(appctx, errmsg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the readiness of an ACME challenge per couple <crt>+<dns>
|
||||
* Return:
|
||||
* - -2 if the crt was not found
|
||||
* - -1 if an non-ready couple crt+dns wasn't not found
|
||||
* - 0 if the challenges are ready for the certificate
|
||||
* - > 0 with the number of remaining challenge to enable
|
||||
*/
|
||||
int acme_challenge_ready(const char *crt, const char *dns)
|
||||
{
|
||||
struct ebmb_node *node = NULL;
|
||||
struct acme_ctx *ctx = NULL;
|
||||
struct acme_auth *auth = NULL;
|
||||
int found = 0;
|
||||
int remain = 0;
|
||||
|
||||
HA_RWLOCK_WRLOCK(OTHER_LOCK, &acme_lock);
|
||||
node = ebst_lookup(&acme_tasks, crt);
|
||||
if (!node) {
|
||||
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
||||
return -2;
|
||||
}
|
||||
|
||||
ctx = ebmb_entry(node, struct acme_ctx, node);
|
||||
if (ctx->cfg->cond_ready & ACME_RDY_CLI)
|
||||
auth = ctx->auths;
|
||||
while (auth) {
|
||||
if (strncmp(dns, auth->dns.ptr, auth->dns.len) == 0) {
|
||||
if ((auth->ready & ACME_RDY_CLI) == 0) {
|
||||
auth->ready |= ACME_RDY_CLI;
|
||||
found++;
|
||||
}
|
||||
}
|
||||
if ((auth->ready & ACME_RDY_CLI) == 0)
|
||||
remain++;
|
||||
auth = auth->next;
|
||||
}
|
||||
/* no remaining challenge to ready: wake the task only if it is
|
||||
* currently suspended in ACME_CLI_WAIT, not in the middle of an
|
||||
* HTTP exchange.
|
||||
*/
|
||||
if (!remain && ctx->state == ACME_CLI_WAIT)
|
||||
task_wakeup(ctx->task, TASK_WOKEN_MSG);
|
||||
|
||||
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
||||
|
||||
if (!found)
|
||||
return -1;
|
||||
|
||||
return remain;
|
||||
}
|
||||
|
||||
static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx *appctx, void *private)
|
||||
{
|
||||
char *msg = NULL;
|
||||
const char *crt;
|
||||
const char *dns;
|
||||
int ret;
|
||||
struct acme_ctx *ctx = NULL;
|
||||
struct acme_auth *auth = NULL;
|
||||
int found = 0;
|
||||
int remain = 0;
|
||||
struct ebmb_node *node = NULL;
|
||||
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||
return 1;
|
||||
|
|
@ -3629,13 +3517,39 @@ static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx
|
|||
crt = args[2];
|
||||
dns = args[4];
|
||||
|
||||
ret = acme_challenge_ready(crt, dns);
|
||||
if (ret < 0) {
|
||||
return cli_dynerr(appctx, memprintf(&msg, "Couldn't find an ACME task using crt \"%s\" and dns \"%s\" to set as ready!\n", crt, dns));
|
||||
} else if (ret == 0) {
|
||||
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "'%s' challenges ready! All challenges ready, starting challenges validation!", crt));
|
||||
} else if (ret > 0) {
|
||||
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "'%s' challenge(s) ready! Remaining challenges to deploy: %d", crt, ret));
|
||||
HA_RWLOCK_WRLOCK(OTHER_LOCK, &acme_lock);
|
||||
node = ebst_lookup(&acme_tasks, crt);
|
||||
if (node) {
|
||||
ctx = ebmb_entry(node, struct acme_ctx, node);
|
||||
if (ctx->cfg->cond_ready & ACME_RDY_CLI)
|
||||
auth = ctx->auths;
|
||||
while (auth) {
|
||||
if (strncmp(dns, auth->dns.ptr, auth->dns.len) == 0) {
|
||||
if (!(auth->ready & ACME_RDY_CLI)) {
|
||||
auth->ready |= ACME_RDY_CLI;
|
||||
found++;
|
||||
} else {
|
||||
memprintf(&msg, "ACME challenge for crt \"%s\" and dns \"%s\" was already READY !\n", crt, dns);
|
||||
}
|
||||
}
|
||||
if ((auth->ready & ACME_RDY_CLI) == 0)
|
||||
remain++;
|
||||
auth = auth->next;
|
||||
}
|
||||
}
|
||||
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
||||
if (!found) {
|
||||
if (!msg)
|
||||
memprintf(&msg, "Couldn't find an ACME task using crt \"%s\" and dns \"%s\" to set as ready!\n", crt, dns);
|
||||
goto err;
|
||||
} else {
|
||||
if (!remain) {
|
||||
if (ctx)
|
||||
task_wakeup(ctx->task, TASK_WOKEN_MSG);
|
||||
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "%d '%s' challenge(s) ready! All challenges ready, starting challenges validation!", found, dns));
|
||||
} else {
|
||||
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "%d '%s' challenge(s) ready! Remaining challenges to deploy: %d", found, dns, remain));
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
|
|
@ -3745,127 +3659,6 @@ static void __acme_init(void)
|
|||
}
|
||||
INITCALL0(STG_REGISTER, __acme_init);
|
||||
|
||||
#ifdef USE_LUA
|
||||
|
||||
#define CLASS_ACME_EVENT "AcmeEvent"
|
||||
static int class_acme_event_ref;
|
||||
|
||||
/* Push a new AcmeEvent object for an ACME_DEPLOY event onto the Lua stack.
|
||||
* The object exposes crtname, domain, thumbprint, dns_record fields, and
|
||||
* optionally provider and vars if they were configured.
|
||||
*/
|
||||
static void hlua_fcn_new_acme_event_deploy(lua_State *L, const struct event_hdl_cb_data_acme_deploy *e)
|
||||
{
|
||||
lua_newtable(L);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, class_acme_event_ref);
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
lua_pushstring(L, e->safe.crtname ? e->safe.crtname : "");
|
||||
lua_setfield(L, -2, "crtname");
|
||||
|
||||
lua_pushstring(L, e->safe.domain ? e->safe.domain : "");
|
||||
lua_setfield(L, -2, "domain");
|
||||
|
||||
lua_pushstring(L, e->safe.thumbprint ? e->safe.thumbprint : "");
|
||||
lua_setfield(L, -2, "thumbprint");
|
||||
|
||||
lua_pushstring(L, e->safe.dns_record ? e->safe.dns_record : "");
|
||||
lua_setfield(L, -2, "dns_record");
|
||||
|
||||
if (e->safe.provider) {
|
||||
lua_pushstring(L, e->safe.provider);
|
||||
lua_setfield(L, -2, "provider");
|
||||
}
|
||||
|
||||
if (e->safe.vars) {
|
||||
lua_pushstring(L, e->safe.vars);
|
||||
lua_setfield(L, -2, "vars");
|
||||
}
|
||||
}
|
||||
|
||||
/* Push a new AcmeEvent object for an ACME_NEWCERT event onto the Lua stack.
|
||||
* The object exposes a <crtname> field with the certificate store name.
|
||||
*/
|
||||
static void hlua_fcn_new_acme_event_newcert(lua_State *L, const char *crtname)
|
||||
{
|
||||
lua_newtable(L);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, class_acme_event_ref);
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
lua_pushstring(L, crtname);
|
||||
lua_setfield(L, -2, "crtname");
|
||||
}
|
||||
|
||||
/*
|
||||
* ACME.challenge_ready(crt, dns)
|
||||
*
|
||||
* Marks the ACME challenge for domain <dns> in certificate <crt> as ready.
|
||||
* Returns the number of remaining challenges, or 0 if all challenges are
|
||||
* ready and validation has been triggered.
|
||||
* Raises a Lua error if the certificate or domain is not found.
|
||||
*/
|
||||
__LJMP static int hlua_acme_challenge_ready(lua_State *L)
|
||||
{
|
||||
const char *crt;
|
||||
const char *dns;
|
||||
int ret;
|
||||
|
||||
if (lua_gettop(L) != 2)
|
||||
WILL_LJMP(luaL_error(L, "'ACME.challenge_ready' needs 2 arguments."));
|
||||
|
||||
crt = MAY_LJMP(luaL_checkstring(L, 1));
|
||||
dns = MAY_LJMP(luaL_checkstring(L, 2));
|
||||
|
||||
ret = acme_challenge_ready(crt, dns);
|
||||
if (ret == -2)
|
||||
WILL_LJMP(luaL_error(L, "ACME.challenge_ready: certificate '%s' not found", crt));
|
||||
if (ret == -1)
|
||||
WILL_LJMP(luaL_error(L, "ACME.challenge_ready: domain '%s' not found for certificate '%s'", dns, crt));
|
||||
|
||||
lua_pushinteger(L, ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int acme_hlua_init_state(lua_State *L, char **errmsg)
|
||||
{
|
||||
/* Register AcmeEvent class */
|
||||
lua_newtable(L);
|
||||
class_acme_event_ref = hlua_register_metatable(L, CLASS_ACME_EVENT);
|
||||
|
||||
lua_newtable(L);
|
||||
hlua_class_function(L, "challenge_ready", hlua_acme_challenge_ready);
|
||||
lua_setglobal(L, "ACME");
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
REGISTER_HLUA_STATE_INIT(acme_hlua_init_state);
|
||||
|
||||
/* Push ACME event data as a Lua table for core.event_sub() handlers.
|
||||
* Called from hlua_event_hdl_cb_push_args() when the event family is ACME.
|
||||
*/
|
||||
void acme_hlua_event_push_args(struct hlua *hlua, struct event_hdl_sub_type event, void *data)
|
||||
{
|
||||
if (!lua_checkstack(hlua->T, 3))
|
||||
WILL_LJMP(luaL_error(hlua->T, "Lua out of memory error."));
|
||||
|
||||
if (event_hdl_sub_type_equal(EVENT_HDL_SUB_ACME_DEPLOY, event)) {
|
||||
struct event_hdl_cb_data_acme_deploy *e_acme = data;
|
||||
|
||||
hlua->nargs += 1;
|
||||
MAY_LJMP(hlua_fcn_new_acme_event_deploy(hlua->T, e_acme));
|
||||
}
|
||||
else if (event_hdl_sub_type_equal(EVENT_HDL_SUB_ACME_NEWCERT, event)) {
|
||||
struct event_hdl_cb_data_acme_newcert *e_acme = data;
|
||||
|
||||
hlua->nargs += 1;
|
||||
MAY_LJMP(hlua_fcn_new_acme_event_newcert(hlua->T, e_acme->safe.crtname));
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* USE_LUA */
|
||||
|
||||
#endif /* ! HAVE_ACME */
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
@ -611,7 +613,7 @@ size_t appctx_htx_snd_buf(struct appctx *appctx, struct buffer *buf, size_t coun
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -1818,7 +1818,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;
|
||||
const struct mux_proto_list *mux_proto;
|
||||
struct server *srv;
|
||||
struct ist name = IST_NULL;
|
||||
struct sample *name_smp;
|
||||
|
|
@ -2139,10 +2139,12 @@ int connect_server(struct stream *s)
|
|||
}
|
||||
|
||||
if (may_start_mux_now) {
|
||||
/* Delay MUX init if an XPRT handshake is required prior. */
|
||||
/* Delay QMux MUX init to let xprt_qmux handshake runs first. */
|
||||
mux_proto = conn_select_mux_be(srv_conn);
|
||||
if (mux_proto && mux_proto->init_xprt)
|
||||
if (mux_proto && mux_proto->init_xprt == XPRT_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 +2254,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
26
src/check.c
26
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;
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -847,43 +847,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.
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
@ -1279,7 +1269,7 @@ static int cpu_policy_first_usable_node(int policy, int tmin, int tmax, int gmin
|
|||
if (tmin <= thr_count && thr_count < tmax)
|
||||
tmax = thr_count;
|
||||
|
||||
ha_diag_notice("Multi-socket cpu detected, automatically binding on active CPUs of '%d' (%u active cpu(s))\n", first_node_id, cpu_count);
|
||||
ha_diag_warning("Multi-socket cpu detected, automatically binding on active CPUs of '%d' (%u active cpu(s))\n", first_node_id, cpu_count);
|
||||
|
||||
if (!global.nbthread)
|
||||
global.nbthread = tmax;
|
||||
|
|
@ -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
|
||||
|
|
@ -1586,9 +1565,9 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
|
|||
}
|
||||
|
||||
if (global.nbthread)
|
||||
ha_diag_notice("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
||||
ha_diag_warning("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
||||
else
|
||||
ha_diag_notice("Could not determine any CPU cluster\n");
|
||||
ha_diag_warning("Could not determine any CPU cluster\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1684,9 +1653,9 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
|
|||
}
|
||||
|
||||
if (global.nbthread)
|
||||
ha_diag_notice("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
||||
ha_diag_warning("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
||||
else
|
||||
ha_diag_notice("Could not determine any CPU cluster\n");
|
||||
ha_diag_warning("Could not determine any CPU cluster\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
14
src/errors.c
14
src/errors.c
|
|
@ -386,20 +386,6 @@ void ha_diag_warning(const char *fmt, ...)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Displays the message on stderr with the pid if MODE_DIAG is set.
|
||||
*/
|
||||
void ha_diag_notice(const char *fmt, ...)
|
||||
{
|
||||
va_list argp;
|
||||
|
||||
if (global.mode & MODE_DIAG) {
|
||||
va_start(argp, fmt);
|
||||
print_message(1, "DIAG", fmt, argp);
|
||||
va_end(argp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Displays the message on stderr with the pid.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -48,9 +48,6 @@ static struct event_hdl_sub_type_map event_hdl_sub_type_map[] = {
|
|||
{"PAT_REF_SET", EVENT_HDL_SUB_PAT_REF_SET},
|
||||
{"PAT_REF_COMMIT", EVENT_HDL_SUB_PAT_REF_COMMIT},
|
||||
{"PAT_REF_CLEAR", EVENT_HDL_SUB_PAT_REF_CLEAR},
|
||||
{"ACME", EVENT_HDL_SUB_ACME},
|
||||
{"ACME_NEWCERT", EVENT_HDL_SUB_ACME_NEWCERT},
|
||||
{"ACME_DEPLOY", EVENT_HDL_SUB_ACME_DEPLOY},
|
||||
};
|
||||
|
||||
/* internal types (only used in this file) */
|
||||
|
|
|
|||
30
src/h1.c
30
src/h1.c
|
|
@ -316,9 +316,6 @@ void h1_parse_upgrade_header(struct h1m *h1m, struct ist *value)
|
|||
skip_val:
|
||||
word.ptr = p = n;
|
||||
}
|
||||
|
||||
if (istlen(*value))
|
||||
h1m->flags |= H1_MF_UPG_HDR;
|
||||
}
|
||||
|
||||
/* Macros used in the HTTP/1 parser, to check for the expected presence of
|
||||
|
|
@ -713,16 +710,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 +952,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) {
|
||||
|
|
@ -1275,10 +1248,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);
|
||||
|
|
|
|||
27
src/h1_htx.c
27
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,31 +213,6 @@ 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
|
|
|||
193
src/h3.c
193
src/h3.c
|
|
@ -92,10 +92,6 @@ static const struct name_desc h3_trace_decoding[] = {
|
|||
{ .name="clean", .desc="only user-friendly stuff, generally suitable for level \"user\"" },
|
||||
#define H3_VERB_MINIMAL 2
|
||||
{ .name="minimal", .desc="report only qcc/qcs state and flags, no real decoding" },
|
||||
#define H3_VERB_SIMPLE 3
|
||||
{ .name="simple", .desc="add request/response status line or frame info when available" },
|
||||
#define H3_VERB_ADVANCED 4
|
||||
{ .name="advanced", .desc="add header fields or frame decoding when available" },
|
||||
{ /* end */ }
|
||||
};
|
||||
|
||||
|
|
@ -216,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:
|
||||
|
|
@ -394,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
|
||||
|
|
@ -407,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))
|
||||
|
|
@ -433,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:
|
||||
|
|
@ -634,51 +594,6 @@ static struct ist _h3_trim_header(struct ist value)
|
|||
return v;
|
||||
}
|
||||
|
||||
static void _h3_trace_header(const struct ist n, const struct ist v,
|
||||
uint64_t mask, const struct ist trc_loc, const char *func,
|
||||
const struct qcc *qcc, const struct qcs *qcs)
|
||||
{
|
||||
struct ist n_short, v_short;
|
||||
const char *c_str __maybe_unused;
|
||||
const char *s_str __maybe_unused;
|
||||
|
||||
chunk_reset(&trash);
|
||||
c_str = chunk_newstr(&trash);
|
||||
if (qcc)
|
||||
chunk_appendf(&trash, "qcc=%p(%c)", qcc, (qcc->flags & QC_CF_IS_BACK) ? 'B' : 'F');
|
||||
|
||||
s_str = chunk_newstr(&trash);
|
||||
if (qcs)
|
||||
chunk_appendf(&trash, " qcc=%p(%llu)", qcs, (ullong)qcs->id);
|
||||
|
||||
n_short = ist2(chunk_newstr(&trash), 0);
|
||||
istscpy(&n_short, n, 256);
|
||||
trash.data += n_short.len;
|
||||
if (n_short.len != n.len)
|
||||
chunk_appendf(&trash, " (... +%ld)", (long)(n.len - n_short.len));
|
||||
|
||||
v_short = ist2(chunk_newstr(&trash), 0);
|
||||
istscpy(&v_short, v, 1024);
|
||||
trash.data += v_short.len;
|
||||
if (v_short.len != v.len)
|
||||
chunk_appendf(&trash, " (... +%ld)", (long)(v.len - v_short.len));
|
||||
|
||||
TRACE_PRINTF_LOC(TRACE_LEVEL_USER, mask, trc_loc, func,
|
||||
0, 0, 0, 0, "%s%s %s %s: %s", c_str, s_str,
|
||||
mask & H3_EV_TX_HDR ? "sndh" : "rcvh",
|
||||
istptr(n_short), istptr(v_short));
|
||||
}
|
||||
|
||||
/* Output a trace for HTTP/3 header <n>:<v> if tracing is enabled. */
|
||||
static void h3_trace_header(const struct ist n, const struct ist v,
|
||||
uint64_t mask, const struct ist trc_loc, const char *func,
|
||||
const struct qcc *qcc, const struct qcs *qcs)
|
||||
{
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
|
||||
TRACE_ENABLED(TRACE_LEVEL_USER, mask, qcc ? qcc->conn : 0, qcs, 0, 0))
|
||||
_h3_trace_header(n, v, mask, trc_loc, func, qcc, qcs);
|
||||
}
|
||||
|
||||
/* Parse from buffer <buf> a H3 HEADERS frame of length <len>. Data are copied
|
||||
* in a local HTX buffer and transfer to the stream connector layer. <fin> must be
|
||||
* set if this is the last data to transfer from this stream.
|
||||
|
|
@ -744,12 +659,6 @@ static ssize_t h3_req_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
|
||||
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, 0, 0, 0)) {
|
||||
for (i = 0; list[i].n.len; ++i)
|
||||
h3_trace_header(list[i].n, list[i].v, H3_EV_RX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
}
|
||||
|
||||
if (!b_alloc(&htx_buf, DB_SE_RX)) {
|
||||
TRACE_ERROR("HTX buffer alloc failure", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
|
||||
len = -1;
|
||||
|
|
@ -1238,13 +1147,6 @@ static ssize_t h3_resp_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
|
||||
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, 0, 0, 0)) {
|
||||
int i;
|
||||
for (i = 0; list[i].n.len; ++i)
|
||||
h3_trace_header(list[i].n, list[i].v, H3_EV_RX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
}
|
||||
|
||||
if (!(appbuf = qcc_get_stream_rxbuf(qcs))) {
|
||||
TRACE_ERROR("buffer alloc failure", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
|
||||
len = -1;
|
||||
|
|
@ -2028,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) {
|
||||
|
|
@ -2055,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) {
|
||||
|
|
@ -2064,26 +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.
|
||||
*/
|
||||
TRACE_ERROR("Received unexpected PUSH_PROMISE frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
|
||||
qcc_set_error(qcs->qcc, H3_ERR_ID_ERROR, 1, muxc_tevt_type_proto_err);
|
||||
goto err;
|
||||
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
|
||||
*
|
||||
|
|
@ -2322,7 +2191,6 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx)
|
|||
|
||||
if (qpack_encode_method(&headers_buf, sl->info.req.meth, meth))
|
||||
goto err_full;
|
||||
h3_trace_header(ist(":method"), meth, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
|
||||
if (uri.ptr[0] != '/' && uri.ptr[0] != '*') {
|
||||
int len = 1;
|
||||
|
|
@ -2354,23 +2222,13 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx)
|
|||
|
||||
if (qpack_encode_scheme(&headers_buf, scheme))
|
||||
goto err_full;
|
||||
h3_trace_header(ist(":scheme"), scheme, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
|
||||
if (qpack_encode_path(&headers_buf, uri))
|
||||
goto err_full;
|
||||
h3_trace_header(ist(":path"), uri, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
|
||||
if (istlen(auth)) {
|
||||
if (qpack_encode_auth(&headers_buf, auth))
|
||||
goto err_full;
|
||||
h3_trace_header(ist(":authority"), auth, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
}
|
||||
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
|
||||
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, 0, 0, 0)) {
|
||||
int i;
|
||||
for (i = 0; list[i].n.len; ++i)
|
||||
h3_trace_header(list[i].n, list[i].v, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
}
|
||||
|
||||
if (!(sl->flags & HTX_SL_F_XFER_LEN)) {
|
||||
|
|
@ -2507,13 +2365,6 @@ static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
|
|||
TRACE_USER("handling final HTX response", H3_EV_STRM_SEND, qcs->qcc->conn, qcs);
|
||||
h3s->flags &= ~H3_SF_SENT_INTERIM;
|
||||
}
|
||||
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED) {
|
||||
char sts[4];
|
||||
h3_trace_header(ist(":status"), ist(ultoa_r(status, sts, sizeof(sts))),
|
||||
H3_EV_TX_FRAME|H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__,
|
||||
qcs->qcc, qcs);
|
||||
}
|
||||
}
|
||||
else if (type == HTX_BLK_HDR) {
|
||||
if (unlikely(hdr >= sizeof(list) / sizeof(list[0]) - 1)) {
|
||||
|
|
@ -2531,14 +2382,6 @@ static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
|
|||
}
|
||||
}
|
||||
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
|
||||
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, 0, 0, 0)) {
|
||||
int i;
|
||||
for (i = 0; list[i].n.len; ++i)
|
||||
h3_trace_header(list[i].n, list[i].v, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
}
|
||||
|
||||
|
||||
/* Current function expects HTX start-line to be present. This also
|
||||
* ensures <status> conformance has been checked prior to encoding it.
|
||||
*/
|
||||
|
|
|
|||
109
src/haproxy.c
109
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;
|
||||
|
|
@ -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);
|
||||
|
|
@ -3364,18 +3329,16 @@ static void setup_user_ns(uid_t euid, gid_t egid)
|
|||
|
||||
static int do_chroot(const char *prog, const char *path)
|
||||
{
|
||||
const char *dir, *chroot_dir;
|
||||
int error, chroot_error;
|
||||
const char *chroot_dir = path;
|
||||
int error = 0;
|
||||
|
||||
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) {
|
||||
chroot_dir = mkdtemp(tmpdir);
|
||||
if (chroot_dir == NULL) {
|
||||
ha_alert("[%s.main()] Cannot create(%s) for chroot auto.\n",
|
||||
prog, tmpdir);
|
||||
return -1;
|
||||
|
|
@ -3386,32 +3349,16 @@ static int do_chroot(const char *prog, const char *path)
|
|||
* want to remove the directory).
|
||||
*/
|
||||
DISGUISE(rmdir(tmpdir));
|
||||
chroot_dir = ".";
|
||||
if (!error)
|
||||
chroot_error = chroot(".");
|
||||
error = chroot(".");
|
||||
} else if (strcmp(path, "/") != 0) {
|
||||
chroot_error = chroot(path);
|
||||
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)
|
||||
if (!error)
|
||||
error = chdir("/");
|
||||
|
||||
if (error || chroot_error) {
|
||||
ha_alert("[%s.main()] Cannot chroot(%s).\n", prog, dir);
|
||||
if (error) {
|
||||
ha_alert("[%s.main()] Cannot chroot(%s).\n", prog, chroot_dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -3755,6 +3702,20 @@ int main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CLONE_NEWUSER
|
||||
/* When we aren't root and intend to chroot, we try the Linux-only
|
||||
* unshare(CLONE_NEWUSER) mechanism if available to allow chroot as an
|
||||
* unprivileged user. If that doesn't work, we just let the subsequent
|
||||
* chroot() fail as it would have previously.
|
||||
*/
|
||||
if (geteuid() != 0 && global.chroot != NULL) {
|
||||
uid_t euid = geteuid();
|
||||
gid_t egid = getegid();
|
||||
if (unshare(CLONE_NEWUSER) == 0)
|
||||
setup_user_ns(euid, egid);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Must chroot and setgid/setuid in the children */
|
||||
/* chroot if needed */
|
||||
if (global.chroot != NULL && do_chroot(argv[0], global.chroot) != 0) {
|
||||
|
|
|
|||
|
|
@ -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':
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
662
src/hlua.c
662
src/hlua.c
|
|
@ -73,9 +73,6 @@
|
|||
#include <haproxy/event_hdl.h>
|
||||
#include <haproxy/check.h>
|
||||
#include <haproxy/mailers.h>
|
||||
#if defined(HAVE_ACME)
|
||||
#include <haproxy/acme.h>
|
||||
#endif /* HAVE_ACME */
|
||||
|
||||
/* Global LUA flags */
|
||||
|
||||
|
|
@ -108,6 +105,16 @@ static uint8_t hlua_body = 1;
|
|||
*/
|
||||
static uint8_t hlua_bool_sample_conversion = HLUA_BOOL_SAMPLE_CONVERSION_UNK;
|
||||
|
||||
/* Lua uses longjmp to perform yield or throwing errors. This
|
||||
* macro is used only for identifying the function that can
|
||||
* not return because a longjmp is executed.
|
||||
* __LJMP marks a prototype of hlua file that can use longjmp.
|
||||
* WILL_LJMP() marks an lua function that will use longjmp.
|
||||
* MAY_LJMP() marks an lua function that may use longjmp.
|
||||
*/
|
||||
#define __LJMP
|
||||
#define WILL_LJMP(func) do { func; my_unreachable(); } while(0)
|
||||
#define MAY_LJMP(func) func
|
||||
|
||||
/* This couple of function executes securely some Lua calls outside of
|
||||
* the lua runtime environment. Each Lua call can return a longjmp
|
||||
|
|
@ -169,24 +176,6 @@ static int hlua_panic_ljmp(lua_State *L) { WILL_LJMP(longjmp(safe_ljmp_env, 1));
|
|||
*/
|
||||
static struct list referenced_functions = LIST_HEAD_INIT(referenced_functions);
|
||||
|
||||
/* List of callbacks registered via hap_register_hlua_state_init(), called
|
||||
* for each new lua_State created in hlua_init_state().
|
||||
*/
|
||||
static struct list hlua_state_init_list = LIST_HEAD_INIT(hlua_state_init_list);
|
||||
|
||||
void hap_register_hlua_state_init(int (*fct)(lua_State *L, char **errmsg))
|
||||
{
|
||||
struct hlua_state_init_fct *entry;
|
||||
|
||||
entry = calloc(1, sizeof(*entry));
|
||||
if (!entry) {
|
||||
ha_alert("hlua: out of memory registering state init callback\n");
|
||||
exit(1);
|
||||
}
|
||||
entry->fct = fct;
|
||||
LIST_APPEND(&hlua_state_init_list, &entry->list);
|
||||
}
|
||||
|
||||
/* This variable is used only during initialization to identify the Lua state
|
||||
* currently being initialized. 0 is the common lua state, 1 to n are the Lua
|
||||
* states dedicated to each thread (in this case hlua_state_id==tid+1).
|
||||
|
|
@ -510,6 +499,7 @@ static int class_fetches_ref;
|
|||
static int class_converters_ref;
|
||||
static int class_http_ref;
|
||||
static int class_http_msg_ref;
|
||||
static int class_httpclient_ref;
|
||||
static int class_map_ref;
|
||||
static int class_applet_tcp_ref;
|
||||
static int class_applet_http_ref;
|
||||
|
|
@ -998,6 +988,13 @@ const char *hlua_traceback(lua_State *L, const char* sep)
|
|||
* stack. If the number of arguments available is not the same
|
||||
* then <nb> an error is thrown.
|
||||
*/
|
||||
__LJMP static inline void check_args(lua_State *L, int nb, char *fcn)
|
||||
{
|
||||
if (lua_gettop(L) == nb)
|
||||
return;
|
||||
WILL_LJMP(luaL_error(L, "'%s' needs %d arguments", fcn, nb));
|
||||
}
|
||||
|
||||
/* This function pushes an error string prefixed by the file name
|
||||
* and the line number where the error is encountered.
|
||||
*
|
||||
|
|
@ -1014,7 +1011,7 @@ __LJMP static int _hlua_pusherror(lua_State *L)
|
|||
|
||||
return 1;
|
||||
}
|
||||
int hlua_pusherror(lua_State *L, const char *fmt, ...)
|
||||
static int hlua_pusherror(lua_State *L, const char *fmt, ...)
|
||||
{
|
||||
va_list argp;
|
||||
int ret = 1;
|
||||
|
|
@ -1786,6 +1783,34 @@ int hlua_ctx_init(struct hlua *lua, int state_id, struct task *task)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* kill all associated httpclient to this hlua task
|
||||
* We must take extra precautions as we're manipulating lua-exposed
|
||||
* objects without the main lua lock.
|
||||
*/
|
||||
static void hlua_httpclient_destroy_all(struct hlua *hlua)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
|
||||
/* use thread-safe accessors for hc_list since GC cycle initiated by
|
||||
* another thread sharing the same main lua stack (lua coroutine)
|
||||
* could execute hlua_httpclient_gc() on the hlua->hc_list items
|
||||
* in parallel: Lua GC applies on the main stack, it is not limited to
|
||||
* a single coroutine stack, see Github issue #2037 for reference.
|
||||
* Remember, coroutines created using lua_newthread() are not meant to
|
||||
* be thread safe in Lua. (From lua co-author:
|
||||
* http://lua-users.org/lists/lua-l/2011-07/msg00072.html)
|
||||
*
|
||||
* This security measure is superfluous when 'lua-load-per-thread' is used
|
||||
* since in this case coroutines exclusively run on the same thread
|
||||
* (main stack is not shared between OS threads).
|
||||
*/
|
||||
while ((hlua_hc = MT_LIST_POP(&hlua->hc_list, typeof(hlua_hc), by_hlua))) {
|
||||
httpclient_stop_and_destroy(hlua_hc->hc);
|
||||
hlua_hc->hc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Used to destroy the Lua coroutine when the attached stream or task
|
||||
* is destroyed. The destroy also the memory context. The struct "lua"
|
||||
* will be freed.
|
||||
|
|
@ -2924,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++;
|
||||
}
|
||||
|
|
@ -6684,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));
|
||||
|
|
@ -7925,6 +7936,488 @@ __LJMP static int hlua_http_msg_unset_eom(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* Class HTTPClient
|
||||
*
|
||||
*
|
||||
*/
|
||||
__LJMP static struct hlua_httpclient *hlua_checkhttpclient(lua_State *L, int ud)
|
||||
{
|
||||
return MAY_LJMP(hlua_checkudata(L, ud, class_httpclient_ref));
|
||||
}
|
||||
|
||||
|
||||
/* stops the httpclient and ask it to kill itself */
|
||||
__LJMP static int hlua_httpclient_gc(lua_State *L)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
|
||||
MAY_LJMP(check_args(L, 1, "__gc"));
|
||||
|
||||
hlua_hc = MAY_LJMP(hlua_checkhttpclient(L, 1));
|
||||
|
||||
if (MT_LIST_DELETE(&hlua_hc->by_hlua)) {
|
||||
/* we won the race against hlua_httpclient_destroy_all() */
|
||||
httpclient_stop_and_destroy(hlua_hc->hc);
|
||||
hlua_hc->hc = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
__LJMP static int hlua_httpclient_new(lua_State *L)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
struct hlua *hlua;
|
||||
|
||||
/* Get hlua struct, or NULL if we execute from main lua state */
|
||||
hlua = hlua_gethlua(L);
|
||||
if (!hlua)
|
||||
return 0;
|
||||
|
||||
/* Check stack size. */
|
||||
if (!lua_checkstack(L, 3)) {
|
||||
hlua_pusherror(L, "httpclient: full stack");
|
||||
goto err;
|
||||
}
|
||||
/* Create the object: obj[0] = userdata. */
|
||||
lua_newtable(L);
|
||||
hlua_hc = MAY_LJMP(lua_newuserdata(L, sizeof(*hlua_hc)));
|
||||
lua_rawseti(L, -2, 0);
|
||||
memset(hlua_hc, 0, sizeof(*hlua_hc));
|
||||
|
||||
hlua_hc->hc = httpclient_new(hlua, 0, IST_NULL);
|
||||
if (!hlua_hc->hc)
|
||||
goto err;
|
||||
|
||||
MT_LIST_APPEND(&hlua->hc_list, &hlua_hc->by_hlua);
|
||||
|
||||
/* Pop a class stream metatable and affect it to the userdata. */
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, class_httpclient_ref);
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
|
||||
err:
|
||||
WILL_LJMP(lua_error(L));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Callback of the httpclient, this callback wakes the lua task up, once the
|
||||
* httpclient receives some data
|
||||
*
|
||||
*/
|
||||
|
||||
static void hlua_httpclient_cb(struct httpclient *hc)
|
||||
{
|
||||
struct hlua *hlua = hc->caller;
|
||||
|
||||
if (!hlua || !hlua->task)
|
||||
return;
|
||||
|
||||
task_wakeup(hlua->task, TASK_WOKEN_MSG);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill the lua stack with headers from the httpclient response
|
||||
* This works the same way as the hlua_http_get_headers() function
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_get_headers(lua_State *L, struct hlua_httpclient *hlua_hc)
|
||||
{
|
||||
struct http_hdr *hdr;
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
for (hdr = hlua_hc->hc->res.hdrs; hdr && isttest(hdr->n); hdr++) {
|
||||
struct ist n, v;
|
||||
int len;
|
||||
|
||||
n = hdr->n;
|
||||
v = hdr->v;
|
||||
|
||||
/* Check for existing entry:
|
||||
* assume that the table is on the top of the stack, and
|
||||
* push the key in the stack, the function lua_gettable()
|
||||
* perform the lookup.
|
||||
*/
|
||||
|
||||
lua_pushlstring(L, n.ptr, n.len);
|
||||
lua_gettable(L, -2);
|
||||
|
||||
switch (lua_type(L, -1)) {
|
||||
case LUA_TNIL:
|
||||
/* Table not found, create it. */
|
||||
lua_pop(L, 1); /* remove the nil value. */
|
||||
lua_pushlstring(L, n.ptr, n.len); /* push the header name as key. */
|
||||
lua_newtable(L); /* create and push empty table. */
|
||||
lua_pushlstring(L, v.ptr, v.len); /* push header value. */
|
||||
lua_rawseti(L, -2, 0); /* index header value (pop it). */
|
||||
lua_rawset(L, -3); /* index new table with header name (pop the values). */
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
/* Entry found: push the value in the table. */
|
||||
len = lua_rawlen(L, -1);
|
||||
lua_pushlstring(L, v.ptr, v.len); /* push header value. */
|
||||
lua_rawseti(L, -2, len+1); /* index header value (pop it). */
|
||||
lua_pop(L, 1); /* remove the table (it is stored in the main table). */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Other cases are errors. */
|
||||
hlua_pusherror(L, "internal error during the parsing of headers.");
|
||||
WILL_LJMP(lua_error(L));
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and return an array of http_hdr ist extracted from the <headers> lua table
|
||||
*
|
||||
* Caller must free the result
|
||||
*/
|
||||
struct http_hdr *hlua_httpclient_table_to_hdrs(lua_State *L)
|
||||
{
|
||||
struct http_hdr hdrs[global.tune.max_http_hdr];
|
||||
struct http_hdr *result = NULL;
|
||||
uint32_t hdr_num = 0;
|
||||
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
struct ist name, value;
|
||||
const char *n, *v;
|
||||
size_t nlen, vlen;
|
||||
|
||||
if (!lua_isstring(L, -2) || !lua_istable(L, -1)) {
|
||||
/* Skip element if the key is not a string or if the value is not a table */
|
||||
goto next_hdr;
|
||||
}
|
||||
|
||||
n = lua_tolstring(L, -2, &nlen);
|
||||
name = ist2(n, nlen);
|
||||
|
||||
/* Loop on header's values */
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2)) {
|
||||
if (!lua_isstring(L, -1)) {
|
||||
/* Skip the value if it is not a string */
|
||||
goto next_value;
|
||||
}
|
||||
|
||||
if (hdr_num >= global.tune.max_http_hdr) {
|
||||
lua_pop(L, 2);
|
||||
goto skip_headers;
|
||||
}
|
||||
|
||||
v = lua_tolstring(L, -1, &vlen);
|
||||
value = ist2(v, vlen);
|
||||
name = ist2(n, nlen);
|
||||
|
||||
hdrs[hdr_num].n = istdup(name);
|
||||
hdrs[hdr_num].v = istdup(value);
|
||||
|
||||
hdr_num++;
|
||||
|
||||
next_value:
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
next_hdr:
|
||||
lua_pop(L, 1);
|
||||
|
||||
}
|
||||
|
||||
if (hdr_num) {
|
||||
/* alloc and copy the headers in the httpclient struct */
|
||||
result = calloc((hdr_num + 1), sizeof(*result));
|
||||
if (!result)
|
||||
goto skip_headers;
|
||||
memcpy(result, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
|
||||
|
||||
result[hdr_num].n = IST_NULL;
|
||||
result[hdr_num].v = IST_NULL;
|
||||
}
|
||||
|
||||
skip_headers:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For each yield, checks if there is some data in the httpclient and push them
|
||||
* in the lua buffer, once the httpclient finished its job, push the result on
|
||||
* the stack
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_rcv_yield(lua_State *L, int status, lua_KContext ctx)
|
||||
{
|
||||
struct buffer *tr;
|
||||
int res;
|
||||
struct hlua *hlua = hlua_gethlua(L);
|
||||
struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
|
||||
|
||||
|
||||
tr = get_trash_chunk();
|
||||
|
||||
res = httpclient_res_xfer(hlua_hc->hc, tr);
|
||||
luaL_addlstring(&hlua_hc->b, b_orig(tr), res);
|
||||
|
||||
if (!httpclient_data(hlua_hc->hc) && httpclient_ended(hlua_hc->hc)) {
|
||||
|
||||
luaL_pushresult(&hlua_hc->b);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "status");
|
||||
lua_pushinteger(L, hlua_hc->hc->res.status);
|
||||
lua_settable(L, -3);
|
||||
|
||||
|
||||
lua_pushstring(L, "reason");
|
||||
lua_pushlstring(L, hlua_hc->hc->res.reason.ptr, hlua_hc->hc->res.reason.len);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "headers");
|
||||
hlua_httpclient_get_headers(L, hlua_hc);
|
||||
lua_settable(L, -3);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (httpclient_data(hlua_hc->hc))
|
||||
task_wakeup(hlua->task, TASK_WOKEN_MSG);
|
||||
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call this when trying to stream a body during a request
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_snd_yield(lua_State *L, int status, lua_KContext ctx)
|
||||
{
|
||||
struct hlua *hlua;
|
||||
struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
|
||||
const char *body_str = NULL;
|
||||
int ret;
|
||||
int end = 0;
|
||||
size_t buf_len;
|
||||
size_t to_send = 0;
|
||||
|
||||
hlua = hlua_gethlua(L);
|
||||
|
||||
if (!hlua || !hlua->task)
|
||||
WILL_LJMP(luaL_error(L, "The 'get' function is only allowed in "
|
||||
"'frontend', 'backend' or 'task'"));
|
||||
|
||||
ret = lua_getfield(L, -1, "body");
|
||||
if (ret != LUA_TSTRING)
|
||||
goto rcv;
|
||||
|
||||
body_str = lua_tolstring(L, -1, &buf_len);
|
||||
lua_pop(L, 1);
|
||||
|
||||
to_send = buf_len - hlua_hc->sent;
|
||||
|
||||
if ((hlua_hc->sent + to_send) >= buf_len)
|
||||
end = 1;
|
||||
|
||||
/* the end flag is always set since we are using the whole remaining size */
|
||||
hlua_hc->sent += httpclient_req_xfer(hlua_hc->hc, ist2(body_str + hlua_hc->sent, to_send), end);
|
||||
|
||||
if (buf_len > hlua_hc->sent) {
|
||||
/* still need to process the buffer */
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));
|
||||
} else {
|
||||
goto rcv;
|
||||
/* we sent the whole request buffer we can recv */
|
||||
}
|
||||
return 0;
|
||||
|
||||
rcv:
|
||||
|
||||
/* we return a "res" object */
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushstring(L, "body");
|
||||
luaL_buffinit(L, &hlua_hc->b);
|
||||
|
||||
task_wakeup(hlua->task, TASK_WOKEN_MSG);
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an HTTP request and wait for a response
|
||||
*/
|
||||
|
||||
__LJMP static int hlua_httpclient_send(lua_State *L, enum http_meth_t meth)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
struct http_hdr *hdrs = NULL;
|
||||
struct http_hdr *hdrs_i = NULL;
|
||||
struct hlua *hlua;
|
||||
const char *url_str = NULL;
|
||||
const char *body_str = NULL;
|
||||
size_t buf_len = 0;
|
||||
int ret;
|
||||
|
||||
hlua = hlua_gethlua(L);
|
||||
|
||||
if (!hlua || !hlua->task)
|
||||
WILL_LJMP(luaL_error(L, "The 'get' function is only allowed in "
|
||||
"'frontend', 'backend' or 'task'"));
|
||||
|
||||
if (lua_gettop(L) != 2 || lua_type(L, -1) != LUA_TTABLE)
|
||||
WILL_LJMP(luaL_error(L, "'get' needs a table as argument"));
|
||||
|
||||
hlua_hc = hlua_checkhttpclient(L, 1);
|
||||
|
||||
/* An HTTPclient instance must never process more that one request. So
|
||||
* at this stage, it must never have been started.
|
||||
*/
|
||||
if (httpclient_started(hlua_hc->hc))
|
||||
WILL_LJMP(luaL_error(L, "httpclient instance cannot be reused. It must process at most one request"));
|
||||
|
||||
lua_pushnil(L); /* first key */
|
||||
while (lua_next(L, 2)) {
|
||||
if (strcmp(lua_tostring(L, -2), "dst") == 0) {
|
||||
if (httpclient_set_dst(hlua_hc->hc, lua_tostring(L, -1)) < 0)
|
||||
WILL_LJMP(luaL_error(L, "Can't use the 'dst' argument"));
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "url") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TSTRING)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'url', must be a string"));
|
||||
url_str = lua_tostring(L, -1);
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "timeout") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TNUMBER)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'timeout', must be a number"));
|
||||
httpclient_set_timeout(hlua_hc->hc, lua_tointeger(L, -1));
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "headers") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TTABLE)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'headers', must be a table"));
|
||||
hdrs = hlua_httpclient_table_to_hdrs(L);
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "body") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TSTRING)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'body', must be a string"));
|
||||
body_str = lua_tolstring(L, -1, &buf_len);
|
||||
|
||||
} else {
|
||||
WILL_LJMP(luaL_error(L, "'%s' invalid parameter name", lua_tostring(L, -2)));
|
||||
}
|
||||
/* removes 'value'; keeps 'key' for next iteration */
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
if (!url_str) {
|
||||
WILL_LJMP(luaL_error(L, "'get' need a 'url' argument"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
hlua_hc->sent = 0;
|
||||
|
||||
istfree(&hlua_hc->hc->req.url);
|
||||
hlua_hc->hc->req.url = istdup(ist(url_str));
|
||||
hlua_hc->hc->req.meth = meth;
|
||||
|
||||
/* update the httpclient callbacks */
|
||||
hlua_hc->hc->ops.res_stline = hlua_httpclient_cb;
|
||||
hlua_hc->hc->ops.res_headers = hlua_httpclient_cb;
|
||||
hlua_hc->hc->ops.res_payload = hlua_httpclient_cb;
|
||||
hlua_hc->hc->ops.res_end = hlua_httpclient_cb;
|
||||
|
||||
/* a body is available, it will use the request callback */
|
||||
if (body_str && buf_len) {
|
||||
hlua_hc->hc->ops.req_payload = hlua_httpclient_cb;
|
||||
}
|
||||
|
||||
ret = httpclient_req_gen(hlua_hc->hc, hlua_hc->hc->req.url, meth, hdrs, IST_NULL);
|
||||
|
||||
/* free the temporary headers array */
|
||||
hdrs_i = hdrs;
|
||||
while (hdrs_i && isttest(hdrs_i->n)) {
|
||||
istfree(&hdrs_i->n);
|
||||
istfree(&hdrs_i->v);
|
||||
hdrs_i++;
|
||||
}
|
||||
ha_free(&hdrs);
|
||||
|
||||
|
||||
if (ret != ERR_NONE) {
|
||||
WILL_LJMP(luaL_error(L, "Can't generate the HTTP request"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!httpclient_start(hlua_hc->hc))
|
||||
WILL_LJMP(luaL_error(L, "couldn't start the httpclient"));
|
||||
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends an HTTP HEAD request and wait for a response
|
||||
*
|
||||
* httpclient:head(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_head(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_HEAD);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an HTTP GET request and wait for a response
|
||||
*
|
||||
* httpclient:get(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_get(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_GET);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends an HTTP PUT request and wait for a response
|
||||
*
|
||||
* httpclient:put(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_put(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_PUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an HTTP POST request and wait for a response
|
||||
*
|
||||
* httpclient:post(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_post(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_POST);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Sends an HTTP DELETE request and wait for a response
|
||||
*
|
||||
* httpclient:delete(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_delete(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_DELETE);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
|
|
@ -9556,11 +10049,6 @@ __LJMP static void hlua_event_hdl_cb_push_args(struct hlua_event_sub *hlua_sub,
|
|||
lua_settable(hlua->T, -3);
|
||||
}
|
||||
}
|
||||
#if defined(HAVE_ACME)
|
||||
else if (event_hdl_sub_family_equal(EVENT_HDL_SUB_ACME, event)) {
|
||||
MAY_LJMP(acme_hlua_event_push_args(hlua, event, data));
|
||||
}
|
||||
#endif /* HAVE_ACME */
|
||||
/* sub mgmt */
|
||||
hlua->nargs += 1;
|
||||
hlua_fcn_new_event_sub(hlua->T, hlua_sub->sub);
|
||||
|
|
@ -12890,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;
|
||||
}
|
||||
|
|
@ -13000,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);
|
||||
|
|
@ -13020,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));
|
||||
|
|
@ -13110,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;
|
||||
}
|
||||
|
|
@ -13520,7 +13992,6 @@ int hlua_post_init()
|
|||
|
||||
hlua_body = 0;
|
||||
|
||||
|
||||
#if defined(USE_OPENSSL)
|
||||
/* Initialize SSL server. */
|
||||
if (socket_ssl->xprt->prepare_srv) {
|
||||
|
|
@ -13831,6 +14302,7 @@ lua_State *hlua_init_state(int thread_num)
|
|||
hlua_class_function(L, "get_patref", hlua_get_patref);
|
||||
hlua_class_function(L, "get_var", hlua_core_get_var);
|
||||
hlua_class_function(L, "tcp", hlua_socket_new);
|
||||
hlua_class_function(L, "httpclient", hlua_httpclient_new);
|
||||
hlua_class_function(L, "event_sub", hlua_event_global_sub);
|
||||
hlua_class_function(L, "log", hlua_log);
|
||||
hlua_class_function(L, "Debug", hlua_log_debug);
|
||||
|
|
@ -14152,6 +14624,30 @@ lua_State *hlua_init_state(int thread_num)
|
|||
/* Register previous table in the registry with reference and named entry. */
|
||||
class_http_msg_ref = hlua_register_metatable(L, CLASS_HTTP_MSG);
|
||||
|
||||
/*
|
||||
*
|
||||
* Register class HTTPClient
|
||||
*
|
||||
*/
|
||||
|
||||
/* Create and fill the metatable. */
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, "__index");
|
||||
lua_newtable(L);
|
||||
hlua_class_function(L, "get", hlua_httpclient_get);
|
||||
hlua_class_function(L, "head", hlua_httpclient_head);
|
||||
hlua_class_function(L, "put", hlua_httpclient_put);
|
||||
hlua_class_function(L, "post", hlua_httpclient_post);
|
||||
hlua_class_function(L, "delete", hlua_httpclient_delete);
|
||||
lua_settable(L, -3); /* Sets the __index entry. */
|
||||
/* Register the garbage collector entry. */
|
||||
lua_pushstring(L, "__gc");
|
||||
lua_pushcclosure(L, hlua_httpclient_gc, 0);
|
||||
lua_settable(L, -3); /* Push the last 2 entries in the table at index -3 */
|
||||
|
||||
|
||||
|
||||
class_httpclient_ref = hlua_register_metatable(L, CLASS_HTTPCLIENT);
|
||||
/*
|
||||
*
|
||||
* Register class AppletTCP
|
||||
|
|
@ -14303,28 +14799,6 @@ lua_State *hlua_init_state(int thread_num)
|
|||
/* Register previous table in the registry with reference and named entry. */
|
||||
class_socket_ref = hlua_register_metatable(L, CLASS_SOCKET);
|
||||
|
||||
/* Call all registered state init callbacks. */
|
||||
{
|
||||
struct hlua_state_init_fct *e;
|
||||
char *errmsg = NULL;
|
||||
int err_code;
|
||||
|
||||
list_for_each_entry(e, &hlua_state_init_list, list) {
|
||||
err_code = e->fct(L, &errmsg);
|
||||
if (errmsg) {
|
||||
if (err_code & ERR_ALERT)
|
||||
ha_alert("Lua: %s\n", errmsg);
|
||||
else if (err_code & ERR_WARN)
|
||||
ha_warning("Lua: %s\n", errmsg);
|
||||
else
|
||||
ha_notice("Lua: %s\n", errmsg);
|
||||
ha_free(&errmsg);
|
||||
}
|
||||
if (err_code & (ERR_ABORT|ERR_FATAL))
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
lua_atpanic(L, hlua_panic_safe);
|
||||
|
||||
return L;
|
||||
|
|
@ -14345,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]);
|
||||
|
|
@ -14413,16 +14884,10 @@ static void hlua_deinit()
|
|||
{
|
||||
int thr;
|
||||
struct hlua_reg_filter *reg_flt, *reg_flt_bck;
|
||||
struct hlua_state_init_fct *e, *eb;
|
||||
|
||||
list_for_each_entry_safe(reg_flt, reg_flt_bck, &referenced_filters, l)
|
||||
release_hlua_reg_filter(reg_flt);
|
||||
|
||||
list_for_each_entry_safe(e, eb, &hlua_state_init_list, list) {
|
||||
LIST_DELETE(&e->list);
|
||||
free(e);
|
||||
}
|
||||
|
||||
for (thr = 0; thr < MAX_THREADS+1; thr++) {
|
||||
if (hlua_states[thr])
|
||||
lua_close(hlua_states[thr]);
|
||||
|
|
@ -14442,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);
|
||||
|
|
|
|||
|
|
@ -351,8 +351,6 @@ int hpack_dht_insert(struct hpack_dht *dht, struct ist name, struct ist value)
|
|||
else {
|
||||
/* need to defragment the table before inserting upfront */
|
||||
dht = hpack_dht_defrag(dht);
|
||||
if (!dht)
|
||||
return -1;
|
||||
wrap = dht->wrap + 1;
|
||||
head = dht->head + 1;
|
||||
dht->dte[head].addr = dht->dte[dht->front].addr - (name.len + value.len);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue