mirror of
https://github.com/haproxy/haproxy.git
synced 2026-06-16 13:00:38 -04:00
Compare commits
244 commits
v3.4-dev13
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ad74d634a | ||
|
|
5985276735 | ||
|
|
43932db851 | ||
|
|
7266ed8c30 | ||
|
|
9a6d1fe3f0 | ||
|
|
8e1c51378e | ||
|
|
33c765fc59 | ||
|
|
08aa12392d | ||
|
|
82a16a2927 | ||
|
|
292b07270c | ||
|
|
7bfa568e27 | ||
|
|
c64e242b1c | ||
|
|
5530f50e4b | ||
|
|
f371bfd608 | ||
|
|
0748799ad1 | ||
|
|
339d25636d | ||
|
|
974560128d | ||
|
|
2ff861747c | ||
|
|
98b1fd4ff9 | ||
|
|
b9aa1c0e64 | ||
|
|
aaee6c463c | ||
|
|
caa1cd0674 | ||
|
|
0988b9c773 | ||
|
|
c9f3ddcb1e | ||
|
|
95cb3251a0 | ||
|
|
74b16c5477 | ||
|
|
8b6d8f5e4f | ||
|
|
91f9e3a3dd | ||
|
|
92206fb02f | ||
|
|
d2c9bf70e5 | ||
|
|
4bb21dae2f | ||
|
|
81d7624e01 | ||
|
|
960fa1c921 | ||
|
|
7d63efa5f5 | ||
|
|
96b08e959c | ||
|
|
784f972a6f | ||
|
|
5c0733db9a | ||
|
|
d0fde90e16 | ||
|
|
3dfd86062b | ||
|
|
cc01214a67 | ||
|
|
00c081b5f3 | ||
|
|
53fe4181a5 | ||
|
|
9e60d35aaf | ||
|
|
0a90ff6b3d | ||
|
|
6c75202b48 | ||
|
|
fb38e40ad5 | ||
|
|
9bc37232f4 | ||
|
|
3c923d075c | ||
|
|
6f8dab2583 | ||
|
|
a4520229a7 | ||
|
|
3fa818c78f | ||
|
|
a7888f0373 | ||
|
|
ef191c46d7 | ||
|
|
b9fa07bd20 | ||
|
|
172306c308 | ||
|
|
e51ae5ce66 | ||
|
|
45a64123d6 | ||
|
|
ac776e3819 | ||
|
|
1e00743520 | ||
|
|
b227ad2dc7 | ||
|
|
07deafa104 | ||
|
|
a39b1a40ad | ||
|
|
83ae0c250c | ||
|
|
f7bc8246ee | ||
|
|
3daf4498f3 | ||
|
|
4b9c8b24c5 | ||
|
|
6a7b27a0a4 | ||
|
|
7835e1fcbe | ||
|
|
02f0101cde | ||
|
|
64a335366d | ||
|
|
1cf7dc07e9 | ||
|
|
667645ed2b | ||
|
|
a7c64a5b12 | ||
|
|
b794190262 | ||
|
|
bf4878226e | ||
|
|
8b71e1f155 | ||
|
|
91aa9b88c9 | ||
|
|
d0ab99932a | ||
|
|
1b4255a885 | ||
|
|
326618b9a9 | ||
|
|
e1b5f3bbc3 | ||
|
|
d17fb63ce7 | ||
|
|
564b9d06c0 | ||
|
|
24ea0e013d | ||
|
|
777ea8b185 | ||
|
|
c24db7c76a | ||
|
|
9993688954 | ||
|
|
cb161bfeb7 | ||
|
|
1c9e4b0d18 | ||
|
|
7ac4bcfbd4 | ||
|
|
83634a4c9a | ||
|
|
ca5f6cd053 | ||
|
|
c82ac139f4 | ||
|
|
837d69f8ef | ||
|
|
bfbca23dc2 | ||
|
|
24455aa4e0 | ||
|
|
c0aa9f01f1 | ||
|
|
4a540a4fb7 | ||
|
|
b8543c54d4 | ||
|
|
8497107132 | ||
|
|
030a2bfeeb | ||
|
|
f8fd6d25d8 | ||
|
|
2199053018 | ||
|
|
de25313cd8 | ||
|
|
f1aac4a3b2 | ||
|
|
3a5189faba | ||
|
|
c989d9da6d | ||
|
|
bda42604c0 | ||
|
|
41a20c1738 | ||
|
|
03b828b648 | ||
|
|
8badf5d2fa | ||
|
|
551e01e3e7 | ||
|
|
0bf22b86d0 | ||
|
|
c1d6973571 | ||
|
|
a93b407811 | ||
|
|
9393ff4f71 | ||
|
|
30811a3bac | ||
|
|
839b87ac9f | ||
|
|
2d91a846f9 | ||
|
|
004ad29bb2 | ||
|
|
d796a31945 | ||
|
|
45f14ba836 | ||
|
|
4185bc2cb8 | ||
|
|
dad00a7442 | ||
|
|
f91b1ce9af | ||
|
|
f5847d11f7 | ||
|
|
617df441d6 | ||
|
|
40508247c6 | ||
|
|
5e5b1522cf | ||
|
|
54633f078c | ||
|
|
6599dd7d41 | ||
|
|
45ad1037d0 | ||
|
|
ab8603c6d5 | ||
|
|
4e0af590e8 | ||
|
|
52ce316786 | ||
|
|
1cf1a0c8b1 | ||
|
|
c76e0f1bc4 | ||
|
|
802a3b7288 | ||
|
|
fb828a4711 | ||
|
|
b21e130ea5 | ||
|
|
026a038bbd | ||
|
|
6ebf0d4c95 | ||
|
|
fbd7148b15 | ||
|
|
2130c9ccfb | ||
|
|
1c59c39171 | ||
|
|
9a39e55ded | ||
|
|
1974240520 | ||
|
|
7ad81403d0 | ||
|
|
8874f06b9e | ||
|
|
629fbee3be | ||
|
|
e2d2f67666 | ||
|
|
0e83b7cd08 | ||
|
|
2f20eb5bd8 | ||
|
|
40313cd0d5 | ||
|
|
091768ab3e | ||
|
|
cd652efeca | ||
|
|
205312023a | ||
|
|
8a8898aedd | ||
|
|
1589621100 | ||
|
|
8d771110e0 | ||
|
|
85a833feba | ||
|
|
31cd3d13aa | ||
|
|
88da61e218 | ||
|
|
16446de17c | ||
|
|
010ab9798e | ||
|
|
3ffbf5539e | ||
|
|
3843f48faf | ||
|
|
b238c08015 | ||
|
|
547c2e4e78 | ||
|
|
3ac082b2b2 | ||
|
|
1a5a33396d | ||
|
|
4e7518ed21 | ||
|
|
ce9371a768 | ||
|
|
2c0e633f6b | ||
|
|
b463072032 | ||
|
|
f7130c0f36 | ||
|
|
dfb6daca1f | ||
|
|
e4a5a64198 | ||
|
|
4a8bb2fe5f | ||
|
|
d8460a5339 | ||
|
|
8e77620616 | ||
|
|
433cce7af1 | ||
|
|
4a9ec66fd8 | ||
|
|
73b5f0eed4 | ||
|
|
7ac4d7d69f | ||
|
|
85003563c5 | ||
|
|
f932863484 | ||
|
|
26c3b3f41d | ||
|
|
9b6389c8a0 | ||
|
|
93f9ecbfe6 | ||
|
|
2a47cab7f3 | ||
|
|
c41c731f5e | ||
|
|
2653936510 | ||
|
|
997c99df9c | ||
|
|
076655e18d | ||
|
|
8cb0a0c53d | ||
|
|
e583b38c63 | ||
|
|
ffdc91c4a1 | ||
|
|
4f58fef3d4 | ||
|
|
73472025f2 | ||
|
|
5cb932826d | ||
|
|
8bdcc55163 | ||
|
|
b9aaf3c18a | ||
|
|
635652c5aa | ||
|
|
04811943b5 | ||
|
|
e8c9aabd62 | ||
|
|
32fc35ef09 | ||
|
|
6bb8cb51e6 | ||
|
|
8fe8d5fbe3 | ||
|
|
b78b023d55 | ||
|
|
7d182a2ed5 | ||
|
|
c0e302fe79 | ||
|
|
478e7e52cb | ||
|
|
8e1d33a648 | ||
|
|
49d6306de3 | ||
|
|
01ebb668a4 | ||
|
|
340cc86efb | ||
|
|
f62d020140 | ||
|
|
bbef74fb21 | ||
|
|
608951844e | ||
|
|
f9088a5d75 | ||
|
|
007d5946b4 | ||
|
|
4db85fc53e | ||
|
|
41bb1c24f6 | ||
|
|
9091cfa617 | ||
|
|
57b526e022 | ||
|
|
2644f9ddf9 | ||
|
|
7cab3a3c3a | ||
|
|
04b9215a2e | ||
|
|
75f72c2eb9 | ||
|
|
1ed4ef6659 | ||
|
|
3fab21ea42 | ||
|
|
f9d4d659a4 | ||
|
|
c0aa91a202 | ||
|
|
e2c3cd9eb7 | ||
|
|
6717531053 | ||
|
|
812962d110 | ||
|
|
8fe8f78473 | ||
|
|
cdeb2aa4ef | ||
|
|
9e6e0fd149 | ||
|
|
e98595e4e5 | ||
|
|
413f6f9a1f | ||
|
|
3475a5bb9f | ||
|
|
050e06dd66 |
153 changed files with 4210 additions and 1603 deletions
18
.cirrus.yml
18
.cirrus.yml
|
|
@ -1,18 +0,0 @@
|
||||||
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
Normal file
38
.github/workflows/freebsd.yml
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
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:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 25 * *"
|
- cron: "0 3 * * 1"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|
|
||||||
180
CHANGELOG
180
CHANGELOG
|
|
@ -1,6 +1,186 @@
|
||||||
ChangeLog :
|
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
|
2026/05/20 : 3.4-dev13
|
||||||
- BUG/MINOR: backend: correct parameter value validation in get_server_ph_post()
|
- BUG/MINOR: backend: correct parameter value validation in get_server_ph_post()
|
||||||
- BUG/MINOR: config/dns: properly fail on duplicate nameserver name detection
|
- 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
|
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
|
for your system, you want to be certain to have all the fixes or to get some
|
||||||
commercial support, other choices are available at http://www.haproxy.com/.
|
commercial support, other choices are available at https://www.haproxy.com/.
|
||||||
|
|
||||||
|
|
||||||
Areas covered in this document
|
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.
|
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.
|
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
|
Some systems provide this library under various names to avoid conflicts with
|
||||||
previous versions. By default, HAProxy looks for "lua5.4", "lua54", "lua5.3",
|
previous versions. By default, HAProxy looks for "lua5.5", "lua55", "lua5.4",
|
||||||
"lua53", "lua". If your system uses a different naming, you may need to set the
|
"lua54", "lua5.3", "lua53", "lua". If your system uses a different naming, you
|
||||||
library name in the "LUA_LIB_NAME" variable.
|
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
|
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 :
|
can be downloaded from https://www.lua.org/, extracted and built, for example :
|
||||||
|
|
|
||||||
75
Makefile
75
Makefile
|
|
@ -61,7 +61,6 @@
|
||||||
# USE_OBSOLETE_LINKER : use when the linker fails to emit __start_init/__stop_init
|
# 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_THREAD_DUMP : use the more advanced thread state dump system. Automatic.
|
||||||
# USE_OT : enable the OpenTracing filter
|
# 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_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only.
|
||||||
# USE_LIBATOMIC : force to link with/without libatomic. Automatic.
|
# USE_LIBATOMIC : force to link with/without libatomic. Automatic.
|
||||||
# USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours
|
# USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours
|
||||||
|
|
@ -95,6 +94,7 @@
|
||||||
# SILENT_DEFINE may be used to specify other defines which will not be
|
# SILENT_DEFINE may be used to specify other defines which will not be
|
||||||
# reported by "haproxy -vv".
|
# reported by "haproxy -vv".
|
||||||
# EXTRA is used to force building or not building some extra tools.
|
# 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.
|
# 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
|
# It might be useful to set DESTDIR if you want to install haproxy
|
||||||
# in a sandbox.
|
# in a sandbox.
|
||||||
|
|
@ -124,7 +124,7 @@
|
||||||
# LUA_LIB : force the lib path to lua
|
# LUA_LIB : force the lib path to lua
|
||||||
# LUA_INC : force the include path to lua
|
# LUA_INC : force the include path to lua
|
||||||
# LUA_LIB_NAME : force the lib name (or automatically evaluated, by order of
|
# LUA_LIB_NAME : force the lib name (or automatically evaluated, by order of
|
||||||
# priority : lua5.4, lua54, lua5.3, lua53, lua).
|
# priority: lua5.5, lua55, lua5.4, lua54, lua5.3, lua53, lua).
|
||||||
# OT_DEBUG : compile the OpenTracing filter in debug mode
|
# OT_DEBUG : compile the OpenTracing filter in debug mode
|
||||||
# OT_INC : force the include path to libopentracing-c-wrapper
|
# OT_INC : force the include path to libopentracing-c-wrapper
|
||||||
# OT_LIB : force the lib 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
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_LUA:0=),)
|
ifneq ($(USE_LUA:0=),)
|
||||||
check_lua_inc = $(shell if [ -d $(2)$(1) ]; then echo $(2)$(1); fi;)
|
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.4 lua54 lua5.3 lua53 lua,$(call check_lua_inc,$(lib),"/usr/include/")))
|
LUA_INC := $(firstword $(foreach lib,lua5.5 lua55 lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_inc,$(lib),"/usr/include/")))
|
||||||
|
|
||||||
check_lua_lib = $(shell echo "int main(){}" | $(CC) -o /dev/null -x c - $(2) -l$(1) 2>/dev/null && echo $(1))
|
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))
|
||||||
LUA_LD_FLAGS := -Wl,$(if $(EXPORT_SYMBOL),$(EXPORT_SYMBOL),--export-dynamic) $(if $(LUA_LIB),-L$(LUA_LIB))
|
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
|
# Try to automatically detect the Lua library if not set
|
||||||
ifeq ($(LUA_LIB_NAME),)
|
ifeq ($(LUA_LIB_NAME),)
|
||||||
LUA_LIB_NAME := $(firstword $(foreach lib,lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_lib,$(lib),$(LUA_LD_FLAGS))))
|
LUA_LIB_NAME := $(firstword $(foreach lib,lua lua5.5 lua55 lua5.4 lua54 lua5.3 lua53,$(call check_lua_lib,$(lib),$(LUA_LD_FLAGS))))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Lua lib name must be set now (forced/detected above)
|
# Lua lib name must be set now (forced/detected above)
|
||||||
|
|
@ -722,70 +722,15 @@ ifneq ($(USE_PROMEX:0=),)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_DEVICEATLAS:0=),)
|
ifneq ($(USE_DEVICEATLAS:0=),)
|
||||||
# Use DEVICEATLAS_SRC and possibly DEVICEATLAS_INC and DEVICEATLAS_LIB to force path
|
EXTRA_MAKE += addons/deviceatlas
|
||||||
# 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
|
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=),)
|
ifneq ($(USE_51DEGREES:0=),)
|
||||||
ifeq ($(51DEGREES_VER),4) # v4 here
|
EXTRA_MAKE += addons/51degrees
|
||||||
_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
|
endif # USE_51DEGREES
|
||||||
|
|
||||||
ifneq ($(USE_WURFL:0=),)
|
ifneq ($(USE_WURFL:0=),)
|
||||||
# Use WURFL_SRC and possibly WURFL_INC and WURFL_LIB to force path
|
EXTRA_MAKE += addons/wurfl
|
||||||
# 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
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_PCRE:0=)$(USE_STATIC_PCRE:0=)$(USE_PCRE_JIT:0=),)
|
ifneq ($(USE_PCRE:0=)$(USE_STATIC_PCRE:0=)$(USE_PCRE_JIT:0=),)
|
||||||
|
|
@ -1058,7 +1003,7 @@ IGNORE_OPTS=help install install-man install-doc install-bin \
|
||||||
|
|
||||||
ifneq ($(TARGET),)
|
ifneq ($(TARGET),)
|
||||||
ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),)
|
ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),)
|
||||||
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 = $(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: $(build_opts)
|
.build_opts: $(build_opts)
|
||||||
else
|
else
|
||||||
.build_opts:
|
.build_opts:
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
[](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml)
|
[](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/netbsd.yml)
|
||||||
[](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml)
|
[](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml)
|
||||||
[](https://cirrus-ci.com/github/haproxy/haproxy/)
|
[](https://github.com/haproxy/haproxy/actions/workflows/freebsd.yml)
|
||||||
[](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml)
|
[](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml)
|
||||||
|
|
||||||

|

|
||||||
|
|
|
||||||
2
VERDATE
2
VERDATE
|
|
@ -1,2 +1,2 @@
|
||||||
$Format:%ci$
|
$Format:%ci$
|
||||||
2026/05/20
|
2026/06/03
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
3.4-dev13
|
3.5-dev0
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,16 @@ static int _51d_property_name_list(char **args, int section_type, struct proxy *
|
||||||
|
|
||||||
while (*(args[cur_arg])) {
|
while (*(args[cur_arg])) {
|
||||||
name = calloc(1, sizeof(*name));
|
name = calloc(1, sizeof(*name));
|
||||||
|
if (!name) {
|
||||||
|
memprintf(err, "'%s' failed to allocate memory.", args[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
name->name = strdup(args[cur_arg]);
|
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);
|
LIST_APPEND(&global_51degrees.property_names, &name->list);
|
||||||
++cur_arg;
|
++cur_arg;
|
||||||
}
|
}
|
||||||
|
|
@ -303,6 +312,7 @@ static void _51d_init_device_offsets(fiftyoneDegreesDeviceOffsets *offsets) {
|
||||||
|
|
||||||
static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets)
|
static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets)
|
||||||
{
|
{
|
||||||
|
struct buffer *temp = get_trash_chunk();
|
||||||
struct channel *chn;
|
struct channel *chn;
|
||||||
struct htx *htx;
|
struct htx *htx;
|
||||||
struct http_hdr_ctx ctx;
|
struct http_hdr_ctx ctx;
|
||||||
|
|
@ -324,7 +334,15 @@ static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOff
|
||||||
|
|
||||||
if (http_find_header(htx, name, &ctx, 1)) {
|
if (http_find_header(htx, name, &ctx, 1)) {
|
||||||
(offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i);
|
(offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i);
|
||||||
(offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, ctx.value.ptr);
|
/* 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->size++;
|
offsets->size++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -919,6 +937,10 @@ static int init_51degrees(void)
|
||||||
list_for_each_entry(name, &global_51degrees.property_names, list)
|
list_for_each_entry(name, &global_51degrees.property_names, list)
|
||||||
++i;
|
++i;
|
||||||
_51d_property_list = calloc(i, sizeof(*_51d_property_list));
|
_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;
|
i = 0;
|
||||||
list_for_each_entry(name, &global_51degrees.property_names, list)
|
list_for_each_entry(name, &global_51degrees.property_names, list)
|
||||||
|
|
@ -1053,7 +1075,7 @@ static int init_51degrees(void)
|
||||||
|
|
||||||
static void deinit_51degrees(void)
|
static void deinit_51degrees(void)
|
||||||
{
|
{
|
||||||
struct _51d_property_names *_51d_prop_name, *_51d_prop_nameb;
|
struct _51d_property_names *_51d_prop_name = NULL, *_51d_prop_nameb = NULL;
|
||||||
|
|
||||||
#if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED)
|
#if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED)
|
||||||
free(global_51degrees.header_names);
|
free(global_51degrees.header_names);
|
||||||
|
|
|
||||||
37
addons/51degrees/Makefile.mk
Normal file
37
addons/51degrees/Makefile.mk
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# 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,6 +1,13 @@
|
||||||
# DEVICEATLAS_SRC : DeviceAtlas API source root path
|
# 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++
|
CXX := c++
|
||||||
CXXLIB := -lstdc++
|
CXXLIB := -lstdc++
|
||||||
|
|
||||||
|
|
@ -28,5 +35,7 @@ OPTIONS_OBJS += $(DEVICEATLAS_SRC)/dadwcurl.o
|
||||||
OPTIONS_OBJS += $(DEVICEATLAS_SRC)/Os/daunix.o
|
OPTIONS_OBJS += $(DEVICEATLAS_SRC)/Os/daunix.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
OPTIONS_OBJS += addons/deviceatlas/da.o
|
||||||
|
|
||||||
addons/deviceatlas/dummy/%.o: addons/deviceatlas/dummy/%.cpp
|
addons/deviceatlas/dummy/%.o: addons/deviceatlas/dummy/%.cpp
|
||||||
$(cmd_CXX) $(CXXFLAGS) -c -o $@ $<
|
$(cmd_CXX) $(CXXFLAGS) -c -o $@ $<
|
||||||
13
addons/wurfl/Makefile.mk
Normal file
13
addons/wurfl/Makefile.mk
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# 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,6 +1801,8 @@ void filter_count_ip(const char *source_field, const char *accept_field, const c
|
||||||
*/
|
*/
|
||||||
if (unlikely(!ustat))
|
if (unlikely(!ustat))
|
||||||
ustat = calloc(1, sizeof(*ustat));
|
ustat = calloc(1, sizeof(*ustat));
|
||||||
|
if (!ustat)
|
||||||
|
return;
|
||||||
|
|
||||||
ustat->nb_err = err;
|
ustat->nb_err = err;
|
||||||
ustat->nb_req = 1;
|
ustat->nb_req = 1;
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ end
|
||||||
# returns $node filled with the first node of ebroot $arg0
|
# returns $node filled with the first node of ebroot $arg0
|
||||||
define ebtree_first
|
define ebtree_first
|
||||||
# browse ebtree left until encountering leaf
|
# browse ebtree left until encountering leaf
|
||||||
set $node = (struct eb_node *)$arg0->b[0]
|
set $node = (struct eb_node *)((struct eb_root*)$arg0)->b[0]
|
||||||
while 1
|
while 1
|
||||||
_ebtree_set_tag_node $node
|
_ebtree_set_tag_node $node
|
||||||
if $tag == 0
|
if $tag == 0
|
||||||
|
|
@ -41,7 +41,7 @@ end
|
||||||
# finds next ebtree node after $arg0, and returns it in $node
|
# finds next ebtree node after $arg0, and returns it in $node
|
||||||
define ebtree_next
|
define ebtree_next
|
||||||
# get parent
|
# get parent
|
||||||
set $node = (struct eb_root *)$arg0->leaf_p
|
set $node = (struct eb_root *)((struct eb_node *)$arg0)->leaf_p
|
||||||
# Walking up from right branch, so we cannot be below root
|
# Walking up from right branch, so we cannot be below root
|
||||||
# while (eb_gettag(t) != EB_LEFT) // #define EB_LEFT 0
|
# while (eb_gettag(t) != EB_LEFT) // #define EB_LEFT 0
|
||||||
while 1
|
while 1
|
||||||
|
|
|
||||||
39
dev/gdb/fd.gdb
Normal file
39
dev/gdb/fd.gdb
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# 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
|
||||||
31
dev/gdb/task.gdb
Normal file
31
dev/gdb/task.gdb
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
10
dev/gdb/thread.gdb
Normal file
10
dev/gdb/thread.gdb
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# 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
|
||||||
70
dev/patchbot/prompts/prompt15-3.5-mist7bv2-pfx.txt
Normal file
70
dev/patchbot/prompts/prompt15-3.5-mist7bv2-pfx.txt
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
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.
|
||||||
|
|
||||||
29
dev/patchbot/prompts/prompt15-3.5-mist7bv2-sfx.txt
Normal file
29
dev/patchbot/prompts/prompt15-3.5-mist7bv2-sfx.txt
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
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
|
HAProxy
|
||||||
Configuration Manual
|
Configuration Manual
|
||||||
----------------------
|
----------------------
|
||||||
version 3.4
|
version 3.5
|
||||||
2026/05/20
|
2026/06/03
|
||||||
|
|
||||||
|
|
||||||
This document covers the configuration language as implemented in the version
|
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",
|
the configuration file sees the new value. See also "presetenv", "resetenv",
|
||||||
and "unsetenv".
|
and "unsetenv".
|
||||||
|
|
||||||
shm-stats-file <name> [ EXPERIMENTAL ]
|
shm-stats-file <name>
|
||||||
When this directive is set, it enables the use of shared memory for storing
|
When this directive is set, it enables the use of shared memory for storing
|
||||||
stats counters. <name> is used as argument to shm_open() to open the shared
|
stats counters. <name> is used as argument to shm_open() to open the shared
|
||||||
memory at a unique location. It also means that the directive is only
|
memory at a unique location. It also means that the directive is only
|
||||||
|
|
@ -3345,7 +3345,7 @@ shm-stats-file <name> [ EXPERIMENTAL ]
|
||||||
|
|
||||||
See also "guid", "guid-prefix" and "shm-stats-file-max-objects"
|
See also "guid", "guid-prefix" and "shm-stats-file-max-objects"
|
||||||
|
|
||||||
shm-stats-file-max-objects <number> [ EXPERIMENTAL ]
|
shm-stats-file-max-objects <number>
|
||||||
This setting defines the maximum number of objects the shared memory used
|
This setting defines the maximum number of objects the shared memory used
|
||||||
for shared counters will be able to store per thread group. It is directly
|
for shared counters will be able to store per thread group. It is directly
|
||||||
related to the maximum memory size of the shm and is used to "premap" the
|
related to the maximum memory size of the shm and is used to "premap" the
|
||||||
|
|
@ -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 string,math,table,utf8 # safe subset, no I/O or OS
|
||||||
tune.lua.openlibs all # default, load everything
|
tune.lua.openlibs all # default, load everything
|
||||||
|
|
||||||
This setting must be set before any "lua-load" or "lua-load-per-thread"
|
This setting must be set before any "lua-load", "lua-load-per-thread" or
|
||||||
directive, otherwise a parse error is returned.
|
"lua-prepend-path" directive, otherwise a parse error is returned.
|
||||||
|
|
||||||
tune.lua.service-timeout <timeout>
|
tune.lua.service-timeout <timeout>
|
||||||
This is the execution timeout for the Lua services. This is useful for
|
This is the execution timeout for the Lua services. This is useful for
|
||||||
|
|
@ -9105,7 +9105,9 @@ http-reuse { never | safe | aggressive | always }
|
||||||
- proxy protocol
|
- proxy protocol
|
||||||
- TOS and mark socket options
|
- TOS and mark socket options
|
||||||
- connection name, determined either by the result of the evaluation of the
|
- connection name, determined either by the result of the evaluation of the
|
||||||
"pool-conn-name" expression if present, otherwise by the "sni" expression
|
"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.
|
||||||
|
|
||||||
In some occasions, connection lookup or reuse is not performed due to extra
|
In some occasions, connection lookup or reuse is not performed due to extra
|
||||||
restrictions. This is determined by the reuse strategy specified via the
|
restrictions. This is determined by the reuse strategy specified via the
|
||||||
|
|
@ -9179,6 +9181,26 @@ http-reuse { never | safe | aggressive | always }
|
||||||
too few connections are kept open. It may be desirable in this case to adjust
|
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.
|
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
|
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
|
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
|
it may happen that unfair load between groups leads to more idle connections
|
||||||
|
|
@ -9962,7 +9984,7 @@ no option accept-unsafe-violations-in-http-request
|
||||||
When this option is set, the following rules are observed:
|
When this option is set, the following rules are observed:
|
||||||
|
|
||||||
* In H1 only, invalid characters, including NULL character, in header name
|
* In H1 only, invalid characters, including NULL character, in header name
|
||||||
will be accepted;
|
will not be rejected; however the header will be dropped.
|
||||||
|
|
||||||
* In H1 only, NULL character in header value will be accepted;
|
* In H1 only, NULL character in header value will be accepted;
|
||||||
|
|
||||||
|
|
@ -10027,8 +10049,11 @@ no option accept-unsafe-violations-in-http-response
|
||||||
|
|
||||||
When this option is set, the following rules are observed:
|
When this option is set, the following rules are observed:
|
||||||
|
|
||||||
|
* In H1 only, status codes longer than 3 digits but whose value fits in 16
|
||||||
|
bits are not rejected.
|
||||||
|
|
||||||
* In H1 only, invalid characters, including NULL character, in header name
|
* In H1 only, invalid characters, including NULL character, in header name
|
||||||
will be accepted;
|
will not be rejected; however the header will be dropped.
|
||||||
|
|
||||||
* In H1 only, NULL character in header value will be accepted;
|
* In H1 only, NULL character in header value will be accepted;
|
||||||
|
|
||||||
|
|
@ -19896,11 +19921,13 @@ sni-auto
|
||||||
May be used in the following contexts: tcp, http, log, peers, ring
|
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
|
The "sni-auto" parameter enables the automatic SNI selection, if no value was
|
||||||
already set. It is enabled by default but this parameter may be used as
|
already set. It sets the "sni" expression to "req.hdr(host),field(1,:)",
|
||||||
"server" setting to reset any "no-sni-auto" setting which would have been
|
which means that an SNI will be presented with the Host name of the request
|
||||||
inherited from "default-server" directive as default value. It may also be
|
that is being sent to the server, but dropping the port number. It is enabled
|
||||||
used as "default-server" setting to reset any previous "default-server"
|
by default but this parameter may be used as "server" setting to reset any
|
||||||
"no-sni-auto" setting.
|
"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
|
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
|
value, if found. Otherwise it remains unset. For other protocols, the option
|
||||||
|
|
@ -32610,9 +32637,9 @@ https://github.com/haproxy/wiki/wiki/ACME:--native-haproxy
|
||||||
Current limitations:
|
Current limitations:
|
||||||
- The feature is limited to the http-01, dns-01 or dns-persist-01 challenges
|
- 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
|
for now. http-01 is completely handled by HAProxy, but dns-01 and
|
||||||
dns-persist-01 needs either the dataplaneAPI or another 3rd party
|
dns-persist-01 needs either the dataplaneAPI, a lua script using event_hdl or
|
||||||
tool to talk to a DNS provider API. dns-persist-01 only needs the TXT entry
|
another 3rd party tool to talk to a DNS provider API. dns-persist-01 only
|
||||||
to be set once, so it could be set manually without a tool.
|
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
|
- It is possible to start without an existing certificate on the disk. To do
|
||||||
so, the certificate must configured in a crt-store.
|
so, the certificate must configured in a crt-store.
|
||||||
When using the "acme" keyword in a crt-store, a temporary key pair will be
|
When using the "acme" keyword in a crt-store, a temporary key pair will be
|
||||||
|
|
@ -32683,6 +32710,8 @@ challenge-ready <value>[,<value>]*
|
||||||
"acme challenge_ready <crt> domain <domain>" on the master CLI or
|
"acme challenge_ready <crt> domain <domain>" on the master CLI or
|
||||||
the stats socket. This allows an external DNS provisioning tool to
|
the stats socket. This allows an external DNS provisioning tool to
|
||||||
confirm that the TXT record has been set before HAProxy proceeds.
|
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
|
dns - perform a DNS pre-check by resolving the TXT record for
|
||||||
"_acme-challenge.<domain>" using the configured "default" resolvers
|
"_acme-challenge.<domain>" using the configured "default" resolvers
|
||||||
|
|
|
||||||
|
|
@ -526,12 +526,6 @@ In addition, some variables are related to the global runqueue:
|
||||||
unsigned int grq_total; /* total number of entries in the global run queue, atomic */
|
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 */
|
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
|
2022-06-14 - progress on task affinity
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,16 @@ 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
|
cast and freed. The const char* is here to leave more freedom to use consts
|
||||||
when making such options lists.
|
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)())
|
- void hap_register_per_thread_alloc(int (*fct)())
|
||||||
|
|
||||||
This adds a call to function <fct> to the list of functions to be called when
|
This adds a call to function <fct> to the list of functions to be called when
|
||||||
|
|
@ -324,6 +334,18 @@ alphanumerically ordered:
|
||||||
is that it allows to register multiple <post> callbacks and to register them
|
is that it allows to register multiple <post> callbacks and to register them
|
||||||
elsewhere in the code.
|
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)
|
- REGISTER_PER_THREAD_ALLOC(fct)
|
||||||
|
|
||||||
Registers a call to register_per_thread_alloc(fct) at stage STG_REGISTER.
|
Registers a call to register_per_thread_alloc(fct) at stage STG_REGISTER.
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,11 @@ HAPROXY CORE PRINCIPLES
|
||||||
- Aliases: uchar (unsigned char), uint (unsigned int), ulong (unsigned long),
|
- Aliases: uchar (unsigned char), uint (unsigned int), ulong (unsigned long),
|
||||||
ushort (unsigned short), ullong (unsigned long long), llong (long long),
|
ushort (unsigned short), ullong (unsigned long long), llong (long long),
|
||||||
schar (signed char).
|
schar (signed char).
|
||||||
- size_t always same size as long but often declared as uint on 32-bit and
|
- size_t is always the same size as long, but its underlying type is often
|
||||||
ulong on 64-bit. Do not use in printf() without a cast (ulong with "%lu").
|
uint on 32-bit and ulong on 64-bit. This is a frequent source of build
|
||||||
|
errors on 32-bit platforms (e.g. passing a size_t where a long* is
|
||||||
|
expected, or printing one with "%lu"); always cast in printf() (ulong
|
||||||
|
with "%lu").
|
||||||
- Main platforms are x86_64 and aarch64 with high thread counts (>=64).
|
- Main platforms are x86_64 and aarch64 with high thread counts (>=64).
|
||||||
- Unaligned accesses are permitted for archs that support them; portable
|
- Unaligned accesses are permitted for archs that support them; portable
|
||||||
wrappers in net_helper.h (read_u32(), write_u32() etc).
|
wrappers in net_helper.h (read_u32(), write_u32() etc).
|
||||||
|
|
@ -36,7 +39,8 @@ HAPROXY CORE PRINCIPLES
|
||||||
3. MEMORY MANAGEMENT AND POOLS
|
3. MEMORY MANAGEMENT AND POOLS
|
||||||
- Pools are used for runtime allocation; malloc/free are for boot code only.
|
- 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() semantics match malloc(); the return must always be tested.
|
||||||
- pool_alloc() and malloc() are not interchangeable / compatible.
|
- pool_alloc() and malloc() are not interchangeable: memory obtained from one
|
||||||
|
must not be released using the other's free function.
|
||||||
- pool_free() semantics match free(); it is a no-op on NULL.
|
- pool_free() semantics match free(); it is a no-op on NULL.
|
||||||
- pool_free() makes the pointer invalid immediately; it must not be touched
|
- pool_free() makes the pointer invalid immediately; it must not be touched
|
||||||
or passed to pool_free() again.
|
or passed to pool_free() again.
|
||||||
|
|
@ -57,8 +61,8 @@ HAPROXY CORE PRINCIPLES
|
||||||
- idempotent functions b_alloc() and b_free() use pools to manage the
|
- idempotent functions b_alloc() and b_free() use pools to manage the
|
||||||
storage area and check <size> to know if alloc/free still needed.
|
storage area and check <size> to know if alloc/free still needed.
|
||||||
- a non-contiguous version exists (ncbuf, ncbmbuf), allowing holes anywhere
|
- a non-contiguous version exists (ncbuf, ncbmbuf), allowing holes anywhere
|
||||||
in data. The former mandates holes of at least 8 bytes. The second relies
|
in data. ncbuf mandates holes of at least 8 bytes, while ncbmbuf relies on
|
||||||
on a bitmap of populated places.
|
a bitmap of populated places.
|
||||||
- another string API exists, "ist", representing a pointer and a length in a
|
- 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
|
struct that is returned by inline functions and macros. It is described in
|
||||||
doc/internals/api/ist.txt
|
doc/internals/api/ist.txt
|
||||||
|
|
@ -73,8 +77,11 @@ HAPROXY CORE PRINCIPLES
|
||||||
- Chunks are used for linear operations like chunk_printf().
|
- Chunks are used for linear operations like chunk_printf().
|
||||||
- Trash is a thread-local temporary buffer; scope stays within the caller.
|
- Trash is a thread-local temporary buffer; scope stays within the caller.
|
||||||
- trash always the same size as a buffer (global.tune.bufsize).
|
- trash always the same size as a buffer (global.tune.bufsize).
|
||||||
- get_trash_chunk() provides up to 3 rotating thread-local trash chunks (with
|
- get_trash_chunk() provides rotating thread-local trash chunks. Since almost
|
||||||
a scope spanning from the call to the next function call).
|
any function may itself call get_trash_chunk(), a returned chunk is only
|
||||||
|
guaranteed valid until the next call into another function and must not be
|
||||||
|
held across such a call. The rotation lets a single function safely use up
|
||||||
|
to 3 distinct chunks at once for its own data manipulation.
|
||||||
- For longer lived trash chunks, alloc_trash_chunk() is available but must be
|
- For longer lived trash chunks, alloc_trash_chunk() is available but must be
|
||||||
released using free_trash_chunk() on leaving.
|
released using free_trash_chunk() on leaving.
|
||||||
- standard doubly-linked lists (struct list) are provided via macros LIST_*.
|
- standard doubly-linked lists (struct list) are provided via macros LIST_*.
|
||||||
|
|
@ -103,8 +110,9 @@ HAPROXY CORE PRINCIPLES
|
||||||
"ti" current thread info.
|
"ti" current thread info.
|
||||||
- "tgid" always current tg number, "tg_ctx" current tg context.
|
- "tgid" always current tg number, "tg_ctx" current tg context.
|
||||||
- HA_ATOMIC_* for atomic operations on integers and pointers (includes load
|
- HA_ATOMIC_* for atomic operations on integers and pointers (includes load
|
||||||
and store). DWCAS available on some platforms but requires an equivalent
|
and store). DWCAS is available on some platforms but requires an equivalent
|
||||||
for other ones.
|
fallback on the others (possibly a more complex operation, e.g. emulation
|
||||||
|
using two or more CAS).
|
||||||
- The _HA_ATOMIC_* version (leading underscore) do not use barriers so these
|
- The _HA_ATOMIC_* version (leading underscore) do not use barriers so these
|
||||||
must be explicit (__ha_barrier_*).
|
must be explicit (__ha_barrier_*).
|
||||||
- Atomic loops must use CPU relaxation or exponential back-off.
|
- Atomic loops must use CPU relaxation or exponential back-off.
|
||||||
|
|
@ -178,8 +186,9 @@ HAPROXY CORE PRINCIPLES
|
||||||
- Avoid static storage when persistence is not needed.
|
- Avoid static storage when persistence is not needed.
|
||||||
- Macros in uppercase unless they're used to wrap functions which then get a
|
- Macros in uppercase unless they're used to wrap functions which then get a
|
||||||
leading underscore.
|
leading underscore.
|
||||||
- Explicitly compare functions returning non-zero with 0 (e.g. strcmp) unless
|
- Explicitly compare against 0 the return of functions that yield an integer
|
||||||
they explicitly return a boolean (e.g. isalnum) or a pointer (e.g. strchr).
|
which is not a boolean (e.g. strcmp), unless they return a boolean (e.g.
|
||||||
|
isalnum) or a pointer (e.g. strchr).
|
||||||
- Unsigned int comparisons to zero never use >0 but !=0 to avoid signedness
|
- Unsigned int comparisons to zero never use >0 but !=0 to avoid signedness
|
||||||
mistakes.
|
mistakes.
|
||||||
- turn non-zero integer to boolean using "!" or "!!".
|
- turn non-zero integer to boolean using "!" or "!!".
|
||||||
|
|
|
||||||
233
doc/internals/threat-model.txt
Normal file
233
doc/internals/threat-model.txt
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
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
|
HAProxy Starter Guide
|
||||||
-----------------------
|
-----------------------
|
||||||
version 3.4
|
version 3.5
|
||||||
|
|
||||||
|
|
||||||
This document is an introduction to HAProxy for all those who don't know it, as
|
This document is an introduction to HAProxy for all those who don't know it, as
|
||||||
|
|
@ -97,6 +97,9 @@ to the mailing list whose responses are present in these documents.
|
||||||
protocol which is implemented by HAProxy and a number of third party
|
protocol which is implemented by HAProxy and a number of third party
|
||||||
products.
|
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
|
- README : how to build HAProxy from sources
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1686,15 +1689,7 @@ 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
|
a very long thread, please avoid pasting very long dumps (a few hundreds lines
|
||||||
or more) and attach them instead.
|
or more) and attach them instead.
|
||||||
|
|
||||||
If you've found what you're absolutely certain can be considered a critical
|
If you believe you may have found a security issue, please refer to the file
|
||||||
security issue that would put many users in serious trouble if discussed in a
|
doc/security.txt. It explains what does and does not qualify as a vulnerability
|
||||||
public place, then you can send it with the reproducer to security@haproxy.org.
|
in HAProxy, and how to report a genuine one privately. Most suspected issues
|
||||||
A small team of trusted developers will receive it and will be able to propose
|
turn out to be ordinary bugs that are better reported as described above.
|
||||||
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,9 +893,7 @@ Core class
|
||||||
|
|
||||||
**context**: init, task, action
|
**context**: init, task, action
|
||||||
|
|
||||||
This function returns a new object of a *httpclient* class. An *httpclient*
|
This function returns a new object of a *httpclient* class.
|
||||||
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.
|
:returns: A :ref:`httpclient_class` object.
|
||||||
|
|
||||||
|
|
@ -1031,6 +1029,12 @@ Core class
|
||||||
Be careful when subscribing to this type since many events might be
|
Be careful when subscribing to this type since many events might be
|
||||||
generated.
|
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::
|
.. Note::
|
||||||
Use **SERVER** in **event_types** to subscribe to all server events types
|
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
|
at once. Note that this should only be used for testing purposes since a
|
||||||
|
|
@ -1048,7 +1052,8 @@ Core class
|
||||||
* **event** (*string*): the event type (one of the **event_types** specified
|
* **event** (*string*): the event type (one of the **event_types** specified
|
||||||
when subscribing)
|
when subscribing)
|
||||||
* **event_data**: specific to each event family (For **SERVER** family,
|
* **event_data**: specific to each event family (For **SERVER** family,
|
||||||
a :ref:`server_event_class` object)
|
a :ref:`server_event_class` object; for **ACME** family,
|
||||||
|
a :ref:`acme_event_class` object)
|
||||||
* **sub**: class to manage the subscription from within the event
|
* **sub**: class to manage the subscription from within the event
|
||||||
(a :ref:`event_sub_class` object)
|
(a :ref:`event_sub_class` object)
|
||||||
* **when**: timestamp corresponding to the date when the event was generated.
|
* **when**: timestamp corresponding to the date when the event was generated.
|
||||||
|
|
@ -2580,10 +2585,7 @@ HTTPClient class
|
||||||
.. js:class:: HTTPClient
|
.. js:class:: HTTPClient
|
||||||
|
|
||||||
The httpclient class allows issue of outbound HTTP requests through a simple
|
The httpclient class allows issue of outbound HTTP requests through a simple
|
||||||
API without the knowledge of HAProxy internals. Any instance must be used to
|
API without the knowledge of HAProxy internals.
|
||||||
process one and only one request. It must never be reused to process several
|
|
||||||
requests.
|
|
||||||
|
|
||||||
.. js:function:: HTTPClient.get(httpclient, request)
|
.. js:function:: HTTPClient.get(httpclient, request)
|
||||||
.. js:function:: HTTPClient.head(httpclient, request)
|
.. js:function:: HTTPClient.head(httpclient, request)
|
||||||
.. js:function:: HTTPClient.put(httpclient, request)
|
.. js:function:: HTTPClient.put(httpclient, request)
|
||||||
|
|
@ -2612,7 +2614,8 @@ HTTPClient class
|
||||||
haproxy address format.
|
haproxy address format.
|
||||||
:param integer request.timeout: Optional timeout parameter, set a
|
:param integer request.timeout: Optional timeout parameter, set a
|
||||||
"timeout server" on the connections.
|
"timeout server" on the connections.
|
||||||
:returns: Lua table containing the response
|
: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.
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: lua
|
.. code-block:: lua
|
||||||
|
|
@ -4739,6 +4742,75 @@ CertCache class
|
||||||
CertCache.set{filename="certs/localhost9994.pem.rsa", crt=crt}
|
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
|
External Lua libraries
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
------------------------
|
------------------------
|
||||||
HAProxy Management Guide
|
HAProxy Management Guide
|
||||||
------------------------
|
------------------------
|
||||||
version 3.4
|
version 3.5
|
||||||
|
|
||||||
|
|
||||||
This document describes how to start, stop, manage, and troubleshoot HAProxy,
|
This document describes how to start, stop, manage, and troubleshoot HAProxy,
|
||||||
|
|
@ -215,6 +215,18 @@ list of options is :
|
||||||
in foreground and to show incoming and outgoing events. It must never be
|
in foreground and to show incoming and outgoing events. It must never be
|
||||||
used in an init script.
|
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
|
-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
|
tokenized, so comments are stripped and indenting is forced. If a non-zero
|
||||||
key is specified, lines are truncated before sensitive/confidential fields,
|
key is specified, lines are truncated before sensitive/confidential fields,
|
||||||
|
|
@ -2457,7 +2469,8 @@ httpclient [--htx] <method> <URI>
|
||||||
name in the URL using the "default" resolvers section, which is populated
|
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
|
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
|
able to resolve an host from /etc/hosts if you don't use a local dns daemon
|
||||||
which can resolve those.
|
which can resolve those. If an internal error occurs (e.g. connection failure,
|
||||||
|
timeout, etc.), the status code will be set to 0.
|
||||||
|
|
||||||
The --htx option allow to use the haproxy internal htx representation using
|
The --htx option allow to use the haproxy internal htx representation using
|
||||||
the htx_dump() function, mainly used for debugging.
|
the htx_dump() function, mainly used for debugging.
|
||||||
|
|
|
||||||
40
doc/security.txt
Normal file
40
doc/security.txt
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
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
|
||||||
162
examples/lua/acme-gandi-livedns.lua
Normal file
162
examples/lua/acme-gandi-livedns.lua
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
-- 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,6 +135,29 @@ struct acme_ctx {
|
||||||
#define ACME_VERB_ADVANCED 4
|
#define ACME_VERB_ADVANCED 4
|
||||||
#define ACME_VERB_COMPLETE 5
|
#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 /* ! HAVE_ACME */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,14 @@
|
||||||
#include <haproxy/ssl_ckch-t.h>
|
#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 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();
|
EVP_PKEY *acme_gen_tmp_pkey();
|
||||||
X509 *acme_gen_tmp_x509();
|
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
|
#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)
|
static inline int channel_may_send(const struct channel *chn)
|
||||||
{
|
{
|
||||||
return chn_cons(chn)->state == SC_ST_EST;
|
return chn_cons(chn)->state >= SC_ST_REQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HTX version of channel_may_recv(). Returns non-zero if the channel can still
|
/* HTX version of channel_may_recv(). Returns non-zero if the channel can still
|
||||||
|
|
|
||||||
|
|
@ -165,9 +165,7 @@ static forceinline struct buffer *alloc_small_trash_chunk(void)
|
||||||
*/
|
*/
|
||||||
static forceinline struct buffer *alloc_trash_chunk_sz(size_t size)
|
static forceinline struct buffer *alloc_trash_chunk_sz(size_t size)
|
||||||
{
|
{
|
||||||
if (pool_head_small_trash && size <= pool_head_small_trash->size)
|
if (size <= pool_head_trash->size)
|
||||||
return alloc_small_trash_chunk();
|
|
||||||
else if (size <= pool_head_trash->size)
|
|
||||||
return alloc_trash_chunk();
|
return alloc_trash_chunk();
|
||||||
else if (pool_head_large_trash && size <= pool_head_large_trash->size)
|
else if (pool_head_large_trash && size <= pool_head_large_trash->size)
|
||||||
return alloc_large_trash_chunk();
|
return alloc_large_trash_chunk();
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,8 @@ enum {
|
||||||
/* below we have all handshake flags grouped into one */
|
/* below we have all handshake flags grouped into one */
|
||||||
CO_FL_HANDSHAKE = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
|
CO_FL_HANDSHAKE = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
|
||||||
CO_FL_WAIT_XPRT = CO_FL_WAIT_L4_CONN | CO_FL_HANDSHAKE | CO_FL_WAIT_L6_CONN,
|
CO_FL_WAIT_XPRT = CO_FL_WAIT_L4_CONN | CO_FL_HANDSHAKE | CO_FL_WAIT_L6_CONN,
|
||||||
|
/* handshake running on top of a layer6 */
|
||||||
|
CO_FL_WAIT_XPRT_L6 = CO_FL_QMUX_SEND | CO_FL_QMUX_RECV,
|
||||||
|
|
||||||
CO_FL_SSL_WAIT_HS = 0x08000000, /* wait for an SSL handshake to complete */
|
CO_FL_SSL_WAIT_HS = 0x08000000, /* wait for an SSL handshake to complete */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ int conn_reverse(struct connection *conn);
|
||||||
const char *conn_err_code_name(struct connection *c);
|
const char *conn_err_code_name(struct connection *c);
|
||||||
const char *conn_err_code_str(struct connection *c);
|
const char *conn_err_code_str(struct connection *c);
|
||||||
int xprt_add_hs(struct connection *conn);
|
int xprt_add_hs(struct connection *conn);
|
||||||
|
int xprt_add_l6hs(struct connection *conn, int xprt);
|
||||||
void register_mux_proto(struct mux_proto_list *list);
|
void register_mux_proto(struct mux_proto_list *list);
|
||||||
|
|
||||||
static inline void conn_report_term_evt(struct connection *conn, enum term_event_loc loc, unsigned char type);
|
static inline void conn_report_term_evt(struct connection *conn, enum term_event_loc loc, unsigned char type);
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,8 @@ void ha_warning(const char *fmt, ...)
|
||||||
* These functions are reserved to output diagnostics on MODE_DIAG.
|
* These functions are reserved to output diagnostics on MODE_DIAG.
|
||||||
* Use the underscore variants only if MODE_DIAG has already been checked.
|
* 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_vdiag_warning(const char *fmt, va_list argp);
|
||||||
void _ha_diag_warning(const char *fmt, ...);
|
void _ha_diag_warning(const char *fmt, ...);
|
||||||
void ha_diag_warning(const char *fmt, ...)
|
void ha_diag_warning(const char *fmt, ...)
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,16 @@ struct event_hdl_sub {
|
||||||
#define EVENT_HDL_SUB_PAT_REF_COMMIT EVENT_HDL_SUB_TYPE(2,4)
|
#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)
|
#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
|
/* Please reflect changes above in event_hdl_sub_type_map defined
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ extern struct cfgfile fileless_cfg;
|
||||||
/* storage for collected libs */
|
/* storage for collected libs */
|
||||||
extern void *lib_storage;
|
extern void *lib_storage;
|
||||||
extern size_t lib_size;
|
extern size_t lib_size;
|
||||||
|
extern char *lib_output_file;
|
||||||
|
|
||||||
struct proxy;
|
struct proxy;
|
||||||
struct server;
|
struct server;
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ enum h1m_state {
|
||||||
#define H1_MF_UPG_WEBSOCKET 0x00008000 // Set for a Websocket upgrade handshake
|
#define H1_MF_UPG_WEBSOCKET 0x00008000 // Set for a Websocket upgrade handshake
|
||||||
#define H1_MF_TE_CHUNKED 0x00010000 // T-E "chunked"
|
#define H1_MF_TE_CHUNKED 0x00010000 // T-E "chunked"
|
||||||
#define H1_MF_TE_OTHER 0x00020000 // T-E other than supported ones found (only "chunked" is supported for now)
|
#define H1_MF_TE_OTHER 0x00020000 // T-E other than supported ones found (only "chunked" is supported for now)
|
||||||
/* unused: 0x00040000 */
|
#define H1_MF_UPG_HDR 0x00040000 // non-empty Upgrapde header found
|
||||||
#define H1_MF_NOT_HTTP 0x00080000 // Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted)
|
#define H1_MF_NOT_HTTP 0x00080000 // Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted)
|
||||||
/* Mask to use to reset H1M flags when we restart headers parsing.
|
/* Mask to use to reset H1M flags when we restart headers parsing.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
#include <haproxy/buf-t.h>
|
#include <haproxy/buf-t.h>
|
||||||
#include <haproxy/mux_quic-t.h>
|
#include <haproxy/mux_quic-t.h>
|
||||||
|
|
||||||
/* H3 unidirecational stream types
|
/* H3 unidirectional stream types
|
||||||
* Emitted as the first byte on the stream to differentiate it.
|
* Emitted as the first byte on the stream to differentiate it.
|
||||||
*/
|
*/
|
||||||
#define H3_UNI_S_T_CTRL 0x00
|
#define H3_UNI_S_T_CTRL 0x00
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
#define CLASS_HTTP "HTTP"
|
#define CLASS_HTTP "HTTP"
|
||||||
#define CLASS_HTTP_MSG "HTTPMessage"
|
#define CLASS_HTTP_MSG "HTTPMessage"
|
||||||
#define CLASS_HTTPCLIENT "HTTPClient"
|
#define CLASS_HTTPCLIENT "HTTPClient"
|
||||||
|
#define CLASS_HTTPCLIENT_REQ "HTTPClientRequest"
|
||||||
#define CLASS_MAP "Map"
|
#define CLASS_MAP "Map"
|
||||||
#define CLASS_APPLET_TCP "AppletTCP"
|
#define CLASS_APPLET_TCP "AppletTCP"
|
||||||
#define CLASS_APPLET_HTTP "AppletHTTP"
|
#define CLASS_APPLET_HTTP "AppletHTTP"
|
||||||
|
|
@ -259,6 +260,11 @@ struct hlua_patref_iterator_context {
|
||||||
struct pat_ref_gen *gen; /* the generation we are iterating over */
|
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 */
|
#else /* USE_LUA */
|
||||||
/************************ For use when Lua is disabled ********************/
|
/************************ For use when Lua is disabled ********************/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,17 @@
|
||||||
|
|
||||||
#ifdef USE_LUA
|
#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. */
|
/* The following macros are used to set flags. */
|
||||||
#define HLUA_SET_RUN(__hlua) do {(__hlua)->flags |= HLUA_RUN;} while(0)
|
#define HLUA_SET_RUN(__hlua) do {(__hlua)->flags |= HLUA_RUN;} while(0)
|
||||||
#define HLUA_CLR_RUN(__hlua) do {(__hlua)->flags &= ~HLUA_RUN;} while(0)
|
#define HLUA_CLR_RUN(__hlua) do {(__hlua)->flags &= ~HLUA_RUN;} while(0)
|
||||||
|
|
@ -51,6 +62,11 @@
|
||||||
|
|
||||||
/* Lua HAProxy integration functions. */
|
/* Lua HAProxy integration functions. */
|
||||||
void hlua_yield_asap(lua_State *L);
|
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);
|
const char *hlua_traceback(lua_State *L, const char* sep);
|
||||||
void hlua_ctx_destroy(struct hlua *lua);
|
void hlua_ctx_destroy(struct hlua *lua);
|
||||||
void hlua_init();
|
void hlua_init();
|
||||||
|
|
@ -65,6 +81,15 @@ void hlua_pushref(lua_State *L, int ref);
|
||||||
void hlua_unref(lua_State *L, int ref);
|
void hlua_unref(lua_State *L, int ref);
|
||||||
struct hlua *hlua_gethlua(lua_State *L);
|
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);
|
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 */
|
#else /* USE_LUA */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -326,6 +326,50 @@ static inline int is_immutable_header(struct ist hdr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function parses comma-separated values from <hv> and rewrite it in place,
|
||||||
|
* skip all occurrences of <value>. It is the caller responsibility to deal with
|
||||||
|
* empty header value.
|
||||||
|
*/
|
||||||
|
static inline void http_remove_header_value(struct ist *hv, struct ist value)
|
||||||
|
{
|
||||||
|
char *e, *n, *p;
|
||||||
|
struct ist word;
|
||||||
|
|
||||||
|
word.ptr = hv->ptr - 1; // -1 for next loop's pre-increment
|
||||||
|
p = hv->ptr;
|
||||||
|
e = hv->ptr + hv->len;
|
||||||
|
hv->len = 0;
|
||||||
|
|
||||||
|
while (++word.ptr < e) {
|
||||||
|
/* skip leading delimiter and blanks */
|
||||||
|
if (HTTP_IS_LWS(*word.ptr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
n = http_find_hdr_value_end(word.ptr, e); // next comma or end of line
|
||||||
|
word.len = n - word.ptr;
|
||||||
|
|
||||||
|
/* trim trailing blanks */
|
||||||
|
while (word.len && HTTP_IS_LWS(word.ptr[word.len-1]))
|
||||||
|
word.len--;
|
||||||
|
|
||||||
|
if (isteqi(word, value))
|
||||||
|
goto skip_val;
|
||||||
|
|
||||||
|
if (hv->ptr + hv->len == p) {
|
||||||
|
/* no rewrite done till now */
|
||||||
|
hv->len = n - hv->ptr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (hv->len)
|
||||||
|
hv->ptr[hv->len++] = ',';
|
||||||
|
istcat(hv, word, e - hv->ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_val:
|
||||||
|
word.ptr = p = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _HAPROXY_HTTP_H */
|
#endif /* _HAPROXY_HTTP_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,9 @@ static inline int httpclient_started(struct httpclient *hc)
|
||||||
return !!(hc->flags & HTTPCLIENT_FS_STARTED);
|
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 */
|
#endif /* !_HAPROXY_HTTPCLIENT_H */
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,7 @@
|
||||||
#define HTX_SL_F_CONN_UPG 0x00001000 /* The message contains "connection: upgrade" header */
|
#define HTX_SL_F_CONN_UPG 0x00001000 /* The message contains "connection: upgrade" header */
|
||||||
#define HTX_SL_F_BODYLESS_RESP 0x00002000 /* The response to this message is bodyless (only for request) */
|
#define HTX_SL_F_BODYLESS_RESP 0x00002000 /* The response to this message is bodyless (only for request) */
|
||||||
#define HTX_SL_F_NOT_HTTP 0x00004000 /* Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted) */
|
#define HTX_SL_F_NOT_HTTP 0x00004000 /* Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted) */
|
||||||
|
#define HTX_SL_F_UPG_HDR 0x00008000 /* non-empty Upgrapde header found */
|
||||||
|
|
||||||
/* This function is used to report flags in debugging tools. Please reflect
|
/* This function is used to report flags in debugging tools. Please reflect
|
||||||
* below any single-bit flag addition above in the same order via the
|
* below any single-bit flag addition above in the same order via the
|
||||||
|
|
@ -157,7 +158,8 @@ static forceinline char *hsl_show_flags(char *buf, size_t len, const char *delim
|
||||||
_(HTX_SL_F_CLEN, _(HTX_SL_F_CHNK, _(HTX_SL_F_VER_11,
|
_(HTX_SL_F_CLEN, _(HTX_SL_F_CHNK, _(HTX_SL_F_VER_11,
|
||||||
_(HTX_SL_F_BODYLESS, _(HTX_SL_F_HAS_SCHM, _(HTX_SL_F_SCHM_HTTP,
|
_(HTX_SL_F_BODYLESS, _(HTX_SL_F_HAS_SCHM, _(HTX_SL_F_SCHM_HTTP,
|
||||||
_(HTX_SL_F_SCHM_HTTPS, _(HTX_SL_F_HAS_AUTHORITY,
|
_(HTX_SL_F_SCHM_HTTPS, _(HTX_SL_F_HAS_AUTHORITY,
|
||||||
_(HTX_SL_F_NORMALIZED_URI, _(HTX_SL_F_CONN_UPG)))))))))))));
|
_(HTX_SL_F_NORMALIZED_URI, _(HTX_SL_F_CONN_UPG, _(HTX_SL_F_BODYLESS_RESP,
|
||||||
|
_(HTX_SL_F_NOT_HTTP, _(HTX_SL_F_UPG_HDR))))))))))))))));
|
||||||
/* epilogue */
|
/* epilogue */
|
||||||
_(~0U);
|
_(~0U);
|
||||||
return buf;
|
return buf;
|
||||||
|
|
|
||||||
|
|
@ -121,8 +121,8 @@ static inline size_t array_size_or_fail(size_t m, size_t n)
|
||||||
{
|
{
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
||||||
if (mulsz_overflow(m, n, &size))
|
if (unlikely(mulsz_overflow(m, n, &size)))
|
||||||
return ~(size_t)0;
|
return DISGUISE(~(size_t)0);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -286,9 +286,11 @@ static forceinline char *qcc_show_flags(char *buf, size_t len, const char *delim
|
||||||
/* flags */
|
/* flags */
|
||||||
_(QC_CF_ERRL,
|
_(QC_CF_ERRL,
|
||||||
_(QC_CF_ERRL_DONE,
|
_(QC_CF_ERRL_DONE,
|
||||||
|
_(QC_CF_IS_BACK,
|
||||||
_(QC_CF_CONN_FULL,
|
_(QC_CF_CONN_FULL,
|
||||||
|
_(QC_CF_CONN_SHUT,
|
||||||
_(QC_CF_ERR_CONN,
|
_(QC_CF_ERR_CONN,
|
||||||
_(QC_CF_WAIT_HS)))));
|
_(QC_CF_WAIT_HS)))))));
|
||||||
/* epilogue */
|
/* epilogue */
|
||||||
_(~0U);
|
_(~0U);
|
||||||
return buf;
|
return buf;
|
||||||
|
|
@ -331,7 +333,8 @@ static forceinline char *qcs_show_flags(char *buf, size_t len, const char *delim
|
||||||
_(QC_SF_HREQ_RECV,
|
_(QC_SF_HREQ_RECV,
|
||||||
_(QC_SF_TO_STOP_SENDING,
|
_(QC_SF_TO_STOP_SENDING,
|
||||||
_(QC_SF_UNKNOWN_PL_LENGTH,
|
_(QC_SF_UNKNOWN_PL_LENGTH,
|
||||||
_(QC_SF_RECV_RESET))))))))))));
|
_(QC_SF_RECV_RESET,
|
||||||
|
_(QC_SF_EOI_SUSPENDED)))))))))))));
|
||||||
/* epilogue */
|
/* epilogue */
|
||||||
_(~0U);
|
_(~0U);
|
||||||
return buf;
|
return buf;
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,14 @@
|
||||||
#define QPACK_DEC_INST_SCCL 0x40 // Stream Cancellation
|
#define QPACK_DEC_INST_SCCL 0x40 // Stream Cancellation
|
||||||
#define QPACK_DEC_INST_SACK 0x80 // Section Acknowledgment
|
#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 */
|
/* RFC 9204 6. Error Handling */
|
||||||
enum qpack_err {
|
enum qpack_err {
|
||||||
QPACK_ERR_DECOMPRESSION_FAILED = 0x200,
|
QPACK_ERR_DECOMPRESSION_FAILED = 0x200,
|
||||||
|
|
|
||||||
|
|
@ -93,24 +93,30 @@ static inline struct ist qpack_get_value(const struct qpack_dht *dht, const stru
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* takes an idx, returns the associated name */
|
/* takes an absolute idx (including static table offset), returns the associated name */
|
||||||
static inline struct ist qpack_idx_to_name(const struct qpack_dht *dht, uint32_t idx)
|
static inline struct ist qpack_idx_to_name(const struct qpack_dht *dht, uint32_t idx)
|
||||||
{
|
{
|
||||||
const struct qpack_dte *dte;
|
const struct qpack_dte *dte;
|
||||||
|
|
||||||
dte = qpack_get_dte(dht, idx);
|
if (idx < QPACK_SHT_SIZE)
|
||||||
|
return ist("### ERR ###"); /* static table entries not accessible via dht */
|
||||||
|
|
||||||
|
dte = qpack_get_dte(dht, idx - QPACK_SHT_SIZE);
|
||||||
if (!dte)
|
if (!dte)
|
||||||
return ist("### ERR ###"); // error
|
return ist("### ERR ###"); // error
|
||||||
|
|
||||||
return qpack_get_name(dht, dte);
|
return qpack_get_name(dht, dte);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* takes an idx, returns the associated value */
|
/* takes an absolute idx (including static table offset), returns the associated value */
|
||||||
static inline struct ist qpack_idx_to_value(const struct qpack_dht *dht, uint32_t idx)
|
static inline struct ist qpack_idx_to_value(const struct qpack_dht *dht, uint32_t idx)
|
||||||
{
|
{
|
||||||
const struct qpack_dte *dte;
|
const struct qpack_dte *dte;
|
||||||
|
|
||||||
dte = qpack_get_dte(dht, idx);
|
if (idx < QPACK_SHT_SIZE)
|
||||||
|
return ist("### ERR ###"); /* static table entries not accessible via dht */
|
||||||
|
|
||||||
|
dte = qpack_get_dte(dht, idx - QPACK_SHT_SIZE);
|
||||||
if (!dte)
|
if (!dte)
|
||||||
return ist("### ERR ###"); // error
|
return ist("### ERR ###"); // error
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,8 @@ static inline void in46un_to_addr(const union sockaddr_in46 *src,
|
||||||
in6->sin6_family = AF_INET6;
|
in6->sin6_family = AF_INET6;
|
||||||
in6->sin6_addr = src->in6.sin6_addr;
|
in6->sin6_addr = src->in6.sin6_addr;
|
||||||
in6->sin6_port = src->in6.sin6_port;
|
in6->sin6_port = src->in6.sin6_port;
|
||||||
|
in6->sin6_flowinfo = src->in6.sin6_flowinfo;
|
||||||
|
in6->sin6_scope_id = src->in6.sin6_scope_id;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* include/haproxy/dns-t.h
|
* include/haproxy/resolvers-t.h
|
||||||
* This file provides structures and types for DNS.
|
* This file provides structures and types for DNS.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
|
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
|
||||||
|
|
@ -114,7 +114,7 @@ struct resolv_answer_item {
|
||||||
char name[DNS_MAX_NAME_SIZE+1]; /* answer name */
|
char name[DNS_MAX_NAME_SIZE+1]; /* answer name */
|
||||||
int16_t type; /* question type */
|
int16_t type; /* question type */
|
||||||
int16_t class; /* query class */
|
int16_t class; /* query class */
|
||||||
int32_t ttl; /* response TTL */
|
uint32_t ttl; /* response TTL */
|
||||||
int16_t priority; /* SRV type priority */
|
int16_t priority; /* SRV type priority */
|
||||||
uint16_t weight; /* SRV type weight */
|
uint16_t weight; /* SRV type weight */
|
||||||
uint16_t port; /* SRV type port */
|
uint16_t port; /* SRV type port */
|
||||||
|
|
@ -281,7 +281,7 @@ enum {
|
||||||
* matching preference was found.
|
* matching preference was found.
|
||||||
*/
|
*/
|
||||||
RSLV_UPD_SRVIP_NOT_FOUND, /* provided IP not found
|
RSLV_UPD_SRVIP_NOT_FOUND, /* provided IP not found
|
||||||
* OR provided IP found and preference is not match and an IP
|
* OR provided IP found and preference is not matched and an IP
|
||||||
* matching preference was found.
|
* matching preference was found.
|
||||||
*/
|
*/
|
||||||
RSLV_UPD_NO_IP_FOUND, /* no IP could be found in the response */
|
RSLV_UPD_NO_IP_FOUND, /* no IP could be found in the response */
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* include/haproxy/dns.h
|
* include/haproxy/resolvers.h
|
||||||
* This file provides functions related to DNS protocol
|
* This file provides functions related to DNS protocol
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
|
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
|
||||||
|
|
|
||||||
|
|
@ -279,6 +279,8 @@ struct srv_per_thread {
|
||||||
struct ceb_root *idle_conns; /* Shareable idle connections */
|
struct ceb_root *idle_conns; /* Shareable idle connections */
|
||||||
struct ceb_root *safe_conns; /* Safe 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 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
|
#ifdef USE_QUIC
|
||||||
struct ist quic_retry_token;
|
struct ist quic_retry_token;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -401,7 +403,6 @@ struct server {
|
||||||
* thread, and generally at the same time.
|
* thread, and generally at the same time.
|
||||||
*/
|
*/
|
||||||
THREAD_ALIGN();
|
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_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_idle_nb; /* Current number of connections in the idle list */
|
||||||
unsigned int curr_safe_nb; /* Current number of connections in the safe list */
|
unsigned int curr_safe_nb; /* Current number of connections in the safe list */
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,9 @@
|
||||||
#include <haproxy/tools.h>
|
#include <haproxy/tools.h>
|
||||||
|
|
||||||
|
|
||||||
__decl_thread(extern HA_SPINLOCK_T idle_conn_srv_lock);
|
|
||||||
extern struct idle_conns idle_conns[MAX_THREADS];
|
extern struct idle_conns idle_conns[MAX_THREADS];
|
||||||
extern struct task *idle_conn_task;
|
extern struct task *idle_conn_task[MAX_THREADS];
|
||||||
|
extern struct eb_root idle_conn_srv[MAX_THREADS];
|
||||||
extern struct mt_list servers_list;
|
extern struct mt_list servers_list;
|
||||||
extern struct dict server_key_dict;
|
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_store; /* Number of ckch_store that reference this certificate_ocsp */
|
||||||
int refcount; /* Number of actual references to this certificate_ocsp (SSL_CTXs mostly) */
|
int refcount; /* Number of actual references to this certificate_ocsp (SSL_CTXs mostly) */
|
||||||
struct buffer response;
|
struct buffer response;
|
||||||
long expire;
|
unsigned long expire;
|
||||||
X509 *issuer;
|
X509 *issuer;
|
||||||
STACK_OF(X509) *chain;
|
STACK_OF(X509) *chain;
|
||||||
struct eb64_node next_update; /* Key of items inserted in ocsp_update_tree (sorted by absolute date) */
|
struct eb64_node next_update; /* Key of items inserted in ocsp_update_tree (sorted by absolute date) */
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,7 @@ struct ssl_keylog {
|
||||||
#define SSL_SOCK_F_KTLS_RECV (1 << 3) /* kTLS receive is configure on that socket */
|
#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_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_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 ssl_sock_ctx {
|
||||||
struct connection *conn;
|
struct connection *conn;
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
/* use this to check a task state or to clean it up before queueing */
|
/* 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| \
|
#define TASK_WOKEN_ANY (TASK_WOKEN_OTHER|TASK_WOKEN_INIT|TASK_WOKEN_TIMER| \
|
||||||
TASK_WOKEN_IO|TASK_WOKEN_SIGNAL|TASK_WOKEN_MSG| \
|
TASK_WOKEN_IO|TASK_WOKEN_SIGNAL|TASK_WOKEN_MSG| \
|
||||||
TASK_WOKEN_RES)
|
TASK_WOKEN_RES|TASK_WOKEN_WQ)
|
||||||
|
|
||||||
#define TASK_F_TASKLET 0x00008000 /* nature of this task: 0=task 1=tasklet */
|
#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 */
|
#define TASK_F_USR1 0x00010000 /* preserved user flag 1, application-specific, def:0 */
|
||||||
|
|
@ -61,7 +61,8 @@
|
||||||
#define TASK_F_UEVT2 0x00040000 /* one-shot user event type 2, application specific, def:0 */
|
#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_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_F_UEVT3 0x00100000 /* one-shot user event type 3, application specific, def:0 */
|
||||||
/* unused: 0x200000..0x80000000 */
|
#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 */
|
||||||
|
|
||||||
/* These flags are persistent across scheduler calls */
|
/* These flags are persistent across scheduler calls */
|
||||||
#define TASK_PERSISTENT (TASK_SELF_WAKING | TASK_KILLED | \
|
#define TASK_PERSISTENT (TASK_SELF_WAKING | TASK_KILLED | \
|
||||||
|
|
@ -82,7 +83,7 @@ static forceinline char *task_show_state(char *buf, size_t len, const char *deli
|
||||||
_(TASK_KILLED, _(TASK_HEAVY, _(TASK_WOKEN_INIT,
|
_(TASK_KILLED, _(TASK_HEAVY, _(TASK_WOKEN_INIT,
|
||||||
_(TASK_WOKEN_TIMER, _(TASK_WOKEN_IO, _(TASK_WOKEN_SIGNAL,
|
_(TASK_WOKEN_TIMER, _(TASK_WOKEN_IO, _(TASK_WOKEN_SIGNAL,
|
||||||
_(TASK_WOKEN_MSG, _(TASK_WOKEN_RES, _(TASK_WOKEN_OTHER,
|
_(TASK_WOKEN_MSG, _(TASK_WOKEN_RES, _(TASK_WOKEN_OTHER,
|
||||||
_(TASK_F_TASKLET, _(TASK_F_USR1))))))))))))));
|
_(TASK_F_TASKLET, _(TASK_F_USR1, _(TASK_WOKEN_WQ)))))))))))))));
|
||||||
/* epilogue */
|
/* epilogue */
|
||||||
_(~0U);
|
_(~0U);
|
||||||
return buf;
|
return buf;
|
||||||
|
|
|
||||||
|
|
@ -91,14 +91,13 @@ extern struct pool_head *pool_head_task;
|
||||||
extern struct pool_head *pool_head_tasklet;
|
extern struct pool_head *pool_head_tasklet;
|
||||||
extern struct pool_head *pool_head_notification;
|
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);
|
void __tasklet_wakeup_on(struct tasklet *tl, int thr);
|
||||||
struct list *__tasklet_wakeup_after(struct list *head, struct tasklet *tl);
|
struct list *__tasklet_wakeup_after(struct list *head, struct tasklet *tl);
|
||||||
void task_kill(struct task *t);
|
void task_kill(struct task *t);
|
||||||
void tasklet_kill(struct tasklet *t);
|
void tasklet_kill(struct tasklet *t);
|
||||||
void __task_wakeup(struct task *t);
|
void __task_wakeup(struct task *t);
|
||||||
void __task_queue(struct task *task, struct eb_root *wq);
|
void __task_queue(struct task *task);
|
||||||
|
static inline void _task_queue(struct task *task, const struct ha_caller *caller);
|
||||||
|
|
||||||
unsigned int run_tasks_from_lists(unsigned int budgets[]);
|
unsigned int run_tasks_from_lists(unsigned int budgets[]);
|
||||||
|
|
||||||
|
|
@ -118,7 +117,7 @@ void process_runnable_tasks(void);
|
||||||
void wake_expired_tasks(void);
|
void wake_expired_tasks(void);
|
||||||
|
|
||||||
/* Checks the next timer for the current thread by looking into its own timer
|
/* Checks the next timer for the current thread by looking into its own timer
|
||||||
* list and the global one. It may return TICK_ETERNITY if no timer is present.
|
* list. It may return TICK_ETERNITY if no timer is present.
|
||||||
* Note that the next timer might very well be slightly in the past.
|
* Note that the next timer might very well be slightly in the past.
|
||||||
*/
|
*/
|
||||||
int next_timer_expiry(void);
|
int next_timer_expiry(void);
|
||||||
|
|
@ -205,6 +204,77 @@ static inline uint64_t task_mono_time(void)
|
||||||
return th_ctx->sched_call_date;
|
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> */
|
/* 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
|
/* 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,
|
* by the current thread, in the global runqueue otherwies. With DEBUG_TASK,
|
||||||
|
|
@ -220,7 +290,9 @@ static inline void _task_wakeup(struct task *t, unsigned int f, const struct ha_
|
||||||
|
|
||||||
state = _HA_ATOMIC_OR_FETCH(&t->state, f);
|
state = _HA_ATOMIC_OR_FETCH(&t->state, f);
|
||||||
while (!(state & (TASK_RUNNING | TASK_QUEUED))) {
|
while (!(state & (TASK_RUNNING | TASK_QUEUED))) {
|
||||||
if (_HA_ATOMIC_CAS(&t->state, &state, state | 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 (likely(caller)) {
|
if (likely(caller)) {
|
||||||
caller = HA_ATOMIC_XCHG(&t->caller, caller);
|
caller = HA_ATOMIC_XCHG(&t->caller, caller);
|
||||||
BUG_ON((ulong)caller & 1);
|
BUG_ON((ulong)caller & 1);
|
||||||
|
|
@ -231,6 +303,7 @@ static inline void _task_wakeup(struct task *t, unsigned int f, const struct ha_
|
||||||
__task_wakeup(t);
|
__task_wakeup(t);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
state = _HA_ATOMIC_LOAD(&t->state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,14 +318,28 @@ static inline void task_drop_running(struct task *t, unsigned int f)
|
||||||
{
|
{
|
||||||
unsigned int state, new_state;
|
unsigned int state, new_state;
|
||||||
|
|
||||||
state = _HA_ATOMIC_LOAD(&t->state);
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
new_state = state | f;
|
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;
|
||||||
|
}
|
||||||
if (new_state & TASK_WOKEN_ANY)
|
if (new_state & TASK_WOKEN_ANY)
|
||||||
new_state |= TASK_QUEUED;
|
new_state |= TASK_QUEUED;
|
||||||
|
|
||||||
if (_HA_ATOMIC_CAS(&t->state, &state, new_state & ~TASK_RUNNING))
|
|
||||||
|
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))
|
||||||
break;
|
break;
|
||||||
__ha_cpu_relax();
|
__ha_cpu_relax();
|
||||||
}
|
}
|
||||||
|
|
@ -273,31 +360,21 @@ static inline struct task *__task_unlink_wq(struct task *t)
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remove a task from its wait queue. It may either be the local wait queue if
|
/* remove a task from its wait queue, which during normal operations will be
|
||||||
* the task is bound to a single thread or the global queue. If the task uses a
|
* the current thread's wait queue.
|
||||||
* shared wait queue, the global wait queue lock is used.
|
|
||||||
*/
|
*/
|
||||||
static inline struct task *task_unlink_wq(struct task *t)
|
static inline struct task *task_unlink_wq(struct task *t)
|
||||||
{
|
{
|
||||||
unsigned long locked;
|
|
||||||
|
|
||||||
if (likely(task_in_wq(t))) {
|
if (likely(task_in_wq(t))) {
|
||||||
locked = t->tid < 0;
|
BUG_ON(__task_get_current_owner(t->tid) != tid && !(global.mode & MODE_STOPPING));
|
||||||
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);
|
__task_unlink_wq(t);
|
||||||
if (locked)
|
|
||||||
HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Place <task> into the wait queue, where it may already be. If the expiration
|
/* 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.
|
* 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) \
|
#define task_queue(t) \
|
||||||
_task_queue(t, MK_CALLER(WAKEUP_TYPE_TASK_QUEUE, 0, 0))
|
_task_queue(t, MK_CALLER(WAKEUP_TYPE_TASK_QUEUE, 0, 0))
|
||||||
|
|
@ -316,34 +393,17 @@ static inline void _task_queue(struct task *task, const struct ha_caller *caller
|
||||||
if (!tick_isset(task->expire))
|
if (!tick_isset(task->expire))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef USE_THREAD
|
BUG_ON(task->tid >= 0 && task->tid != tid);
|
||||||
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 (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
if (likely(caller)) {
|
||||||
if (likely(caller)) {
|
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
BUG_ON((ulong)caller & 1);
|
||||||
BUG_ON((ulong)caller & 1);
|
|
||||||
#ifdef DEBUG_TASK
|
#ifdef DEBUG_TASK
|
||||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||||
#endif
|
#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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,6 +423,11 @@ static inline void task_set_thread(struct task *t, int thr)
|
||||||
/* no shared queue without threads */
|
/* no shared queue without threads */
|
||||||
thr = 0;
|
thr = 0;
|
||||||
#endif
|
#endif
|
||||||
|
/*
|
||||||
|
* Nothing to do, the task is only temporarily owned
|
||||||
|
*/
|
||||||
|
if (thr == -1 && t->tid == -2 - tid)
|
||||||
|
return;
|
||||||
if (unlikely(task_in_wq(t))) {
|
if (unlikely(task_in_wq(t))) {
|
||||||
task_unlink_wq(t);
|
task_unlink_wq(t);
|
||||||
t->tid = thr;
|
t->tid = thr;
|
||||||
|
|
@ -440,24 +505,30 @@ 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)
|
static inline void _task_instant_wakeup(struct task *t, unsigned int f, const struct ha_caller *caller)
|
||||||
{
|
{
|
||||||
int thr = t->tid;
|
int thr = t->tid;
|
||||||
|
int newtid;
|
||||||
unsigned int state;
|
unsigned int state;
|
||||||
|
|
||||||
if (thr < 0)
|
|
||||||
thr = tid;
|
|
||||||
|
|
||||||
/* first, let's update the task's state with the wakeup condition */
|
/* first, let's update the task's state with the wakeup condition */
|
||||||
state = _HA_ATOMIC_OR_FETCH(&t->state, f);
|
state = _HA_ATOMIC_OR_FETCH(&t->state, f);
|
||||||
|
|
||||||
/* next we need to make sure the task was not/will not be added to the
|
/* 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
|
* run queue because the tasklet list's mt_list uses the same storage
|
||||||
* as the task's run_queue.
|
* as the task's run_queue.
|
||||||
*/
|
*/
|
||||||
do {
|
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 */
|
/* do nothing if someone else already added it */
|
||||||
if (state & (TASK_QUEUED|TASK_RUNNING))
|
if (state & (TASK_QUEUED|TASK_RUNNING))
|
||||||
return;
|
return;
|
||||||
} while (!_HA_ATOMIC_CAS(&t->state, &state, state | TASK_QUEUED));
|
} while (!__task_set_state_and_tid(t, thr, newtid, state, state | TASK_QUEUED));
|
||||||
|
|
||||||
|
if (newtid < 0)
|
||||||
|
thr = __task_get_current_owner(newtid);
|
||||||
BUG_ON_HOT(task_in_rq(t));
|
BUG_ON_HOT(task_in_rq(t));
|
||||||
|
|
||||||
/* at this point we're the first ones to add this task to the list */
|
/* at this point we're the first ones to add this task to the list */
|
||||||
|
|
@ -707,11 +778,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)
|
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 */
|
/* TODO: mthread, check if there is no task with this test */
|
||||||
if (task_in_rq(task))
|
if (task_in_rq(task))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef USE_THREAD
|
|
||||||
if (task->tid < 0) {
|
if (task->tid < 0) {
|
||||||
/*
|
/*
|
||||||
* If the task is already running, then just wake it up, just
|
* If the task is already running, then just wake it up, just
|
||||||
|
|
@ -729,44 +800,26 @@ static inline void _task_schedule(struct task *task, int when, const struct ha_c
|
||||||
task_wakeup(task, TASK_WOKEN_OTHER);
|
task_wakeup(task, TASK_WOKEN_OTHER);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
did_lock = 1;
|
||||||
/* 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);
|
|
||||||
#ifdef DEBUG_TASK
|
|
||||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
__task_queue(task, &tg_ctx->timers);
|
|
||||||
}
|
|
||||||
task_drop_running(task, 0);
|
|
||||||
HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
|
||||||
} else
|
} else
|
||||||
#endif
|
|
||||||
{
|
|
||||||
BUG_ON(task->tid != tid);
|
BUG_ON(task->tid != tid);
|
||||||
if (task_in_wq(task))
|
|
||||||
when = tick_first(when, task->expire);
|
|
||||||
|
|
||||||
task->expire = when;
|
if (task_in_wq(task))
|
||||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
when = tick_first(when, task->expire);
|
||||||
if (likely(caller)) {
|
|
||||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
task->expire = when;
|
||||||
BUG_ON((ulong)caller & 1);
|
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
|
#ifdef DEBUG_TASK
|
||||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
__task_queue(task, &th_ctx->timers);
|
|
||||||
}
|
}
|
||||||
|
__task_queue(task);
|
||||||
}
|
}
|
||||||
|
if (did_lock)
|
||||||
|
task_drop_running(task, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns the string corresponding to a task type as found in the task caller
|
/* returns the string corresponding to a task type as found in the task caller
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,6 @@ struct ha_rwlock {
|
||||||
*/
|
*/
|
||||||
enum lock_label {
|
enum lock_label {
|
||||||
TASK_RQ_LOCK,
|
TASK_RQ_LOCK,
|
||||||
TASK_WQ_LOCK,
|
|
||||||
LISTENER_LOCK,
|
LISTENER_LOCK,
|
||||||
PROXY_LOCK,
|
PROXY_LOCK,
|
||||||
SERVER_LOCK,
|
SERVER_LOCK,
|
||||||
|
|
|
||||||
|
|
@ -135,8 +135,6 @@ struct tgroup_ctx {
|
||||||
ulong threads_idle; /* mask of threads idling in the poller */
|
ulong threads_idle; /* mask of threads idling in the poller */
|
||||||
ulong stopping_threads; /* mask of threads currently stopping */
|
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 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 */
|
uint committed_extra_streams; /* sum of extra front streams committed by muxes in this group */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -188,4 +188,12 @@ struct file_name_node {
|
||||||
char name[VAR_ARRAY]; /* storage, used with cebus_*() */
|
char name[VAR_ARRAY]; /* storage, used with cebus_*() */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* a pair of uint64_t. It's purposely arranged in little endian to help
|
||||||
|
* being vectorized on modern processors.
|
||||||
|
*/
|
||||||
|
struct uint64_pair {
|
||||||
|
uint64_t l;
|
||||||
|
uint64_t h;
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _HAPROXY_TOOLS_T_H */
|
#endif /* _HAPROXY_TOOLS_T_H */
|
||||||
|
|
|
||||||
|
|
@ -1154,6 +1154,8 @@ void *get_sym_curr_addr(const char *name);
|
||||||
void *get_sym_next_addr(const char *name);
|
void *get_sym_next_addr(const char *name);
|
||||||
int dump_libs(struct buffer *output, int with_addr);
|
int dump_libs(struct buffer *output, int with_addr);
|
||||||
void collect_libs(void);
|
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
|
/* Note that this may result in opening libgcc() on first call, so it may need
|
||||||
* to have been called once before chrooting.
|
* to have been called once before chrooting.
|
||||||
|
|
@ -1288,11 +1290,27 @@ static inline void _ha_aligned_free(void *ptr)
|
||||||
int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz);
|
int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz);
|
||||||
|
|
||||||
/* PRNG */
|
/* PRNG */
|
||||||
|
struct uint64_pair _ha_random64_pair_hashed(void);
|
||||||
|
|
||||||
void ha_generate_uuid_v4(struct buffer *output);
|
void ha_generate_uuid_v4(struct buffer *output);
|
||||||
void ha_generate_uuid_v7(struct buffer *output);
|
void ha_generate_uuid_v7(struct buffer *output);
|
||||||
void ha_random_seed(const unsigned char *seed, size_t len);
|
void ha_random_seed(const unsigned char *seed, size_t len);
|
||||||
void ha_random_jump96(uint32_t dist);
|
void ha_random_seed_thread(void);
|
||||||
|
void ha_random_jump128(uint32_t dist);
|
||||||
|
void ha_random_jump192(uint32_t dist);
|
||||||
uint64_t ha_random64(void);
|
uint64_t ha_random64(void);
|
||||||
|
uint64_t ha_random64_internal(void);
|
||||||
|
|
||||||
|
/* Returns a pair of uint64_t randoms hashed so as not to disclose the internal
|
||||||
|
* PRNG state.
|
||||||
|
*/
|
||||||
|
static inline void ha_random64_pair_hashed(uint64_t *l, uint64_t *h)
|
||||||
|
{
|
||||||
|
struct uint64_pair ret = _ha_random64_pair_hashed();
|
||||||
|
|
||||||
|
*l = ret.l;
|
||||||
|
*h = ret.h;
|
||||||
|
}
|
||||||
|
|
||||||
static inline uint32_t ha_random32()
|
static inline uint32_t ha_random32()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
#ifdef CONFIG_PRODUCT_BRANCH
|
#ifdef CONFIG_PRODUCT_BRANCH
|
||||||
#define PRODUCT_BRANCH CONFIG_PRODUCT_BRANCH
|
#define PRODUCT_BRANCH CONFIG_PRODUCT_BRANCH
|
||||||
#else
|
#else
|
||||||
#define PRODUCT_BRANCH "3.4"
|
#define PRODUCT_BRANCH "3.5"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PRODUCT_STATUS
|
#ifdef CONFIG_PRODUCT_STATUS
|
||||||
|
|
|
||||||
77
reg-tests/checks/extcheck.vtc
Normal file
77
reg-tests/checks/extcheck.vtc
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
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
|
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
|
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
|
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
|
recv
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ haproxy h1 -conf {
|
||||||
thread-groups 1
|
thread-groups 1
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
tune.lua.openlibs none
|
tune.lua.openlibs string
|
||||||
tune.lua.bool-sample-conversion normal
|
tune.lua.bool-sample-conversion normal
|
||||||
lua-load ${testdir}/h_txn_get_priv.lua
|
lua-load ${testdir}/h_txn_get_priv.lua
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ core.register_service("fakeserv", "http", function(applet)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local function cron()
|
local function cron()
|
||||||
|
local httpclient = core.httpclient()
|
||||||
|
|
||||||
-- wait for until the correct port is set through the c0 request..
|
-- wait for until the correct port is set through the c0 request..
|
||||||
while vtc_port == 0 do
|
while vtc_port == 0 do
|
||||||
core.msleep(1)
|
core.msleep(1)
|
||||||
|
|
@ -30,19 +32,16 @@ local function cron()
|
||||||
body = body .. i .. ' ABCDEFGHIJKLMNOPQRSTUVWXYZ\n'
|
body = body .. i .. ' ABCDEFGHIJKLMNOPQRSTUVWXYZ\n'
|
||||||
end
|
end
|
||||||
core.Info("First httpclient request")
|
core.Info("First httpclient request")
|
||||||
local httpclient = core.httpclient()
|
|
||||||
local response = httpclient:post{url="http://127.0.0.1:" .. vtc_port, body=body}
|
local response = httpclient:post{url="http://127.0.0.1:" .. vtc_port, body=body}
|
||||||
core.Info("Received: " .. response.body)
|
core.Info("Received: " .. response.body)
|
||||||
|
|
||||||
body = response.body
|
body = response.body
|
||||||
|
|
||||||
core.Info("Second httpclient request")
|
core.Info("Second httpclient request")
|
||||||
local httpclient2 = core.httpclient()
|
local response2 = httpclient:post{url="http://127.0.0.1:" .. vtc_port2, body=body}
|
||||||
local response2 = httpclient2:post{url="http://127.0.0.1:" .. vtc_port2, body=body}
|
|
||||||
|
|
||||||
core.Info("Third httpclient request")
|
core.Info("Third httpclient request")
|
||||||
local httpclient3 = core.httpclient()
|
local response3 = httpclient:get{url="http://127.0.0.1", dst = vtc_port3, headers={ [ "Host" ] = { "foobar.haproxy.local" } }}
|
||||||
local response3 = httpclient3:get{url="http://127.0.0.1", dst = vtc_port3, headers={ [ "Host" ] = { "foobar.haproxy.local" } }}
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ haproxy h1 -conf {
|
||||||
thread-groups 1
|
thread-groups 1
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
tune.lua.openlibs none
|
tune.lua.openlibs string
|
||||||
tune.lua.bool-sample-conversion normal
|
tune.lua.bool-sample-conversion normal
|
||||||
lua-load ${testdir}/lua_httpclient.lua
|
lua-load ${testdir}/lua_httpclient.lua
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ haproxy h1 -conf {
|
||||||
thread-groups 1
|
thread-groups 1
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
tune.lua.openlibs none
|
tune.lua.openlibs string
|
||||||
tune.lua.bool-sample-conversion normal
|
tune.lua.bool-sample-conversion normal
|
||||||
lua-load ${testdir}/lua_socket.lua
|
lua-load ${testdir}/lua_socket.lua
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ haproxy h1 -conf {
|
||||||
thread-groups 1
|
thread-groups 1
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
tune.lua.openlibs none
|
tune.lua.openlibs string,table
|
||||||
tune.lua.bool-sample-conversion normal
|
tune.lua.bool-sample-conversion normal
|
||||||
lua-load ${testdir}/txn_get_priv.lua
|
lua-load ${testdir}/txn_get_priv.lua
|
||||||
lua-load ${testdir}/txn_get_priv-print_r.lua
|
lua-load ${testdir}/txn_get_priv-print_r.lua
|
||||||
|
|
|
||||||
1
reg-tests/qmux/certs
Symbolic link
1
reg-tests/qmux/certs
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../ssl/certs
|
||||||
39
reg-tests/qmux/h3.vtc
Normal file
39
reg-tests/qmux/h3.vtc
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
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
|
||||||
38
reg-tests/qmux/h3_clr.vtc
Normal file
38
reg-tests/qmux/h3_clr.vtc
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
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,5 +1,9 @@
|
||||||
#REGTEST_TYPE=slow
|
#REGTEST_TYPE=broken
|
||||||
# reg-test is around ~2.5s
|
# 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.
|
# broken with BoringSSL.
|
||||||
|
|
||||||
|
|
|
||||||
297
src/acme.c
297
src/acme.c
|
|
@ -19,6 +19,7 @@
|
||||||
#include <haproxy/acme-t.h>
|
#include <haproxy/acme-t.h>
|
||||||
|
|
||||||
#include <haproxy/acme_resolvers.h>
|
#include <haproxy/acme_resolvers.h>
|
||||||
|
#include <haproxy/event_hdl.h>
|
||||||
#include <haproxy/base64.h>
|
#include <haproxy/base64.h>
|
||||||
#include <haproxy/intops.h>
|
#include <haproxy/intops.h>
|
||||||
#include <haproxy/cfgparse.h>
|
#include <haproxy/cfgparse.h>
|
||||||
|
|
@ -37,6 +38,12 @@
|
||||||
#include <haproxy/ssl_utils.h>
|
#include <haproxy/ssl_utils.h>
|
||||||
#include <haproxy/tools.h>
|
#include <haproxy/tools.h>
|
||||||
#include <haproxy/trace.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
|
#define TRACE_SOURCE &trace_acme
|
||||||
|
|
||||||
|
|
@ -1450,6 +1457,27 @@ error:
|
||||||
return ret;
|
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
|
* Update every certificate instances for the new store
|
||||||
*
|
*
|
||||||
|
|
@ -1503,6 +1531,15 @@ int acme_update_certificate(struct task *task, struct acme_ctx *ctx, char **errm
|
||||||
if (dpapi)
|
if (dpapi)
|
||||||
sink_write(dpapi, LOG_HEADER_NONE, 0, line, 3);
|
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;
|
ctx->store = NULL;
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
@ -1532,7 +1569,7 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||||
|
|
||||||
hdrs = hc->res.hdrs;
|
hdrs = hc->res.hdrs;
|
||||||
|
|
||||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||||
istfree(&ctx->nonce);
|
istfree(&ctx->nonce);
|
||||||
ctx->nonce = istdup(hdr->v);
|
ctx->nonce = istdup(hdr->v);
|
||||||
|
|
@ -1562,6 +1599,16 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||||
key = ctx->store->data->key;
|
key = ctx->store->data->key;
|
||||||
ctx->store->data->key = NULL;
|
ctx->store->data->key = NULL;
|
||||||
|
|
||||||
|
/* OpenSSL's BIO_new_mem_buf() expects a NUL-terminated string when
|
||||||
|
* passed -1. The httpclient buffer lacks this, so manually terminate
|
||||||
|
* it here to prevent an out-of-bounds heap read during PEM parsing.
|
||||||
|
*/
|
||||||
|
if (b_room(&hc->res.buf) < 1) {
|
||||||
|
memprintf(errmsg, "ACME certificate response has no room for NUL terminator");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
hc->res.buf.area[hc->res.buf.data] = '\0';
|
||||||
|
|
||||||
/* XXX: might need a function dedicated to this, which does not read a private key */
|
/* XXX: might need a function dedicated to this, which does not read a private key */
|
||||||
if (ssl_sock_load_pem_into_ckch(ctx->store->path, hc->res.buf.area, ctx->store->data , errmsg) != 0)
|
if (ssl_sock_load_pem_into_ckch(ctx->store->path, hc->res.buf.area, ctx->store->data , errmsg) != 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
@ -1735,7 +1782,7 @@ int acme_res_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||||
|
|
||||||
hdrs = hc->res.hdrs;
|
hdrs = hc->res.hdrs;
|
||||||
|
|
||||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||||
istfree(&ctx->nonce);
|
istfree(&ctx->nonce);
|
||||||
ctx->nonce = istdup(hdr->v);
|
ctx->nonce = istdup(hdr->v);
|
||||||
|
|
@ -1836,7 +1883,7 @@ enum acme_ret acme_res_challenge(struct task *task, struct acme_ctx *ctx, struct
|
||||||
|
|
||||||
TRACE_DATA(__FUNCTION__, ACME_EV_RES, ctx, NULL, &hc->res.buf);
|
TRACE_DATA(__FUNCTION__, ACME_EV_RES, ctx, NULL, &hc->res.buf);
|
||||||
|
|
||||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||||
istfree(&ctx->nonce);
|
istfree(&ctx->nonce);
|
||||||
ctx->nonce = istdup(hdr->v);
|
ctx->nonce = istdup(hdr->v);
|
||||||
|
|
@ -1963,7 +2010,7 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
||||||
|
|
||||||
hdrs = hc->res.hdrs;
|
hdrs = hc->res.hdrs;
|
||||||
|
|
||||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||||
istfree(&ctx->nonce);
|
istfree(&ctx->nonce);
|
||||||
ctx->nonce = istdup(hdr->v);
|
ctx->nonce = istdup(hdr->v);
|
||||||
|
|
@ -2018,6 +2065,7 @@ 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 auth is already valid we need to skip solving challenges */
|
||||||
if (strncasecmp("valid", trash.area, trash.data) == 0) {
|
if (strncasecmp("valid", trash.area, trash.data) == 0) {
|
||||||
auth->validated = 1;
|
auth->validated = 1;
|
||||||
|
auth->ready = ctx->cfg->cond_ready; /* no challenge needed, satisfy all readiness conditions */
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2171,6 +2219,22 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
||||||
dpapi = sink_find("dpapi");
|
dpapi = sink_find("dpapi");
|
||||||
if (dpapi)
|
if (dpapi)
|
||||||
sink_write(dpapi, LOG_HEADER_NONE, 0, line, nmsg);
|
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) {
|
else if (strcasecmp(ctx->cfg->challenge, "http-01") == 0) {
|
||||||
/* only useful for http-01 */
|
/* only useful for http-01 */
|
||||||
|
|
@ -2279,7 +2343,7 @@ int acme_res_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||||
|
|
||||||
hdrs = hc->res.hdrs;
|
hdrs = hc->res.hdrs;
|
||||||
|
|
||||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||||
istfree(&ctx->nonce);
|
istfree(&ctx->nonce);
|
||||||
ctx->nonce = istdup(hdr->v);
|
ctx->nonce = istdup(hdr->v);
|
||||||
|
|
@ -2458,7 +2522,7 @@ int acme_res_account(struct task *task, struct acme_ctx *ctx, int newaccount, ch
|
||||||
|
|
||||||
hdrs = hc->res.hdrs;
|
hdrs = hc->res.hdrs;
|
||||||
|
|
||||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||||
if (isteqi(hdr->n, ist("Location"))) {
|
if (isteqi(hdr->n, ist("Location"))) {
|
||||||
istfree(&ctx->kid);
|
istfree(&ctx->kid);
|
||||||
ctx->kid = istdup(hdr->v);
|
ctx->kid = istdup(hdr->v);
|
||||||
|
|
@ -2525,7 +2589,7 @@ int acme_nonce(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
||||||
|
|
||||||
hdrs = hc->res.hdrs;
|
hdrs = hc->res.hdrs;
|
||||||
|
|
||||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||||
istfree(&ctx->nonce);
|
istfree(&ctx->nonce);
|
||||||
ctx->nonce = istdup(hdr->v);
|
ctx->nonce = istdup(hdr->v);
|
||||||
|
|
@ -3495,16 +3559,64 @@ err:
|
||||||
return cli_dynerr(appctx, errmsg);
|
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)
|
static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
{
|
{
|
||||||
char *msg = NULL;
|
char *msg = NULL;
|
||||||
const char *crt;
|
const char *crt;
|
||||||
const char *dns;
|
const char *dns;
|
||||||
struct acme_ctx *ctx = NULL;
|
int ret;
|
||||||
struct acme_auth *auth = NULL;
|
|
||||||
int found = 0;
|
|
||||||
int remain = 0;
|
|
||||||
struct ebmb_node *node = NULL;
|
|
||||||
|
|
||||||
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -3517,39 +3629,13 @@ static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx
|
||||||
crt = args[2];
|
crt = args[2];
|
||||||
dns = args[4];
|
dns = args[4];
|
||||||
|
|
||||||
HA_RWLOCK_WRLOCK(OTHER_LOCK, &acme_lock);
|
ret = acme_challenge_ready(crt, dns);
|
||||||
node = ebst_lookup(&acme_tasks, crt);
|
if (ret < 0) {
|
||||||
if (node) {
|
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));
|
||||||
ctx = ebmb_entry(node, struct acme_ctx, node);
|
} else if (ret == 0) {
|
||||||
if (ctx->cfg->cond_ready & ACME_RDY_CLI)
|
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "'%s' challenges ready! All challenges ready, starting challenges validation!", crt));
|
||||||
auth = ctx->auths;
|
} else if (ret > 0) {
|
||||||
while (auth) {
|
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "'%s' challenge(s) ready! Remaining challenges to deploy: %d", crt, ret));
|
||||||
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:
|
err:
|
||||||
|
|
@ -3659,6 +3745,127 @@ static void __acme_init(void)
|
||||||
}
|
}
|
||||||
INITCALL0(STG_REGISTER, __acme_init);
|
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 */
|
#endif /* ! HAVE_ACME */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,10 @@ static const char *const memprof_methods[MEMPROF_METH_METHODS] = {
|
||||||
struct memprof_stats memprof_stats[MEMPROF_HASH_BUCKETS + 1] = { };
|
struct memprof_stats memprof_stats[MEMPROF_HASH_BUCKETS + 1] = { };
|
||||||
|
|
||||||
/* used to detect recursive calls */
|
/* used to detect recursive calls */
|
||||||
static THREAD_LOCAL int in_memprof = 0;
|
#define MEMPROF_IN_INIT (1U << 0)
|
||||||
|
#define MEMPROF_IN_HANDLER (1U << 1)
|
||||||
|
|
||||||
|
static THREAD_LOCAL uint in_memprof = 0; // arithmetic OR of MEMPROF_IN_*
|
||||||
|
|
||||||
/* These ones are used by glibc and will be called early. They are in charge of
|
/* These ones are used by glibc and will be called early. They are in charge of
|
||||||
* initializing the handlers with the original functions.
|
* initializing the handlers with the original functions.
|
||||||
|
|
@ -137,7 +140,7 @@ static __attribute__((noreturn)) void memprof_die(const char *msg)
|
||||||
*/
|
*/
|
||||||
static void memprof_init()
|
static void memprof_init()
|
||||||
{
|
{
|
||||||
in_memprof++;
|
in_memprof |= MEMPROF_IN_INIT;
|
||||||
memprof_malloc_handler = get_sym_next_addr("malloc");
|
memprof_malloc_handler = get_sym_next_addr("malloc");
|
||||||
if (!memprof_malloc_handler)
|
if (!memprof_malloc_handler)
|
||||||
memprof_die("FATAL: malloc() function not found.\n");
|
memprof_die("FATAL: malloc() function not found.\n");
|
||||||
|
|
@ -168,7 +171,7 @@ static void memprof_init()
|
||||||
memprof_aligned_alloc_handler = get_sym_next_addr("aligned_alloc");
|
memprof_aligned_alloc_handler = get_sym_next_addr("aligned_alloc");
|
||||||
memprof_posix_memalign_handler = get_sym_next_addr("posix_memalign");
|
memprof_posix_memalign_handler = get_sym_next_addr("posix_memalign");
|
||||||
|
|
||||||
in_memprof--;
|
in_memprof &= ~MEMPROF_IN_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* the initial handlers will initialize all regular handlers and will call the
|
/* the initial handlers will initialize all regular handlers and will call the
|
||||||
|
|
@ -177,7 +180,7 @@ static void memprof_init()
|
||||||
*/
|
*/
|
||||||
static void *memprof_malloc_initial_handler(size_t size)
|
static void *memprof_malloc_initial_handler(size_t size)
|
||||||
{
|
{
|
||||||
if (in_memprof) {
|
if (in_memprof & MEMPROF_IN_INIT) {
|
||||||
/* it's likely that dlsym() needs malloc(), let's fail */
|
/* it's likely that dlsym() needs malloc(), let's fail */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +191,7 @@ static void *memprof_malloc_initial_handler(size_t size)
|
||||||
|
|
||||||
static void *memprof_calloc_initial_handler(size_t nmemb, size_t size)
|
static void *memprof_calloc_initial_handler(size_t nmemb, size_t size)
|
||||||
{
|
{
|
||||||
if (in_memprof) {
|
if (in_memprof & MEMPROF_IN_INIT) {
|
||||||
/* it's likely that dlsym() needs calloc(), let's fail */
|
/* it's likely that dlsym() needs calloc(), let's fail */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -198,7 +201,7 @@ static void *memprof_calloc_initial_handler(size_t nmemb, size_t size)
|
||||||
|
|
||||||
static void *memprof_realloc_initial_handler(void *ptr, size_t size)
|
static void *memprof_realloc_initial_handler(void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
if (in_memprof) {
|
if (in_memprof & MEMPROF_IN_INIT) {
|
||||||
/* it's likely that dlsym() needs realloc(), let's fail */
|
/* it's likely that dlsym() needs realloc(), let's fail */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -209,7 +212,7 @@ static void *memprof_realloc_initial_handler(void *ptr, size_t size)
|
||||||
|
|
||||||
static char *memprof_strdup_initial_handler(const char *s)
|
static char *memprof_strdup_initial_handler(const char *s)
|
||||||
{
|
{
|
||||||
if (in_memprof) {
|
if (in_memprof & MEMPROF_IN_INIT) {
|
||||||
/* probably that dlsym() needs strdup(), let's fail */
|
/* probably that dlsym() needs strdup(), let's fail */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +231,7 @@ static void memprof_free_initial_handler(void *ptr)
|
||||||
|
|
||||||
static char *memprof_strndup_initial_handler(const char *s, size_t n)
|
static char *memprof_strndup_initial_handler(const char *s, size_t n)
|
||||||
{
|
{
|
||||||
if (in_memprof) {
|
if (in_memprof & MEMPROF_IN_INIT) {
|
||||||
/* probably that dlsym() needs strndup(), let's fail */
|
/* probably that dlsym() needs strndup(), let's fail */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -239,7 +242,7 @@ static char *memprof_strndup_initial_handler(const char *s, size_t n)
|
||||||
|
|
||||||
static void *memprof_valloc_initial_handler(size_t sz)
|
static void *memprof_valloc_initial_handler(size_t sz)
|
||||||
{
|
{
|
||||||
if (in_memprof) {
|
if (in_memprof & MEMPROF_IN_INIT) {
|
||||||
/* probably that dlsym() needs valloc(), let's fail */
|
/* probably that dlsym() needs valloc(), let's fail */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +253,7 @@ static void *memprof_valloc_initial_handler(size_t sz)
|
||||||
|
|
||||||
static void *memprof_pvalloc_initial_handler(size_t sz)
|
static void *memprof_pvalloc_initial_handler(size_t sz)
|
||||||
{
|
{
|
||||||
if (in_memprof) {
|
if (in_memprof & MEMPROF_IN_INIT) {
|
||||||
/* probably that dlsym() needs pvalloc(), let's fail */
|
/* probably that dlsym() needs pvalloc(), let's fail */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -261,7 +264,7 @@ static void *memprof_pvalloc_initial_handler(size_t sz)
|
||||||
|
|
||||||
static void *memprof_memalign_initial_handler(size_t al, size_t sz)
|
static void *memprof_memalign_initial_handler(size_t al, size_t sz)
|
||||||
{
|
{
|
||||||
if (in_memprof) {
|
if (in_memprof & MEMPROF_IN_INIT) {
|
||||||
/* probably that dlsym() needs memalign(), let's fail */
|
/* probably that dlsym() needs memalign(), let's fail */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +275,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)
|
static void *memprof_aligned_alloc_initial_handler(size_t al, size_t sz)
|
||||||
{
|
{
|
||||||
if (in_memprof) {
|
if (in_memprof & MEMPROF_IN_INIT) {
|
||||||
/* probably that dlsym() needs aligned_alloc(), let's fail */
|
/* probably that dlsym() needs aligned_alloc(), let's fail */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +286,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)
|
static int memprof_posix_memalign_initial_handler(void **ptr, size_t al, size_t sz)
|
||||||
{
|
{
|
||||||
if (in_memprof) {
|
if (in_memprof & MEMPROF_IN_INIT) {
|
||||||
/* probably that dlsym() needs posix_memalign(), let's fail */
|
/* probably that dlsym() needs posix_memalign(), let's fail */
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
@ -344,11 +347,13 @@ void *malloc(size_t size)
|
||||||
struct memprof_stats *bin;
|
struct memprof_stats *bin;
|
||||||
void *ret;
|
void *ret;
|
||||||
|
|
||||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||||
return memprof_malloc_handler(size);
|
return memprof_malloc_handler(size);
|
||||||
|
|
||||||
|
in_memprof |= MEMPROF_IN_HANDLER;
|
||||||
ret = memprof_malloc_handler(size);
|
ret = memprof_malloc_handler(size);
|
||||||
size = malloc_usable_size(ret) + sizeof(void *);
|
size = malloc_usable_size(ret) + sizeof(void *);
|
||||||
|
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||||
|
|
||||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MALLOC);
|
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MALLOC);
|
||||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||||
|
|
@ -371,11 +376,13 @@ void *calloc(size_t nmemb, size_t size)
|
||||||
struct memprof_stats *bin;
|
struct memprof_stats *bin;
|
||||||
void *ret;
|
void *ret;
|
||||||
|
|
||||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||||
return memprof_calloc_handler(nmemb, size);
|
return memprof_calloc_handler(nmemb, size);
|
||||||
|
|
||||||
|
in_memprof |= MEMPROF_IN_HANDLER;
|
||||||
ret = memprof_calloc_handler(nmemb, size);
|
ret = memprof_calloc_handler(nmemb, size);
|
||||||
size = malloc_usable_size(ret) + sizeof(void *);
|
size = malloc_usable_size(ret) + sizeof(void *);
|
||||||
|
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||||
|
|
||||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_CALLOC);
|
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_CALLOC);
|
||||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||||
|
|
@ -401,12 +408,14 @@ void *realloc(void *ptr, size_t size)
|
||||||
size_t size_before;
|
size_t size_before;
|
||||||
void *ret;
|
void *ret;
|
||||||
|
|
||||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||||
return memprof_realloc_handler(ptr, size);
|
return memprof_realloc_handler(ptr, size);
|
||||||
|
|
||||||
|
in_memprof |= MEMPROF_IN_HANDLER;
|
||||||
size_before = malloc_usable_size(ptr);
|
size_before = malloc_usable_size(ptr);
|
||||||
ret = memprof_realloc_handler(ptr, size);
|
ret = memprof_realloc_handler(ptr, size);
|
||||||
size = malloc_usable_size(ret);
|
size = malloc_usable_size(ret);
|
||||||
|
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||||
|
|
||||||
/* only count the extra link for new allocations */
|
/* only count the extra link for new allocations */
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
|
|
@ -439,11 +448,13 @@ char *strdup(const char *s)
|
||||||
size_t size;
|
size_t size;
|
||||||
char *ret;
|
char *ret;
|
||||||
|
|
||||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||||
return memprof_strdup_handler(s);
|
return memprof_strdup_handler(s);
|
||||||
|
|
||||||
|
in_memprof |= MEMPROF_IN_HANDLER;
|
||||||
ret = memprof_strdup_handler(s);
|
ret = memprof_strdup_handler(s);
|
||||||
size = malloc_usable_size(ret) + sizeof(void *);
|
size = malloc_usable_size(ret) + sizeof(void *);
|
||||||
|
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||||
|
|
||||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRDUP);
|
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRDUP);
|
||||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||||
|
|
@ -469,13 +480,15 @@ void free(void *ptr)
|
||||||
struct memprof_stats *bin;
|
struct memprof_stats *bin;
|
||||||
size_t size_before;
|
size_t size_before;
|
||||||
|
|
||||||
if (likely(!(profiling & HA_PROF_MEMORY) || !ptr)) {
|
if (likely(!(profiling & HA_PROF_MEMORY) || !ptr || (in_memprof & MEMPROF_IN_HANDLER))) {
|
||||||
memprof_free_handler(ptr);
|
memprof_free_handler(ptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
in_memprof |= MEMPROF_IN_HANDLER;
|
||||||
size_before = malloc_usable_size(ptr) + sizeof(void *);
|
size_before = malloc_usable_size(ptr) + sizeof(void *);
|
||||||
memprof_free_handler(ptr);
|
memprof_free_handler(ptr);
|
||||||
|
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||||
|
|
||||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_FREE);
|
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_FREE);
|
||||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||||
|
|
@ -495,10 +508,13 @@ char *strndup(const char *s, size_t size)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ret = memprof_strndup_handler(s, size);
|
ret = memprof_strndup_handler(s, size);
|
||||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
in_memprof |= MEMPROF_IN_HANDLER;
|
||||||
size = malloc_usable_size(ret) + sizeof(void *);
|
size = malloc_usable_size(ret) + sizeof(void *);
|
||||||
|
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||||
|
|
||||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRNDUP);
|
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRNDUP);
|
||||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||||
|
|
@ -516,10 +532,13 @@ void *valloc(size_t size)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ret = memprof_valloc_handler(size);
|
ret = memprof_valloc_handler(size);
|
||||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
in_memprof |= MEMPROF_IN_HANDLER;
|
||||||
size = malloc_usable_size(ret) + sizeof(void *);
|
size = malloc_usable_size(ret) + sizeof(void *);
|
||||||
|
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||||
|
|
||||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_VALLOC);
|
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_VALLOC);
|
||||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||||
|
|
@ -537,10 +556,13 @@ void *pvalloc(size_t size)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ret = memprof_pvalloc_handler(size);
|
ret = memprof_pvalloc_handler(size);
|
||||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
in_memprof |= MEMPROF_IN_HANDLER;
|
||||||
size = malloc_usable_size(ret) + sizeof(void *);
|
size = malloc_usable_size(ret) + sizeof(void *);
|
||||||
|
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||||
|
|
||||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_PVALLOC);
|
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_PVALLOC);
|
||||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||||
|
|
@ -558,10 +580,13 @@ void *memalign(size_t align, size_t size)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ret = memprof_memalign_handler(align, size);
|
ret = memprof_memalign_handler(align, size);
|
||||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
in_memprof |= MEMPROF_IN_HANDLER;
|
||||||
size = malloc_usable_size(ret) + sizeof(void *);
|
size = malloc_usable_size(ret) + sizeof(void *);
|
||||||
|
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||||
|
|
||||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MEMALIGN);
|
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MEMALIGN);
|
||||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||||
|
|
@ -579,10 +604,13 @@ void *aligned_alloc(size_t align, size_t size)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ret = memprof_aligned_alloc_handler(align, size);
|
ret = memprof_aligned_alloc_handler(align, size);
|
||||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
in_memprof |= MEMPROF_IN_HANDLER;
|
||||||
size = malloc_usable_size(ret) + sizeof(void *);
|
size = malloc_usable_size(ret) + sizeof(void *);
|
||||||
|
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||||
|
|
||||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_ALIGNED_ALLOC);
|
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_ALIGNED_ALLOC);
|
||||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||||
|
|
@ -600,13 +628,16 @@ int posix_memalign(void **ptr, size_t align, size_t size)
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
|
|
||||||
ret = memprof_posix_memalign_handler(ptr, align, size);
|
ret = memprof_posix_memalign_handler(ptr, align, size);
|
||||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (ret != 0) // error
|
if (ret != 0) // error
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
in_memprof |= MEMPROF_IN_HANDLER;
|
||||||
size = malloc_usable_size(*ptr) + sizeof(void *);
|
size = malloc_usable_size(*ptr) + sizeof(void *);
|
||||||
|
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||||
|
|
||||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_POSIX_MEMALIGN);
|
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_POSIX_MEMALIGN);
|
||||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||||
|
|
|
||||||
|
|
@ -539,9 +539,6 @@ size_t appctx_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, unsig
|
||||||
if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC))
|
if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC))
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
if (!count)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
if (!appctx_get_buf(appctx, &appctx->outbuf)) {
|
if (!appctx_get_buf(appctx, &appctx->outbuf)) {
|
||||||
TRACE_STATE("waiting for appctx outbuf allocation", APPLET_EV_RECV|APPLET_EV_BLK, appctx);
|
TRACE_STATE("waiting for appctx outbuf allocation", APPLET_EV_RECV|APPLET_EV_BLK, appctx);
|
||||||
goto end;
|
goto end;
|
||||||
|
|
@ -550,7 +547,8 @@ size_t appctx_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, unsig
|
||||||
if (flags & CO_RFL_BUF_FLUSH)
|
if (flags & CO_RFL_BUF_FLUSH)
|
||||||
applet_fl_set(appctx, APPCTX_FL_FASTFWD);
|
applet_fl_set(appctx, APPCTX_FL_FASTFWD);
|
||||||
|
|
||||||
ret = CALL_APPLET_WITH_RET(appctx->applet, rcv_buf(appctx, buf, count, flags));
|
if (count)
|
||||||
|
ret = CALL_APPLET_WITH_RET(appctx->applet, rcv_buf(appctx, buf, count, flags));
|
||||||
if (ret)
|
if (ret)
|
||||||
applet_fl_clr(appctx, APPCTX_FL_OUTBLK_FULL);
|
applet_fl_clr(appctx, APPCTX_FL_OUTBLK_FULL);
|
||||||
|
|
||||||
|
|
@ -613,7 +611,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);
|
appctx_htx->flags |= (buf_htx->flags & HTX_FL_EOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
htx_to_buf(appctx_htx, &appctx->outbuf);
|
htx_to_buf(appctx_htx, &appctx->inbuf);
|
||||||
htx_to_buf(buf_htx, buf);
|
htx_to_buf(buf_htx, buf);
|
||||||
ret -= buf_htx->data;
|
ret -= buf_htx->data;
|
||||||
end:
|
end:
|
||||||
|
|
|
||||||
|
|
@ -297,7 +297,7 @@ check_user(struct userlist *ul, const char *user, const char *pass)
|
||||||
fprintf(stderr, ", crypt=%s\n", ((ep) ? ep : ""));
|
fprintf(stderr, ", crypt=%s\n", ((ep) ? ep : ""));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ep && strcmp(ep, u->pass) == 0)
|
if (ep && u->pass && strcmp(ep, u->pass) == 0)
|
||||||
return 1;
|
return 1;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -1818,7 +1818,7 @@ int connect_server(struct stream *s)
|
||||||
{
|
{
|
||||||
struct connection *cli_conn = objt_conn(strm_orig(s));
|
struct connection *cli_conn = objt_conn(strm_orig(s));
|
||||||
struct connection *srv_conn = NULL;
|
struct connection *srv_conn = NULL;
|
||||||
const struct mux_proto_list *mux_proto;
|
const struct mux_proto_list *mux_proto = NULL;
|
||||||
struct server *srv;
|
struct server *srv;
|
||||||
struct ist name = IST_NULL;
|
struct ist name = IST_NULL;
|
||||||
struct sample *name_smp;
|
struct sample *name_smp;
|
||||||
|
|
@ -2139,12 +2139,10 @@ int connect_server(struct stream *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (may_start_mux_now) {
|
if (may_start_mux_now) {
|
||||||
/* Delay QMux MUX init to let xprt_qmux handshake runs first. */
|
/* Delay MUX init if an XPRT handshake is required prior. */
|
||||||
mux_proto = conn_select_mux_be(srv_conn);
|
mux_proto = conn_select_mux_be(srv_conn);
|
||||||
if (mux_proto && mux_proto->init_xprt == XPRT_QMUX) {
|
if (mux_proto && mux_proto->init_xprt)
|
||||||
srv_conn->flags |= (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
|
|
||||||
may_start_mux_now = 0;
|
may_start_mux_now = 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
||||||
|
|
@ -2254,6 +2252,13 @@ int connect_server(struct stream *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (mux_proto && mux_proto->init_xprt) {
|
||||||
|
/* Add handshake layer prior to MUX init if required. Does nothing if SSL layer is active though. */
|
||||||
|
if (xprt_add_l6hs(srv_conn, mux_proto->init_xprt)) {
|
||||||
|
conn_full_close(srv_conn);
|
||||||
|
return SF_ERR_INTERNAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now that the mux may have been created, we can start the xprt.
|
* Now that the mux may have been created, we can start the xprt.
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,9 @@ int base64dec(const char *in, size_t ilen, char *out, size_t olen) {
|
||||||
signed char b;
|
signed char b;
|
||||||
int convlen = 0, i = 0, pad = 0;
|
int convlen = 0, i = 0, pad = 0;
|
||||||
|
|
||||||
|
if (!ilen)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (ilen % 4)
|
if (ilen % 4)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
|
||||||
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.
|
* delete_expired==0, write otherwise.
|
||||||
*/
|
*/
|
||||||
struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_entry *entry,
|
struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_entry *entry,
|
||||||
const char *secondary_key, int delete_expired)
|
const char *primary_hash, const char *secondary_key, int delete_expired)
|
||||||
{
|
{
|
||||||
struct eb32_node *node = &entry->eb;
|
struct eb32_node *node = &entry->eb;
|
||||||
|
|
||||||
|
|
@ -395,6 +395,12 @@ struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_e
|
||||||
entry = node ? eb32_entry(node, struct cache_entry, eb) : NULL;
|
entry = node ? eb32_entry(node, struct cache_entry, eb) : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Now verify the full primary hash matches: eb32 only compares 32 bits so
|
||||||
|
* we could have ended up on a different, unrelated entry.
|
||||||
|
*/
|
||||||
|
if (entry && primary_hash && memcmp(entry->hash, primary_hash, sizeof(entry->hash)))
|
||||||
|
entry = NULL;
|
||||||
|
|
||||||
/* Expired entry */
|
/* Expired entry */
|
||||||
if (entry && entry->expire <= date.tv_sec) {
|
if (entry && entry->expire <= date.tv_sec) {
|
||||||
if (delete_expired) {
|
if (delete_expired) {
|
||||||
|
|
@ -943,8 +949,8 @@ int http_calc_maxage(struct stream *s, struct cache *cache, int *true_maxage)
|
||||||
if (value) {
|
if (value) {
|
||||||
struct buffer *chk = get_trash_chunk();
|
struct buffer *chk = get_trash_chunk();
|
||||||
|
|
||||||
chunk_memcat(chk, value, ctx.value.len - 8 + 1);
|
chunk_memcat(chk, value, ctx.value.len - (8 + 1));
|
||||||
chunk_memcat(chk, "", 1);
|
*(b_tail(chk)) = '\0';
|
||||||
offset = (*chk->area == '"') ? 1 : 0;
|
offset = (*chk->area == '"') ? 1 : 0;
|
||||||
smaxage = strtol(chk->area + offset, &endptr, 10);
|
smaxage = strtol(chk->area + offset, &endptr, 10);
|
||||||
if (unlikely(smaxage < 0 || endptr == chk->area + offset))
|
if (unlikely(smaxage < 0 || endptr == chk->area + offset))
|
||||||
|
|
@ -955,8 +961,8 @@ int http_calc_maxage(struct stream *s, struct cache *cache, int *true_maxage)
|
||||||
if (value) {
|
if (value) {
|
||||||
struct buffer *chk = get_trash_chunk();
|
struct buffer *chk = get_trash_chunk();
|
||||||
|
|
||||||
chunk_memcat(chk, value, ctx.value.len - 7 + 1);
|
chunk_memcat(chk, value, ctx.value.len - (7 + 1));
|
||||||
chunk_memcat(chk, "", 1);
|
*(b_tail(chk)) = '\0';
|
||||||
offset = (*chk->area == '"') ? 1 : 0;
|
offset = (*chk->area == '"') ? 1 : 0;
|
||||||
maxage = strtol(chk->area + offset, &endptr, 10);
|
maxage = strtol(chk->area + offset, &endptr, 10);
|
||||||
if (unlikely(maxage < 0 || endptr == chk->area + offset))
|
if (unlikely(maxage < 0 || endptr == chk->area + offset))
|
||||||
|
|
@ -1303,7 +1309,7 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
|
||||||
if (old) {
|
if (old) {
|
||||||
if (vary_signature)
|
if (vary_signature)
|
||||||
old = get_secondary_entry(cache_tree, old,
|
old = get_secondary_entry(cache_tree, old,
|
||||||
txn->cache_secondary_hash, 1);
|
txn->cache_hash, txn->cache_secondary_hash, 1);
|
||||||
if (old) {
|
if (old) {
|
||||||
if (!old->complete) {
|
if (!old->complete) {
|
||||||
/* An entry with the same primary key is already being
|
/* An entry with the same primary key is already being
|
||||||
|
|
@ -2178,9 +2184,20 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p
|
||||||
if (!http_request_build_secondary_key(s, res->secondary_key_signature)) {
|
if (!http_request_build_secondary_key(s, res->secondary_key_signature)) {
|
||||||
cache_rdlock(cache_tree);
|
cache_rdlock(cache_tree);
|
||||||
sec_entry = get_secondary_entry(cache_tree, res,
|
sec_entry = get_secondary_entry(cache_tree, res,
|
||||||
|
s->txn.http->cache_hash,
|
||||||
s->txn.http->cache_secondary_hash,
|
s->txn.http->cache_secondary_hash,
|
||||||
0);
|
0);
|
||||||
if (sec_entry && sec_entry != res) {
|
if (!sec_entry) {
|
||||||
|
/* Secondary key miss: release the retained primary entry
|
||||||
|
* and reattach the detached row before returning.
|
||||||
|
*/
|
||||||
|
release_entry(cache_tree, res, 0);
|
||||||
|
shctx_wrlock(shctx);
|
||||||
|
if (detached)
|
||||||
|
shctx_row_reattach(shctx, entry_block);
|
||||||
|
shctx_wrunlock(shctx);
|
||||||
|
}
|
||||||
|
else if (sec_entry != res) {
|
||||||
/* The wrong row was added to the hot list. */
|
/* The wrong row was added to the hot list. */
|
||||||
release_entry(cache_tree, res, 0);
|
release_entry(cache_tree, res, 0);
|
||||||
retain_entry(sec_entry);
|
retain_entry(sec_entry);
|
||||||
|
|
@ -3030,6 +3047,7 @@ static int cli_io_handler_show_cache(struct appctx *appctx)
|
||||||
node = eb32_lookup_ge(&cache_tree->entries, next_key);
|
node = eb32_lookup_ge(&cache_tree->entries, next_key);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
ctx->next_key = 0;
|
ctx->next_key = 0;
|
||||||
|
next_key = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1629,11 +1629,6 @@ static int cfg_parse_global_shm_stats_file(char **args, int section_type,
|
||||||
struct proxy *curpx, const struct proxy *defpx,
|
struct proxy *curpx, const struct proxy *defpx,
|
||||||
const char *file, int line, char **err)
|
const char *file, int line, char **err)
|
||||||
{
|
{
|
||||||
if (!experimental_directives_allowed) {
|
|
||||||
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'", args[0]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (global.shm_stats_file != NULL) {
|
if (global.shm_stats_file != NULL) {
|
||||||
memprintf(err, "'%s' already specified.\n", args[0]);
|
memprintf(err, "'%s' already specified.\n", args[0]);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1644,7 +1639,6 @@ static int cfg_parse_global_shm_stats_file(char **args, int section_type,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
|
||||||
global.shm_stats_file = strdup(args[1]);
|
global.shm_stats_file = strdup(args[1]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1653,11 +1647,6 @@ static int cfg_parse_global_shm_stats_file_max_objects(char **args, int section_
|
||||||
struct proxy *curpx, const struct proxy *defpx,
|
struct proxy *curpx, const struct proxy *defpx,
|
||||||
const char *file, int line, char **err)
|
const char *file, int line, char **err)
|
||||||
{
|
{
|
||||||
if (!experimental_directives_allowed) {
|
|
||||||
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'", args[0]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shm_stats_file_max_objects != -1) {
|
if (shm_stats_file_max_objects != -1) {
|
||||||
memprintf(err, "'%s' already specified.\n", args[0]);
|
memprintf(err, "'%s' already specified.\n", args[0]);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1668,7 +1657,6 @@ static int cfg_parse_global_shm_stats_file_max_objects(char **args, int section_
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
|
||||||
shm_stats_file_max_objects = atoi(args[1]);
|
shm_stats_file_max_objects = atoi(args[1]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2486,16 +2486,17 @@ init_proxies_list_stage1:
|
||||||
/* At this point, target names have already been resolved. */
|
/* At this point, target names have already been resolved. */
|
||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
|
|
||||||
idle_conn_task = task_new_anywhere();
|
for (i = 0; i < global.nbthread; i++) {
|
||||||
if (!idle_conn_task) {
|
idle_conn_srv[i] = EB_ROOT;
|
||||||
ha_alert("parsing : failed to allocate global idle connection task.\n");
|
idle_conn_task[i] = task_new_on(i);
|
||||||
cfgerr++;
|
if (!idle_conn_task[i]) {
|
||||||
}
|
ha_alert("parsing : failed to allocate global idle connection task.\n");
|
||||||
else {
|
cfgerr++;
|
||||||
idle_conn_task->process = srv_cleanup_idle_conns;
|
}
|
||||||
idle_conn_task->context = NULL;
|
else {
|
||||||
|
idle_conn_task[i]->process = srv_cleanup_idle_conns;
|
||||||
|
idle_conn_task[i]->context = NULL;
|
||||||
|
|
||||||
for (i = 0; i < global.nbthread; i++) {
|
|
||||||
idle_conns[i].cleanup_task = task_new_on(i);
|
idle_conns[i].cleanup_task = task_new_on(i);
|
||||||
if (!idle_conns[i].cleanup_task) {
|
if (!idle_conns[i].cleanup_task) {
|
||||||
ha_alert("parsing : failed to allocate idle connection tasks for thread '%d'.\n", i);
|
ha_alert("parsing : failed to allocate idle connection tasks for thread '%d'.\n", i);
|
||||||
|
|
|
||||||
26
src/check.c
26
src/check.c
|
|
@ -232,6 +232,9 @@ static void check_trace(enum trace_level level, uint64_t mask,
|
||||||
chunk_appendf(&trace_buf, " sc=%p(0x%08x)", check->sc, check->sc->flags);
|
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) {
|
if (mask & CHK_EV_TCPCHK) {
|
||||||
const char *type;
|
const char *type;
|
||||||
|
|
||||||
|
|
@ -1404,7 +1407,20 @@ struct task *process_chk_conn(struct task *t, void *context, unsigned int state)
|
||||||
|
|
||||||
check_release_buf(check, &check->bi);
|
check_release_buf(check, &check->bi);
|
||||||
check_release_buf(check, &check->bo);
|
check_release_buf(check, &check->bo);
|
||||||
_HA_ATOMIC_DEC(&th_ctx->running_checks);
|
|
||||||
|
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->active_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_INPROGRESS|CHK_ST_IN_ALLOC|CHK_ST_OUT_ALLOC);
|
||||||
check->state &= ~CHK_ST_READY;
|
check->state &= ~CHK_ST_READY;
|
||||||
|
|
@ -1563,6 +1579,7 @@ void free_check(struct check *check)
|
||||||
ha_free(&check->tcpcheck);
|
ha_free(&check->tcpcheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LIST_DEL_INIT(&check->check_queue);
|
||||||
pool_free(pool_head_uniqueid, istptr(check->unique_id));
|
pool_free(pool_head_uniqueid, istptr(check->unique_id));
|
||||||
check->unique_id = IST_NULL;
|
check->unique_id = IST_NULL;
|
||||||
ha_free(&check->pool_conn_name);
|
ha_free(&check->pool_conn_name);
|
||||||
|
|
@ -1682,7 +1699,7 @@ static int start_checks()
|
||||||
for (px = proxies_list; px; px = px->next) {
|
for (px = proxies_list; px; px = px->next) {
|
||||||
for (s = px->srv; s; s = s->next) {
|
for (s = px->srv; s; s = s->next) {
|
||||||
if ((px->options2 & PR_O2_USE_SBUF_CHECK) &&
|
if ((px->options2 & PR_O2_USE_SBUF_CHECK) &&
|
||||||
(s->check.tcpcheck->rs->flags & TCPCHK_RULES_MAY_USE_SBUF))
|
(s->check.tcpcheck->rs && s->check.tcpcheck->rs->flags & TCPCHK_RULES_MAY_USE_SBUF))
|
||||||
s->check.state |= CHK_ST_USE_SMALL_BUFF;
|
s->check.state |= CHK_ST_USE_SMALL_BUFF;
|
||||||
|
|
||||||
if (s->check.state & CHK_ST_CONFIGURED) {
|
if (s->check.state & CHK_ST_CONFIGURED) {
|
||||||
|
|
@ -1799,6 +1816,9 @@ int init_srv_check(struct server *srv)
|
||||||
if (!srv->do_check || !(srv->proxy->cap & PR_CAP_BE))
|
if (!srv->do_check || !(srv->proxy->cap & PR_CAP_BE))
|
||||||
goto out;
|
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;
|
check_type = srv->check.tcpcheck->rs->flags & TCPCHK_RULES_PROTO_CHK;
|
||||||
|
|
||||||
if (!(srv->flags & SRV_F_DYNAMIC)) {
|
if (!(srv->flags & SRV_F_DYNAMIC)) {
|
||||||
|
|
@ -1939,7 +1959,7 @@ int init_srv_check(struct server *srv)
|
||||||
}
|
}
|
||||||
|
|
||||||
init:
|
init:
|
||||||
err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
|
err = init_check(&srv->check, srv->check.type ? srv->check.type : (srv->proxy->options2 & PR_O2_CHK_ANY));
|
||||||
if (err) {
|
if (err) {
|
||||||
ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
|
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);
|
proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
|
||||||
|
|
|
||||||
|
|
@ -145,14 +145,15 @@ struct buffer *get_small_trash_chunk(void)
|
||||||
|
|
||||||
/* Returns a trash chunk accordingly to the requested size. This function may
|
/* 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
|
* fail if the requested size is too big or if the large chunks are not
|
||||||
* configured.
|
* 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.
|
||||||
*/
|
*/
|
||||||
struct buffer *get_trash_chunk_sz(size_t size)
|
struct buffer *get_trash_chunk_sz(size_t size)
|
||||||
{
|
{
|
||||||
if (likely(size > small_trash_size && size <= trash_size))
|
if (likely(size <= trash_size))
|
||||||
return get_trash_chunk();
|
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)
|
else if (large_trash_size && size <= large_trash_size)
|
||||||
return get_large_trash_chunk();
|
return get_large_trash_chunk();
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1151,8 +1151,13 @@ int cli_parse_cmdline(struct appctx *appctx)
|
||||||
*/
|
*/
|
||||||
if (len-1 == strlen(appctx->cli_ctx.payload_pat)) {
|
if (len-1 == strlen(appctx->cli_ctx.payload_pat)) {
|
||||||
if (strncmp(str, appctx->cli_ctx.payload_pat, len-1) == 0) {
|
if (strncmp(str, appctx->cli_ctx.payload_pat, len-1) == 0) {
|
||||||
/* end of payload was reached, rewind before the previous \n and replace it by a \0 */
|
/* end of payload was reached, rewind before the previous \n, if any, and replace it by a \0
|
||||||
b_sub(buf, strlen(appctx->cli_ctx.payload_pat) + 2);
|
* Otherwise, the payload is empty, just
|
||||||
|
*/
|
||||||
|
if (b_data(buf) > len)
|
||||||
|
b_sub(buf, len+1);
|
||||||
|
else
|
||||||
|
b_sub(buf, len);
|
||||||
*b_tail(buf) = '\0';
|
*b_tail(buf) = '\0';
|
||||||
appctx->st1 &= ~APPCTX_CLI_ST1_PAYLOAD;
|
appctx->st1 &= ~APPCTX_CLI_ST1_PAYLOAD;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake)
|
||||||
* information to create one, typically from the ALPN. If we're
|
* information to create one, typically from the ALPN. If we're
|
||||||
* done with the handshake, attempt to create one.
|
* done with the handshake, attempt to create one.
|
||||||
*/
|
*/
|
||||||
if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_QMUX_RECV|CO_FL_QMUX_SEND))) {
|
if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_WAIT_XPRT_L6))) {
|
||||||
ret = conn_create_mux(conn, NULL);
|
ret = conn_create_mux(conn, NULL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -847,6 +847,43 @@ int xprt_add_hs(struct connection *conn)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Activates an <xprt> layer on top of <conn> connection. This handshake layer
|
||||||
|
* should be designed to work on top of the layer 6. If SSL is active and its
|
||||||
|
* handshake still in progress, this function does nothing.
|
||||||
|
*
|
||||||
|
* Returns 0 on success else a negative error code.
|
||||||
|
*/
|
||||||
|
int xprt_add_l6hs(struct connection *conn, int xprt)
|
||||||
|
{
|
||||||
|
const struct xprt_ops *ops = xprt_get(xprt);
|
||||||
|
void *ops_ctx = NULL;
|
||||||
|
|
||||||
|
/* Only QMux is supported as handshake on top of layer6 for now. */
|
||||||
|
BUG_ON(xprt != XPRT_QMUX);
|
||||||
|
|
||||||
|
if (conn->flags & CO_FL_ERROR)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Do nothing if SSL is in used but handshake still in progress. In
|
||||||
|
* this case, xprt layer will be added on handshake completion.
|
||||||
|
*/
|
||||||
|
if (conn->xprt == xprt_get(XPRT_SSL) &&
|
||||||
|
(conn->flags & CO_FL_WAIT_L6_CONN)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ops->init(conn, &ops_ctx))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ops->add_xprt(conn, ops_ctx, conn->xprt_ctx, conn->xprt, NULL, NULL);
|
||||||
|
conn->xprt = ops;
|
||||||
|
conn->xprt_ctx = ops_ctx;
|
||||||
|
/* Reset XPRT READY flag before the next conn_xprt_start(). */
|
||||||
|
conn->flags &= ~CO_FL_XPRT_READY;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* returns a short name for an error, typically the same as the enum name
|
/* returns a short name for an error, typically the same as the enum name
|
||||||
* without the "CO_ER_" prefix, or an empty string for no error (better eye
|
* without the "CO_ER_" prefix, or an empty string for no error (better eye
|
||||||
* catching in logs). This is more compact for some debug cases.
|
* catching in logs). This is more compact for some debug cases.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
/* cpu_policy_conf flags */
|
/* cpu_policy_conf flags */
|
||||||
#define CPU_POLICY_ONE_THREAD_PER_CORE (1 << 0)
|
#define CPU_POLICY_ONE_THREAD_PER_CORE (1 << 0)
|
||||||
|
#define CPU_POLICY_SET_IN_CONFIG (1 << 1)
|
||||||
|
|
||||||
/* cpu_policy_conf affinities */
|
/* cpu_policy_conf affinities */
|
||||||
#define CPU_AFFINITY_PER_GROUP (1 << 0)
|
#define CPU_AFFINITY_PER_GROUP (1 << 0)
|
||||||
|
|
@ -1106,14 +1107,23 @@ static int cpu_policy_first_usable_node(int policy, int tmin, int tmax, int gmin
|
||||||
int grp, thr;
|
int grp, thr;
|
||||||
int thr_count = 0;
|
int thr_count = 0;
|
||||||
|
|
||||||
if (!global.numa_cpu_mapping)
|
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");
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (global.nbthread)
|
if (global.nbthread) {
|
||||||
|
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||||
|
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (cpu_mask_forced)
|
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");
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* determine first and second nodes with usable CPUs */
|
/* determine first and second nodes with usable CPUs */
|
||||||
for (cpu = 0; cpu <= cpu_topo_lastcpu; cpu++) {
|
for (cpu = 0; cpu <= cpu_topo_lastcpu; cpu++) {
|
||||||
|
|
@ -1269,7 +1279,7 @@ static int cpu_policy_first_usable_node(int policy, int tmin, int tmax, int gmin
|
||||||
if (tmin <= thr_count && thr_count < tmax)
|
if (tmin <= thr_count && thr_count < tmax)
|
||||||
tmax = thr_count;
|
tmax = thr_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);
|
ha_diag_notice("Multi-socket cpu detected, automatically binding on active CPUs of '%d' (%u active cpu(s))\n", first_node_id, cpu_count);
|
||||||
|
|
||||||
if (!global.nbthread)
|
if (!global.nbthread)
|
||||||
global.nbthread = tmax;
|
global.nbthread = tmax;
|
||||||
|
|
@ -1505,11 +1515,17 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
|
||||||
int cid;
|
int cid;
|
||||||
int div;
|
int div;
|
||||||
|
|
||||||
if (global.nbthread)
|
if (global.nbthread) {
|
||||||
|
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||||
|
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (global.nbtgroups)
|
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;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ha_cpuset_zero(&visited_cl_set);
|
ha_cpuset_zero(&visited_cl_set);
|
||||||
|
|
||||||
|
|
@ -1520,7 +1536,8 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
|
||||||
div = ha_cpu_policy[policy].arg;
|
div = ha_cpu_policy[policy].arg;
|
||||||
div = div ? div : 1;
|
div = div ? div : 1;
|
||||||
|
|
||||||
while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS) {
|
while (global.nbtgroups < MAX_TGROUPS && (global.nbthread < MAX_THREADS) &&
|
||||||
|
(global.thread_limit == 0 || global.nbthread < global.thread_limit)) {
|
||||||
ha_cpuset_zero(&node_cpu_set);
|
ha_cpuset_zero(&node_cpu_set);
|
||||||
ha_cpuset_zero(&touse_tsid);
|
ha_cpuset_zero(&touse_tsid);
|
||||||
ha_cpuset_zero(&touse_ccx);
|
ha_cpuset_zero(&touse_ccx);
|
||||||
|
|
@ -1550,6 +1567,10 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
|
||||||
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
|
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
|
||||||
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
|
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
|
||||||
thr_count++;
|
thr_count++;
|
||||||
|
|
||||||
|
if (global.thread_limit != 0 &&
|
||||||
|
thr_count + global.nbthread >= global.thread_limit)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now cid = next cluster_id or -1 if none; cpu_count is the
|
/* now cid = next cluster_id or -1 if none; cpu_count is the
|
||||||
|
|
@ -1565,9 +1586,9 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.nbthread)
|
if (global.nbthread)
|
||||||
ha_diag_warning("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
ha_diag_notice("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
||||||
else
|
else
|
||||||
ha_diag_warning("Could not determine any CPU cluster\n");
|
ha_diag_notice("Could not determine any CPU cluster\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1593,11 +1614,17 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
|
||||||
int l3id;
|
int l3id;
|
||||||
int div;
|
int div;
|
||||||
|
|
||||||
if (global.nbthread)
|
if (global.nbthread) {
|
||||||
|
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||||
|
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (global.nbtgroups)
|
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;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ha_cpuset_zero(&visited_ccx_set);
|
ha_cpuset_zero(&visited_ccx_set);
|
||||||
|
|
||||||
|
|
@ -1608,7 +1635,8 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
|
||||||
div = ha_cpu_policy[policy].arg;
|
div = ha_cpu_policy[policy].arg;
|
||||||
div = div ? div : 1;
|
div = div ? div : 1;
|
||||||
|
|
||||||
while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS) {
|
while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS &&
|
||||||
|
(global.thread_limit == 0 || global.nbthread < global.thread_limit)) {
|
||||||
ha_cpuset_zero(&node_cpu_set);
|
ha_cpuset_zero(&node_cpu_set);
|
||||||
ha_cpuset_zero(&touse_tsid);
|
ha_cpuset_zero(&touse_tsid);
|
||||||
ha_cpuset_zero(&touse_ccx);
|
ha_cpuset_zero(&touse_ccx);
|
||||||
|
|
@ -1638,6 +1666,9 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
|
||||||
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
|
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
|
||||||
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
|
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
|
||||||
thr_count++;
|
thr_count++;
|
||||||
|
if (global.thread_limit != 0 &&
|
||||||
|
global.nbthread + thr_count >= global.thread_limit)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now l3id = next L3 ID or -1 if none; cpu_count is the
|
/* now l3id = next L3 ID or -1 if none; cpu_count is the
|
||||||
|
|
@ -1653,9 +1684,9 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.nbthread)
|
if (global.nbthread)
|
||||||
ha_diag_warning("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
ha_diag_notice("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
||||||
else
|
else
|
||||||
ha_diag_warning("Could not determine any CPU cluster\n");
|
ha_diag_notice("Could not determine any CPU cluster\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1672,8 +1703,17 @@ static int cpu_policy_performance(int policy, int tmin, int tmax, int gmin, int
|
||||||
int cpu, cluster;
|
int cpu, cluster;
|
||||||
int capa;
|
int capa;
|
||||||
|
|
||||||
if (global.nbthread || global.nbtgroups)
|
if (global.nbthread) {
|
||||||
|
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||||
|
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||||
return 0;
|
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 */
|
/* sort clusters by average reverse capacity */
|
||||||
cpu_cluster_reorder_by_avg_capa(ha_cpu_clusters, cpu_topo_maxcpus);
|
cpu_cluster_reorder_by_avg_capa(ha_cpu_clusters, cpu_topo_maxcpus);
|
||||||
|
|
@ -1717,8 +1757,17 @@ static int cpu_policy_efficiency(int policy, int tmin, int tmax, int gmin, int g
|
||||||
int cpu, cluster;
|
int cpu, cluster;
|
||||||
int capa;
|
int capa;
|
||||||
|
|
||||||
if (global.nbthread || global.nbtgroups)
|
if (global.nbthread) {
|
||||||
|
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||||
|
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||||
return 0;
|
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 */
|
/* sort clusters by average reverse capacity */
|
||||||
cpu_cluster_reorder_by_avg_capa(ha_cpu_clusters, cpu_topo_maxcpus);
|
cpu_cluster_reorder_by_avg_capa(ha_cpu_clusters, cpu_topo_maxcpus);
|
||||||
|
|
@ -1759,8 +1808,17 @@ static int cpu_policy_resource(int policy, int tmin, int tmax, int gmin, int gma
|
||||||
int cpu, cluster;
|
int cpu, cluster;
|
||||||
int capa;
|
int capa;
|
||||||
|
|
||||||
if (global.nbthread || global.nbtgroups)
|
if (global.nbthread) {
|
||||||
|
if (cpu_policy_conf.flags & CPU_POLICY_SET_IN_CONFIG)
|
||||||
|
ha_notice("cpu-policy is ignored when nbthreads is set.\n");
|
||||||
return 0;
|
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 */
|
/* sort clusters by reverse capacity */
|
||||||
cpu_cluster_reorder_by_capa(ha_cpu_clusters, cpu_topo_maxcpus);
|
cpu_cluster_reorder_by_capa(ha_cpu_clusters, cpu_topo_maxcpus);
|
||||||
|
|
@ -1795,6 +1853,8 @@ int cpu_apply_policy(int tmin, int tmax, int gmin, int gmax, char **err)
|
||||||
|
|
||||||
if (cpu_map_configured()) {
|
if (cpu_map_configured()) {
|
||||||
/* nothing to do */
|
/* 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2350,6 +2410,7 @@ static int cfg_parse_cpu_policy(char **args, int section_type, struct proxy *cur
|
||||||
for (i = 0; ha_cpu_policy[i].name; i++) {
|
for (i = 0; ha_cpu_policy[i].name; i++) {
|
||||||
if (strcmp(args[1], ha_cpu_policy[i].name) == 0) {
|
if (strcmp(args[1], ha_cpu_policy[i].name) == 0) {
|
||||||
cpu_policy_conf.cpu_policy = i;
|
cpu_policy_conf.cpu_policy = i;
|
||||||
|
cpu_policy_conf.flags |= CPU_POLICY_SET_IN_CONFIG;
|
||||||
return 0;
|
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 *dict_insert(struct dict *d, char *s)
|
||||||
{
|
{
|
||||||
struct dict_entry *de;
|
struct dict_entry *de, *tree_de;
|
||||||
struct ebpt_node *n;
|
struct ebpt_node *n;
|
||||||
|
|
||||||
HA_RWLOCK_RDLOCK(DICT_LOCK, &d->rwlock);
|
HA_RWLOCK_RDLOCK(DICT_LOCK, &d->rwlock);
|
||||||
|
|
@ -97,13 +97,18 @@ struct dict_entry *dict_insert(struct dict *d, char *s)
|
||||||
|
|
||||||
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
||||||
n = ebis_insert(&d->values, &de->value);
|
n = ebis_insert(&d->values, &de->value);
|
||||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
tree_de = container_of(n, struct dict_entry, value);
|
||||||
if (n != &de->value) {
|
if (tree_de == de)
|
||||||
|
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||||
|
else {
|
||||||
|
/* another entry was already there, we'll return it, kill
|
||||||
|
* ours and bump the other's refcount before returning it.
|
||||||
|
*/
|
||||||
|
HA_ATOMIC_INC(&tree_de->refcount);
|
||||||
|
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||||
free_dict_entry(de);
|
free_dict_entry(de);
|
||||||
de = container_of(n, struct dict_entry, value);
|
|
||||||
}
|
}
|
||||||
|
return tree_de;
|
||||||
return de;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -117,10 +122,11 @@ void dict_entry_unref(struct dict *d, struct dict_entry *de)
|
||||||
if (!de)
|
if (!de)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
||||||
|
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0) {
|
||||||
|
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
ebpt_delete(&de->value);
|
ebpt_delete(&de->value);
|
||||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||||
|
|
||||||
|
|
|
||||||
14
src/errors.c
14
src/errors.c
|
|
@ -386,6 +386,20 @@ 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.
|
* Displays the message on stderr with the pid.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@ static struct event_hdl_sub_type_map event_hdl_sub_type_map[] = {
|
||||||
{"PAT_REF_SET", EVENT_HDL_SUB_PAT_REF_SET},
|
{"PAT_REF_SET", EVENT_HDL_SUB_PAT_REF_SET},
|
||||||
{"PAT_REF_COMMIT", EVENT_HDL_SUB_PAT_REF_COMMIT},
|
{"PAT_REF_COMMIT", EVENT_HDL_SUB_PAT_REF_COMMIT},
|
||||||
{"PAT_REF_CLEAR", EVENT_HDL_SUB_PAT_REF_CLEAR},
|
{"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) */
|
/* internal types (only used in this file) */
|
||||||
|
|
|
||||||
30
src/h1.c
30
src/h1.c
|
|
@ -316,6 +316,9 @@ void h1_parse_upgrade_header(struct h1m *h1m, struct ist *value)
|
||||||
skip_val:
|
skip_val:
|
||||||
word.ptr = p = n;
|
word.ptr = p = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (istlen(*value))
|
||||||
|
h1m->flags |= H1_MF_UPG_HDR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Macros used in the HTTP/1 parser, to check for the expected presence of
|
/* Macros used in the HTTP/1 parser, to check for the expected presence of
|
||||||
|
|
@ -710,6 +713,16 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
||||||
case H1_MSG_RPCODE:
|
case H1_MSG_RPCODE:
|
||||||
http_msg_rpcode:
|
http_msg_rpcode:
|
||||||
if (likely(HTTP_IS_DIGIT(*ptr))) {
|
if (likely(HTTP_IS_DIGIT(*ptr))) {
|
||||||
|
if (ptr - sl.st.c.ptr >= 3) {
|
||||||
|
/* more than 3 digits */
|
||||||
|
if (h1m->err_pos == -1) /* only capture the error pointer */
|
||||||
|
h1m->err_pos = ptr - start + skip;
|
||||||
|
else if (h1m->err_pos < -1 || sl.st.status >= ((uint16_t)~0 - 9) / 10) {
|
||||||
|
/* strict checks or risk of overflow */
|
||||||
|
state = H1_MSG_RPCODE;
|
||||||
|
goto http_msg_invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
sl.st.status = sl.st.status * 10 + *ptr - '0';
|
sl.st.status = sl.st.status * 10 + *ptr - '0';
|
||||||
EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpcode, http_msg_ood, state, H1_MSG_RPCODE);
|
EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpcode, http_msg_ood, state, H1_MSG_RPCODE);
|
||||||
}
|
}
|
||||||
|
|
@ -952,6 +965,20 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
||||||
goto http_output_full;
|
goto http_output_full;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Skip headers whose names contain forbidden
|
||||||
|
* chars. When any is detected, h1m->err_pos >= 0,
|
||||||
|
* so we recheck the name only when an error was
|
||||||
|
* detected.
|
||||||
|
*/
|
||||||
|
if (unlikely(h1m->err_pos >= 0)) {
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < n.len && HTTP_IS_TOKEN(n.ptr[i]))
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (i < n.len)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (isteqi(n, ist("transfer-encoding"))) {
|
if (isteqi(n, ist("transfer-encoding"))) {
|
||||||
ret = h1_parse_xfer_enc_header(h1m, v);
|
ret = h1_parse_xfer_enc_header(h1m, v);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
@ -1248,9 +1275,10 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
||||||
void h1_generate_random_ws_input_key(char key_out[25])
|
void h1_generate_random_ws_input_key(char key_out[25])
|
||||||
{
|
{
|
||||||
/* generate a random websocket key */
|
/* generate a random websocket key */
|
||||||
const uint64_t rand1 = ha_random64(), rand2 = ha_random64();
|
uint64_t rand1, rand2;
|
||||||
char key[16];
|
char key[16];
|
||||||
|
|
||||||
|
ha_random64_pair_hashed(&rand1, &rand2);
|
||||||
memcpy(key, &rand1, 8);
|
memcpy(key, &rand1, 8);
|
||||||
memcpy(&key[8], &rand2, 8);
|
memcpy(&key[8], &rand2, 8);
|
||||||
a2base64(key, 16, key_out, 25);
|
a2base64(key, 16, key_out, 25);
|
||||||
|
|
|
||||||
27
src/h1_htx.c
27
src/h1_htx.c
|
|
@ -162,6 +162,8 @@ static unsigned int h1m_htx_sl_flags(struct h1m *h1m)
|
||||||
}
|
}
|
||||||
if (h1m->flags & H1_MF_CONN_UPG)
|
if (h1m->flags & H1_MF_CONN_UPG)
|
||||||
flags |= HTX_SL_F_CONN_UPG;
|
flags |= HTX_SL_F_CONN_UPG;
|
||||||
|
if (h1m->flags & H1_MF_UPG_HDR)
|
||||||
|
flags |= HTX_SL_F_UPG_HDR;
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,6 +215,31 @@ 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);
|
flags |= h1m_htx_sl_flags(h1m);
|
||||||
|
|
||||||
sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);
|
sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);
|
||||||
|
|
|
||||||
193
src/h3.c
193
src/h3.c
|
|
@ -92,6 +92,10 @@ static const struct name_desc h3_trace_decoding[] = {
|
||||||
{ .name="clean", .desc="only user-friendly stuff, generally suitable for level \"user\"" },
|
{ .name="clean", .desc="only user-friendly stuff, generally suitable for level \"user\"" },
|
||||||
#define H3_VERB_MINIMAL 2
|
#define H3_VERB_MINIMAL 2
|
||||||
{ .name="minimal", .desc="report only qcc/qcs state and flags, no real decoding" },
|
{ .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 */ }
|
{ /* end */ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -212,8 +216,33 @@ static ssize_t h3_init_uni_stream(struct h3c *h3c, struct qcs *qcs,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case H3_UNI_S_T_PUSH:
|
case H3_UNI_S_T_PUSH:
|
||||||
/* TODO not supported for the moment */
|
if (!conn_is_back(qcs->qcc->conn)) {
|
||||||
h3s->type = H3S_T_PUSH;
|
/* 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;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case H3_UNI_S_T_QPACK_DEC:
|
case H3_UNI_S_T_QPACK_DEC:
|
||||||
|
|
@ -365,7 +394,6 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
|
||||||
|
|
||||||
case H3_FT_CANCEL_PUSH:
|
case H3_FT_CANCEL_PUSH:
|
||||||
case H3_FT_GOAWAY:
|
case H3_FT_GOAWAY:
|
||||||
case H3_FT_MAX_PUSH_ID:
|
|
||||||
/* RFC 9114 7.2.3. CANCEL_PUSH
|
/* RFC 9114 7.2.3. CANCEL_PUSH
|
||||||
*
|
*
|
||||||
* A CANCEL_PUSH frame is sent on the control stream. Receiving a
|
* A CANCEL_PUSH frame is sent on the control stream. Receiving a
|
||||||
|
|
@ -379,13 +407,6 @@ 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.
|
* 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)
|
if (h3s->type != H3S_T_CTRL)
|
||||||
ret = H3_ERR_FRAME_UNEXPECTED;
|
ret = H3_ERR_FRAME_UNEXPECTED;
|
||||||
else if (!(h3c->flags & H3_CF_SETTINGS_RECV))
|
else if (!(h3c->flags & H3_CF_SETTINGS_RECV))
|
||||||
|
|
@ -412,14 +433,33 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
|
||||||
|
|
||||||
case H3_FT_PUSH_PROMISE:
|
case H3_FT_PUSH_PROMISE:
|
||||||
/* RFC 9114 7.2.5. PUSH_PROMISE
|
/* RFC 9114 7.2.5. PUSH_PROMISE
|
||||||
|
*
|
||||||
|
* If a PUSH_PROMISE frame is received on the control stream, the client
|
||||||
|
* MUST respond with a connection error of type H3_FRAME_UNEXPECTED.
|
||||||
*
|
*
|
||||||
* A client MUST NOT send a PUSH_PROMISE frame. A server MUST treat the
|
* A client MUST NOT send a PUSH_PROMISE frame. A server MUST treat the
|
||||||
* receipt of a PUSH_PROMISE frame as a connection error of type
|
* receipt of a PUSH_PROMISE frame as a connection error of type
|
||||||
* H3_FRAME_UNEXPECTED.
|
* H3_FRAME_UNEXPECTED.
|
||||||
*/
|
*/
|
||||||
|
if (h3s->type == H3S_T_CTRL || !conn_is_back(qcs->qcc->conn))
|
||||||
|
ret = H3_ERR_FRAME_UNEXPECTED;
|
||||||
|
break;
|
||||||
|
|
||||||
/* TODO server-side only. */
|
case H3_FT_MAX_PUSH_ID:
|
||||||
ret = H3_ERR_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.
|
||||||
|
*
|
||||||
|
* A server MUST NOT send a MAX_PUSH_ID frame. A client MUST treat the
|
||||||
|
* receipt of a MAX_PUSH_ID frame as a connection error of type
|
||||||
|
* H3_FRAME_UNEXPECTED.
|
||||||
|
*/
|
||||||
|
if (h3s->type != H3S_T_CTRL || conn_is_back(qcs->qcc->conn))
|
||||||
|
ret = H3_ERR_FRAME_UNEXPECTED;
|
||||||
|
else if (!(h3c->flags & H3_CF_SETTINGS_RECV))
|
||||||
|
ret = H3_ERR_MISSING_SETTINGS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -594,6 +634,51 @@ static struct ist _h3_trim_header(struct ist value)
|
||||||
return v;
|
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
|
/* 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
|
* 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.
|
* set if this is the last data to transfer from this stream.
|
||||||
|
|
@ -659,6 +744,12 @@ static ssize_t h3_req_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||||
goto out;
|
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)) {
|
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);
|
TRACE_ERROR("HTX buffer alloc failure", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
|
||||||
len = -1;
|
len = -1;
|
||||||
|
|
@ -1147,6 +1238,13 @@ static ssize_t h3_resp_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
||||||
goto out;
|
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))) {
|
if (!(appbuf = qcc_get_stream_rxbuf(qcs))) {
|
||||||
TRACE_ERROR("buffer alloc failure", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
|
TRACE_ERROR("buffer alloc failure", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
|
||||||
len = -1;
|
len = -1;
|
||||||
|
|
@ -1930,6 +2028,25 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
||||||
h3s->st_req = H3S_ST_REQ_TRAILERS;
|
h3s->st_req = H3S_ST_REQ_TRAILERS;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case H3_FT_CANCEL_PUSH:
|
||||||
|
if (!conn_is_back(qcs->qcc->conn)) {
|
||||||
|
/* RFC 9114 7.2.3. CANCEL_PUSH
|
||||||
|
*
|
||||||
|
* If a server receives a CANCEL_PUSH frame for a push ID
|
||||||
|
* that has not yet been mentioned by a PUSH_PROMISE frame, this MUST be
|
||||||
|
* treated as a connection error of type H3_ID_ERROR.
|
||||||
|
*/
|
||||||
|
TRACE_ERROR("reject CANCEL_PUSH from client", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
|
||||||
|
qcc_set_error(qcs->qcc, H3_ERR_ID_ERROR, 1,
|
||||||
|
muxc_tevt_type_proto_err);
|
||||||
|
qcc_report_glitch(qcs->qcc, 1);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Not supported */
|
||||||
|
ret = flen;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case H3_FT_GOAWAY:
|
case H3_FT_GOAWAY:
|
||||||
ret = h3_parse_goaway_frm(qcs->qcc->ctx, b, flen);
|
ret = h3_parse_goaway_frm(qcs->qcc->ctx, b, flen);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
@ -1938,12 +2055,6 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case H3_FT_CANCEL_PUSH:
|
|
||||||
case H3_FT_PUSH_PROMISE:
|
|
||||||
case H3_FT_MAX_PUSH_ID:
|
|
||||||
/* Not supported */
|
|
||||||
ret = flen;
|
|
||||||
break;
|
|
||||||
case H3_FT_SETTINGS:
|
case H3_FT_SETTINGS:
|
||||||
ret = h3_parse_settings_frm(qcs->qcc->ctx, b, flen);
|
ret = h3_parse_settings_frm(qcs->qcc->ctx, b, flen);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
@ -1953,6 +2064,26 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
||||||
}
|
}
|
||||||
h3c->flags |= H3_CF_SETTINGS_RECV;
|
h3c->flags |= H3_CF_SETTINGS_RECV;
|
||||||
break;
|
break;
|
||||||
|
case H3_FT_PUSH_PROMISE:
|
||||||
|
/* h3_check_frame_valid() must reject on server side. */
|
||||||
|
BUG_ON(!conn_is_back(qcs->qcc->conn));
|
||||||
|
|
||||||
|
/* RFC 9114 7.2.5. PUSH_PROMISE
|
||||||
|
*
|
||||||
|
* A client MUST treat
|
||||||
|
* receipt of a PUSH_PROMISE frame that contains a larger push ID than
|
||||||
|
* the client has advertised as a connection error of H3_ID_ERROR.
|
||||||
|
*/
|
||||||
|
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:
|
default:
|
||||||
/* RFC 9114 Section 9. Extensions to HTTP/3
|
/* RFC 9114 Section 9. Extensions to HTTP/3
|
||||||
*
|
*
|
||||||
|
|
@ -2191,6 +2322,7 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx)
|
||||||
|
|
||||||
if (qpack_encode_method(&headers_buf, sl->info.req.meth, meth))
|
if (qpack_encode_method(&headers_buf, sl->info.req.meth, meth))
|
||||||
goto err_full;
|
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] != '*') {
|
if (uri.ptr[0] != '/' && uri.ptr[0] != '*') {
|
||||||
int len = 1;
|
int len = 1;
|
||||||
|
|
@ -2222,13 +2354,23 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx)
|
||||||
|
|
||||||
if (qpack_encode_scheme(&headers_buf, scheme))
|
if (qpack_encode_scheme(&headers_buf, scheme))
|
||||||
goto err_full;
|
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))
|
if (qpack_encode_path(&headers_buf, uri))
|
||||||
goto err_full;
|
goto err_full;
|
||||||
|
h3_trace_header(ist(":path"), uri, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||||
|
|
||||||
if (istlen(auth)) {
|
if (istlen(auth)) {
|
||||||
if (qpack_encode_auth(&headers_buf, auth))
|
if (qpack_encode_auth(&headers_buf, auth))
|
||||||
goto err_full;
|
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)) {
|
if (!(sl->flags & HTX_SL_F_XFER_LEN)) {
|
||||||
|
|
@ -2365,6 +2507,13 @@ 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);
|
TRACE_USER("handling final HTX response", H3_EV_STRM_SEND, qcs->qcc->conn, qcs);
|
||||||
h3s->flags &= ~H3_SF_SENT_INTERIM;
|
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) {
|
else if (type == HTX_BLK_HDR) {
|
||||||
if (unlikely(hdr >= sizeof(list) / sizeof(list[0]) - 1)) {
|
if (unlikely(hdr >= sizeof(list) / sizeof(list[0]) - 1)) {
|
||||||
|
|
@ -2382,6 +2531,14 @@ 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
|
/* Current function expects HTX start-line to be present. This also
|
||||||
* ensures <status> conformance has been checked prior to encoding it.
|
* ensures <status> conformance has been checked prior to encoding it.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
109
src/haproxy.c
109
src/haproxy.c
|
|
@ -210,7 +210,6 @@ struct global global = {
|
||||||
#endif
|
#endif
|
||||||
/* by default allow clients which use a privileged port for TCP only */
|
/* by default allow clients which use a privileged port for TCP only */
|
||||||
.clt_privileged_ports = HA_PROTO_TCP,
|
.clt_privileged_ports = HA_PROTO_TCP,
|
||||||
.maxthrpertgroup = DEF_MAX_THREADS_PER_GROUP,
|
|
||||||
/* others NULL OK */
|
/* others NULL OK */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -275,6 +274,7 @@ unsigned int deprecated_directives_allowed = 0;
|
||||||
/* mapped storage for collected libs */
|
/* mapped storage for collected libs */
|
||||||
void *lib_storage = NULL;
|
void *lib_storage = NULL;
|
||||||
size_t lib_size = 0;
|
size_t lib_size = 0;
|
||||||
|
char *lib_output_file = NULL;
|
||||||
|
|
||||||
int check_kw_experimental(struct cfg_keyword *kw, const char *file, int linenum,
|
int check_kw_experimental(struct cfg_keyword *kw, const char *file, int linenum,
|
||||||
char **errmsg)
|
char **errmsg)
|
||||||
|
|
@ -785,6 +785,9 @@ static void usage(char *name)
|
||||||
#if defined(HA_HAVE_DUMP_LIBS)
|
#if defined(HA_HAVE_DUMP_LIBS)
|
||||||
" -dL dumps loaded object files after config checks\n"
|
" -dL dumps loaded object files after config checks\n"
|
||||||
#endif
|
#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)
|
#if defined(USE_CPU_AFFINITY)
|
||||||
" -dc dumps the list of selected and evicted CPUs\n"
|
" -dc dumps the list of selected and evicted CPUs\n"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1628,6 +1631,16 @@ void haproxy_init_args(int argc, char **argv)
|
||||||
#if defined(HA_HAVE_DUMP_LIBS)
|
#if defined(HA_HAVE_DUMP_LIBS)
|
||||||
else if (*flag == 'd' && flag[1] == 'L')
|
else if (*flag == 'd' && flag[1] == 'L')
|
||||||
arg_mode |= MODE_DUMP_LIBS;
|
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
|
#endif
|
||||||
else if (*flag == 'd' && flag[1] == 'K') {
|
else if (*flag == 'd' && flag[1] == 'K') {
|
||||||
arg_mode |= MODE_DUMP_KWD;
|
arg_mode |= MODE_DUMP_KWD;
|
||||||
|
|
@ -1926,20 +1939,30 @@ static void dump_registered_keywords(void)
|
||||||
|
|
||||||
/* Generate a random cluster-secret in case the setting is not provided in the
|
/* Generate a random cluster-secret in case the setting is not provided in the
|
||||||
* configuration. This allows to use features which rely on it albeit with some
|
* configuration. This allows to use features which rely on it albeit with some
|
||||||
* limitations.
|
* limitations. The function prefers RAND_bytes() if available, otherwise falls
|
||||||
|
* back to ha_random64_pair_hashed().
|
||||||
*/
|
*/
|
||||||
static void generate_random_cluster_secret()
|
static void generate_random_cluster_secret()
|
||||||
{
|
{
|
||||||
/* used as a default random cluster-secret if none defined. */
|
/* used as a default random cluster-secret if none defined. */
|
||||||
uint64_t rand;
|
union {
|
||||||
|
uint64_t by64[2];
|
||||||
|
uchar by8[16];
|
||||||
|
} rand;
|
||||||
|
|
||||||
/* The caller must not overwrite an already defined secret. */
|
/* The caller must not overwrite an already defined secret. */
|
||||||
BUG_ON(cluster_secret_isset);
|
BUG_ON(cluster_secret_isset);
|
||||||
|
BUG_ON(sizeof(global.cluster_secret) != sizeof(rand));
|
||||||
|
|
||||||
|
#ifdef USE_OPENSSL
|
||||||
|
if (RAND_bytes(rand.by8, sizeof(rand.by8)) != 1)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/* no SSL or not working, fall back to other sources */
|
||||||
|
ha_random64_pair_hashed(&rand.by64[0], &rand.by64[1]);
|
||||||
|
}
|
||||||
|
|
||||||
rand = ha_random64();
|
|
||||||
memcpy(global.cluster_secret, &rand, sizeof(rand));
|
memcpy(global.cluster_secret, &rand, sizeof(rand));
|
||||||
rand = ha_random64();
|
|
||||||
memcpy(global.cluster_secret + sizeof(rand), &rand, sizeof(rand));
|
|
||||||
cluster_secret_isset = 1;
|
cluster_secret_isset = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2129,9 +2152,6 @@ static void step_init_1()
|
||||||
if (init_acl() != 0)
|
if (init_acl() != 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
/* Initialise lua. */
|
|
||||||
hlua_init();
|
|
||||||
|
|
||||||
/* set modes given from cmdline */
|
/* set modes given from cmdline */
|
||||||
global.mode |= (arg_mode & (MODE_DAEMON | MODE_MWORKER | MODE_FOREGROUND | MODE_VERBOSE
|
global.mode |= (arg_mode & (MODE_DAEMON | MODE_MWORKER | MODE_FOREGROUND | MODE_VERBOSE
|
||||||
| MODE_QUIET | MODE_CHECK | MODE_DEBUG | MODE_ZERO_WARNING
|
| MODE_QUIET | MODE_CHECK | MODE_DEBUG | MODE_ZERO_WARNING
|
||||||
|
|
@ -2306,6 +2326,17 @@ static void step_init_2(int argc, char** argv)
|
||||||
|
|
||||||
#if defined(HA_HAVE_DUMP_LIBS)
|
#if defined(HA_HAVE_DUMP_LIBS)
|
||||||
if (global.mode & MODE_DUMP_LIBS && !master) {
|
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");
|
qfprintf(stdout, "List of loaded object files:\n");
|
||||||
chunk_reset(&trash);
|
chunk_reset(&trash);
|
||||||
if (dump_libs(&trash, ((arg_mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_VERBOSE)))
|
if (dump_libs(&trash, ((arg_mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_VERBOSE)))
|
||||||
|
|
@ -2779,6 +2810,7 @@ void deinit(void)
|
||||||
struct cfg_postparser *pprs, *pprsb;
|
struct cfg_postparser *pprs, *pprsb;
|
||||||
char **tmp = init_env;
|
char **tmp = init_env;
|
||||||
int cur_fd;
|
int cur_fd;
|
||||||
|
int i;
|
||||||
|
|
||||||
/* the user may want to skip this phase */
|
/* the user may want to skip this phase */
|
||||||
if (global.tune.options & GTUNE_QUICK_EXIT)
|
if (global.tune.options & GTUNE_QUICK_EXIT)
|
||||||
|
|
@ -2855,8 +2887,10 @@ void deinit(void)
|
||||||
ha_free(&global.server_state_base);
|
ha_free(&global.server_state_base);
|
||||||
ha_free(&global.server_state_file);
|
ha_free(&global.server_state_file);
|
||||||
ha_free(&global.stats_file);
|
ha_free(&global.stats_file);
|
||||||
task_destroy(idle_conn_task);
|
for (i = 0; i < global.nbthread; i++) {
|
||||||
idle_conn_task = NULL;
|
task_destroy(idle_conn_task[i]);
|
||||||
|
idle_conn_task[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
list_for_each_entry_safe(log, logb, &global.loggers, list) {
|
list_for_each_entry_safe(log, logb, &global.loggers, list) {
|
||||||
LIST_DEL_INIT(&log->list);
|
LIST_DEL_INIT(&log->list);
|
||||||
|
|
@ -2950,7 +2984,7 @@ void deinit(void)
|
||||||
free(init_env);
|
free(init_env);
|
||||||
}
|
}
|
||||||
free(progname);
|
free(progname);
|
||||||
|
free_collected_libs();
|
||||||
} /* end deinit() */
|
} /* end deinit() */
|
||||||
|
|
||||||
__attribute__((noreturn)) void deinit_and_exit(int status)
|
__attribute__((noreturn)) void deinit_and_exit(int status)
|
||||||
|
|
@ -3088,6 +3122,7 @@ void *run_thread_poll_loop(void *data)
|
||||||
ha_set_thread(data);
|
ha_set_thread(data);
|
||||||
set_thread_cpu_affinity();
|
set_thread_cpu_affinity();
|
||||||
clock_set_local_source();
|
clock_set_local_source();
|
||||||
|
ha_random_seed_thread();
|
||||||
|
|
||||||
#ifdef USE_THREAD
|
#ifdef USE_THREAD
|
||||||
ha_thread_info[tid].pth_id = ha_get_pthread_id(tid);
|
ha_thread_info[tid].pth_id = ha_get_pthread_id(tid);
|
||||||
|
|
@ -3329,16 +3364,18 @@ static void setup_user_ns(uid_t euid, gid_t egid)
|
||||||
|
|
||||||
static int do_chroot(const char *prog, const char *path)
|
static int do_chroot(const char *prog, const char *path)
|
||||||
{
|
{
|
||||||
const char *chroot_dir = path;
|
const char *dir, *chroot_dir;
|
||||||
int error = 0;
|
int error, chroot_error;
|
||||||
|
|
||||||
|
error = chroot_error = 0;
|
||||||
|
dir = chroot_dir = path;
|
||||||
if (strcmp(path, "auto") == 0) {
|
if (strcmp(path, "auto") == 0) {
|
||||||
/* When "chroot auto" is used, we attempt to chroot to an
|
/* When "chroot auto" is used, we attempt to chroot to an
|
||||||
* anonymous and read-only directory.
|
* anonymous and read-only directory.
|
||||||
*/
|
*/
|
||||||
char tmpdir[] = "/tmp/haproxy.XXXXXX";
|
char tmpdir[] = "/tmp/haproxy.XXXXXX";
|
||||||
chroot_dir = mkdtemp(tmpdir);
|
dir = mkdtemp(tmpdir);
|
||||||
if (chroot_dir == NULL) {
|
if (dir == NULL) {
|
||||||
ha_alert("[%s.main()] Cannot create(%s) for chroot auto.\n",
|
ha_alert("[%s.main()] Cannot create(%s) for chroot auto.\n",
|
||||||
prog, tmpdir);
|
prog, tmpdir);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -3349,16 +3386,32 @@ static int do_chroot(const char *prog, const char *path)
|
||||||
* want to remove the directory).
|
* want to remove the directory).
|
||||||
*/
|
*/
|
||||||
DISGUISE(rmdir(tmpdir));
|
DISGUISE(rmdir(tmpdir));
|
||||||
|
chroot_dir = ".";
|
||||||
if (!error)
|
if (!error)
|
||||||
error = chroot(".");
|
chroot_error = chroot(".");
|
||||||
} else if (strcmp(path, "/") != 0) {
|
} else if (strcmp(path, "/") != 0) {
|
||||||
error = chroot(path);
|
chroot_error = chroot(path);
|
||||||
}
|
}
|
||||||
if (!error)
|
#ifdef CLONE_NEWUSER
|
||||||
|
/* If the chroot failed because of insufficient privileges and
|
||||||
|
* unshare(CLONE_NEWUSER) is available, we attempt it to gain the
|
||||||
|
* abilty to chroot as an unprivileged user. If that worked, we
|
||||||
|
* try the chroot again.
|
||||||
|
*/
|
||||||
|
if (chroot_error && errno == EPERM) {
|
||||||
|
uid_t euid = geteuid();
|
||||||
|
gid_t egid = getegid();
|
||||||
|
if (unshare(CLONE_NEWUSER) == 0) {
|
||||||
|
setup_user_ns(euid, egid);
|
||||||
|
chroot_error = chroot(chroot_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!error && !chroot_error)
|
||||||
error = chdir("/");
|
error = chdir("/");
|
||||||
|
|
||||||
if (error) {
|
if (error || chroot_error) {
|
||||||
ha_alert("[%s.main()] Cannot chroot(%s).\n", prog, chroot_dir);
|
ha_alert("[%s.main()] Cannot chroot(%s).\n", prog, dir);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3702,20 +3755,6 @@ 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 */
|
/* Must chroot and setgid/setuid in the children */
|
||||||
/* chroot if needed */
|
/* chroot if needed */
|
||||||
if (global.chroot != NULL && do_chroot(argv[0], global.chroot) != 0) {
|
if (global.chroot != NULL && do_chroot(argv[0], global.chroot) != 0) {
|
||||||
|
|
|
||||||
|
|
@ -788,7 +788,7 @@ static void hstream_parse_uri(struct ist uri, struct hstream *hs)
|
||||||
} while (*next);
|
} while (*next);
|
||||||
|
|
||||||
if (use_rand)
|
if (use_rand)
|
||||||
result = ((long long)ha_random64() * result) / ((long long)RAND_MAX + 1);
|
result = ((long long)statistical_prng() * result) / 0xFFFFFFFFU;
|
||||||
|
|
||||||
switch (*arg) {
|
switch (*arg) {
|
||||||
case 's':
|
case 's':
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <haproxy/chunk.h>
|
#include <haproxy/chunk.h>
|
||||||
#include <haproxy/errors.h>
|
#include <haproxy/errors.h>
|
||||||
#include <haproxy/global.h>
|
#include <haproxy/global.h>
|
||||||
|
#include <haproxy/openssl-compat.h>
|
||||||
#include <haproxy/version.h>
|
#include <haproxy/version.h>
|
||||||
|
|
||||||
static int haterm_debug;
|
static int haterm_debug;
|
||||||
|
|
@ -28,7 +29,9 @@ static void haterm_usage(char *name)
|
||||||
" -d : enable the traces for all http protocols\n"
|
" -d : enable the traces for all http protocols\n"
|
||||||
" -dS : disables splice() usage even when available\n"
|
" -dS : disables splice() usage even when available\n"
|
||||||
" -dZ : disable zero-copy forwarding\n"
|
" -dZ : disable zero-copy forwarding\n"
|
||||||
|
#if defined(USE_QUIC)
|
||||||
" --" QUIC_BIND_LONG_OPT " <opts> : append options to QUIC \"bind\" lines\n"
|
" --" 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"
|
" --" TCP_BIND_LONG_OPT " <opts> : append options to TCP \"bind\" lines\n"
|
||||||
, name);
|
, name);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
@ -171,7 +174,7 @@ void haproxy_init_args(int argc, char **argv)
|
||||||
struct hbuf fbuf = HBUF_NULL; // "frontend" section
|
struct hbuf fbuf = HBUF_NULL; // "frontend" section
|
||||||
struct hbuf tbuf = HBUF_NULL; // "traces" section
|
struct hbuf tbuf = HBUF_NULL; // "traces" section
|
||||||
char *bits = NULL, *curves = NULL;
|
char *bits = NULL, *curves = NULL;
|
||||||
char *quic_bind_opt = NULL, *tcp_bind_opt = NULL;
|
char *quic_bind_opt __maybe_unused = NULL, *tcp_bind_opt = NULL;
|
||||||
int sargc; /* saved argc */
|
int sargc; /* saved argc */
|
||||||
char **sargv; /* saved argv */
|
char **sargv; /* saved argv */
|
||||||
|
|
||||||
|
|
@ -203,6 +206,7 @@ void haproxy_init_args(int argc, char **argv)
|
||||||
if (*opt == '-') {
|
if (*opt == '-') {
|
||||||
/* long options */
|
/* long options */
|
||||||
opt++;
|
opt++;
|
||||||
|
#if defined(USE_QUIC)
|
||||||
if (strcmp(opt, QUIC_BIND_LONG_OPT) == 0) {
|
if (strcmp(opt, QUIC_BIND_LONG_OPT) == 0) {
|
||||||
argv++; argc--;
|
argv++; argc--;
|
||||||
if (argc <= 0 || **argv == '-')
|
if (argc <= 0 || **argv == '-')
|
||||||
|
|
@ -210,7 +214,9 @@ void haproxy_init_args(int argc, char **argv)
|
||||||
|
|
||||||
quic_bind_opt = *argv;
|
quic_bind_opt = *argv;
|
||||||
}
|
}
|
||||||
else if (strcmp(opt, TCP_BIND_LONG_OPT) == 0) {
|
else
|
||||||
|
#endif
|
||||||
|
if (strcmp(opt, TCP_BIND_LONG_OPT) == 0) {
|
||||||
argv++; argc--;
|
argv++; argc--;
|
||||||
if (argc <= 0 || **argv == '-')
|
if (argc <= 0 || **argv == '-')
|
||||||
haterm_usage(progname);
|
haterm_usage(progname);
|
||||||
|
|
@ -254,6 +260,11 @@ void haproxy_init_args(int argc, char **argv)
|
||||||
else if (*opt == 'd' && *(opt+1) == 'S') {
|
else if (*opt == 'd' && *(opt+1) == 'S') {
|
||||||
global.tune.options &= ~GTUNE_USE_SPLICE;
|
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
|
#endif
|
||||||
else if (*opt == 'd' && *(opt+1) == 'Z') {
|
else if (*opt == 'd' && *(opt+1) == 'Z') {
|
||||||
global.tune.no_zero_copy_fwd |= NO_ZERO_COPY_FWD;
|
global.tune.no_zero_copy_fwd |= NO_ZERO_COPY_FWD;
|
||||||
|
|
@ -394,20 +405,30 @@ void haproxy_init_args(int argc, char **argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* clear HTTP */
|
/* clear HTTP */
|
||||||
hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread\n", ip, port1);
|
hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread%s%s\n", ip, port1,
|
||||||
|
tcp_bind_opt ? " " : "",
|
||||||
|
tcp_bind_opt ? tcp_bind_opt : "");
|
||||||
has_bind = 1;
|
has_bind = 1;
|
||||||
if (port2) {
|
if (port2) {
|
||||||
|
#if defined(USE_OPENSSL)
|
||||||
has_ssl = 1;
|
has_ssl = 1;
|
||||||
|
|
||||||
/* SSL/TCP binding */
|
/* SSL/TCP binding */
|
||||||
hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread ssl "
|
hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread ssl "
|
||||||
"alpn h2,http1.1,http1.0"
|
"alpn h3,h2,http1.1,http1.0"
|
||||||
" crt " HATERM_RSA_CERT_NAME
|
" crt " HATERM_RSA_CERT_NAME
|
||||||
" crt " HATERM_ECDSA_CERT_NAME "%s%s\n",
|
" crt " HATERM_ECDSA_CERT_NAME "%s%s%s\n",
|
||||||
ip, port2,
|
ip, port2,
|
||||||
tcp_bind_opt ? " " : "",
|
tcp_bind_opt ? " " : "",
|
||||||
tcp_bind_opt ? tcp_bind_opt : "");
|
tcp_bind_opt ? tcp_bind_opt : "",
|
||||||
|
# if defined(USE_LINUX_SPLICE) && defined(HA_USE_KTLS)
|
||||||
|
" ktls on"
|
||||||
|
# else
|
||||||
|
"" /* no ktls */
|
||||||
|
# endif
|
||||||
|
);
|
||||||
|
|
||||||
|
# if defined(USE_QUIC)
|
||||||
/* QUIC binding */
|
/* QUIC binding */
|
||||||
hbuf_appendf(&fbuf, "\tbind %s@%s:%s shards by-thread ssl"
|
hbuf_appendf(&fbuf, "\tbind %s@%s:%s shards by-thread ssl"
|
||||||
" crt " HATERM_RSA_CERT_NAME
|
" crt " HATERM_RSA_CERT_NAME
|
||||||
|
|
@ -415,6 +436,11 @@ void haproxy_init_args(int argc, char **argv)
|
||||||
ipv6 ? "quic6" : "quic4", ip, port2,
|
ipv6 ? "quic6" : "quic4", ip, port2,
|
||||||
quic_bind_opt ? " " : "",
|
quic_bind_opt ? " " : "",
|
||||||
quic_bind_opt ? 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
|
else
|
||||||
|
|
@ -438,6 +464,12 @@ void haproxy_init_args(int argc, char **argv)
|
||||||
}
|
}
|
||||||
hbuf_appendf(&gbuf, "global\n");
|
hbuf_appendf(&gbuf, "global\n");
|
||||||
hbuf_appendf(&gbuf, "\ttune.memory.hot-size 3145728\n");
|
hbuf_appendf(&gbuf, "\ttune.memory.hot-size 3145728\n");
|
||||||
|
if (has_ssl)
|
||||||
|
hbuf_appendf(&gbuf, "\texpose-experimental-directives\n");
|
||||||
|
#if defined(USE_LINUX_SPLICE) && defined(HA_USE_KTLS)
|
||||||
|
if (has_ssl)
|
||||||
|
hbuf_appendf(&gbuf, "\ttune.pipesize 262144\n");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "global" section */
|
/* "global" section */
|
||||||
|
|
|
||||||
662
src/hlua.c
662
src/hlua.c
|
|
@ -73,6 +73,9 @@
|
||||||
#include <haproxy/event_hdl.h>
|
#include <haproxy/event_hdl.h>
|
||||||
#include <haproxy/check.h>
|
#include <haproxy/check.h>
|
||||||
#include <haproxy/mailers.h>
|
#include <haproxy/mailers.h>
|
||||||
|
#if defined(HAVE_ACME)
|
||||||
|
#include <haproxy/acme.h>
|
||||||
|
#endif /* HAVE_ACME */
|
||||||
|
|
||||||
/* Global LUA flags */
|
/* Global LUA flags */
|
||||||
|
|
||||||
|
|
@ -105,16 +108,6 @@ static uint8_t hlua_body = 1;
|
||||||
*/
|
*/
|
||||||
static uint8_t hlua_bool_sample_conversion = HLUA_BOOL_SAMPLE_CONVERSION_UNK;
|
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
|
/* This couple of function executes securely some Lua calls outside of
|
||||||
* the lua runtime environment. Each Lua call can return a longjmp
|
* the lua runtime environment. Each Lua call can return a longjmp
|
||||||
|
|
@ -176,6 +169,24 @@ 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);
|
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
|
/* 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
|
* 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).
|
* states dedicated to each thread (in this case hlua_state_id==tid+1).
|
||||||
|
|
@ -499,7 +510,6 @@ static int class_fetches_ref;
|
||||||
static int class_converters_ref;
|
static int class_converters_ref;
|
||||||
static int class_http_ref;
|
static int class_http_ref;
|
||||||
static int class_http_msg_ref;
|
static int class_http_msg_ref;
|
||||||
static int class_httpclient_ref;
|
|
||||||
static int class_map_ref;
|
static int class_map_ref;
|
||||||
static int class_applet_tcp_ref;
|
static int class_applet_tcp_ref;
|
||||||
static int class_applet_http_ref;
|
static int class_applet_http_ref;
|
||||||
|
|
@ -988,13 +998,6 @@ const char *hlua_traceback(lua_State *L, const char* sep)
|
||||||
* stack. If the number of arguments available is not the same
|
* stack. If the number of arguments available is not the same
|
||||||
* then <nb> an error is thrown.
|
* 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
|
/* This function pushes an error string prefixed by the file name
|
||||||
* and the line number where the error is encountered.
|
* and the line number where the error is encountered.
|
||||||
*
|
*
|
||||||
|
|
@ -1011,7 +1014,7 @@ __LJMP static int _hlua_pusherror(lua_State *L)
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
static int hlua_pusherror(lua_State *L, const char *fmt, ...)
|
int hlua_pusherror(lua_State *L, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list argp;
|
va_list argp;
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
|
|
@ -1783,34 +1786,6 @@ int hlua_ctx_init(struct hlua *lua, int state_id, struct task *task)
|
||||||
return 1;
|
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
|
/* Used to destroy the Lua coroutine when the attached stream or task
|
||||||
* is destroyed. The destroy also the memory context. The struct "lua"
|
* is destroyed. The destroy also the memory context. The struct "lua"
|
||||||
* will be freed.
|
* will be freed.
|
||||||
|
|
@ -2949,20 +2924,20 @@ __LJMP static int hlua_socket_receive_yield(struct lua_State *L, int status, lua
|
||||||
|
|
||||||
/* remove final \r\n. */
|
/* remove final \r\n. */
|
||||||
if (nblk == 1) {
|
if (nblk == 1) {
|
||||||
if (blk1[len1-1] == '\n') {
|
if (len1 && blk1[len1-1] == '\n') {
|
||||||
len1--;
|
len1--;
|
||||||
skip_at_end++;
|
skip_at_end++;
|
||||||
if (blk1[len1-1] == '\r') {
|
if (len1 && blk1[len1-1] == '\r') {
|
||||||
len1--;
|
len1--;
|
||||||
skip_at_end++;
|
skip_at_end++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (blk2[len2-1] == '\n') {
|
if (len2 && blk2[len2-1] == '\n') {
|
||||||
len2--;
|
len2--;
|
||||||
skip_at_end++;
|
skip_at_end++;
|
||||||
if (blk2[len2-1] == '\r') {
|
if (len2 && blk2[len2-1] == '\r') {
|
||||||
len2--;
|
len2--;
|
||||||
skip_at_end++;
|
skip_at_end++;
|
||||||
}
|
}
|
||||||
|
|
@ -6709,6 +6684,20 @@ __LJMP static inline int hlua_http_add_hdr(lua_State *L, struct http_msg *msg)
|
||||||
size_t value_len;
|
size_t value_len;
|
||||||
const char *value = MAY_LJMP(luaL_checklstring(L, 3, &value_len));
|
const char *value = MAY_LJMP(luaL_checklstring(L, 3, &value_len));
|
||||||
struct htx *htx = htxbuf(&msg->chn->buf);
|
struct htx *htx = htxbuf(&msg->chn->buf);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
/* Reject header values containing CR/LF/NUL to prevent HTTP header
|
||||||
|
* injection on HTTP/1 output.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < name_len; i++) {
|
||||||
|
if (name[i] == 0 || name[i] == '\r' || name[i] == '\n')
|
||||||
|
WILL_LJMP(lua_error(L));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < value_len; i++) {
|
||||||
|
if (value[i] == 0 || value[i] == '\r' || value[i] == '\n')
|
||||||
|
WILL_LJMP(lua_error(L));
|
||||||
|
}
|
||||||
|
|
||||||
lua_pushboolean(L, http_add_header(htx, ist2(name, name_len),
|
lua_pushboolean(L, http_add_header(htx, ist2(name, name_len),
|
||||||
ist2(value, value_len), 1));
|
ist2(value, value_len), 1));
|
||||||
|
|
@ -7936,488 +7925,6 @@ __LJMP static int hlua_http_msg_unset_eom(lua_State *L)
|
||||||
return 0;
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
|
@ -10049,6 +9556,11 @@ __LJMP static void hlua_event_hdl_cb_push_args(struct hlua_event_sub *hlua_sub,
|
||||||
lua_settable(hlua->T, -3);
|
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 */
|
/* sub mgmt */
|
||||||
hlua->nargs += 1;
|
hlua->nargs += 1;
|
||||||
hlua_fcn_new_event_sub(hlua->T, hlua_sub->sub);
|
hlua_fcn_new_event_sub(hlua->T, hlua_sub->sub);
|
||||||
|
|
@ -13378,6 +12890,16 @@ static int hlua_cfg_parse_openlibs(char **args, int section_type, struct proxy *
|
||||||
return -1;
|
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;
|
hlua_openlibs_flags = flags;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -13478,6 +13000,8 @@ static int hlua_load(char **args, int section_type, struct proxy *curpx,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hlua_init();
|
||||||
|
|
||||||
/* loading for global state */
|
/* loading for global state */
|
||||||
hlua_state_id = 0;
|
hlua_state_id = 0;
|
||||||
ha_set_thread(NULL);
|
ha_set_thread(NULL);
|
||||||
|
|
@ -13496,6 +13020,8 @@ static int hlua_load_per_thread(char **args, int section_type, struct proxy *cur
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hlua_init();
|
||||||
|
|
||||||
if (per_thread_load == NULL) {
|
if (per_thread_load == NULL) {
|
||||||
/* allocate the first entry large enough to store the final NULL */
|
/* allocate the first entry large enough to store the final NULL */
|
||||||
per_thread_load = calloc(1, sizeof(*per_thread_load));
|
per_thread_load = calloc(1, sizeof(*per_thread_load));
|
||||||
|
|
@ -13584,6 +13110,8 @@ static int hlua_config_prepend_path(char **args, int section_type, struct proxy
|
||||||
struct prepend_path *p = NULL;
|
struct prepend_path *p = NULL;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
hlua_init();
|
||||||
|
|
||||||
if (too_many_args(2, args, err, NULL)) {
|
if (too_many_args(2, args, err, NULL)) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
@ -13992,6 +13520,7 @@ int hlua_post_init()
|
||||||
|
|
||||||
hlua_body = 0;
|
hlua_body = 0;
|
||||||
|
|
||||||
|
|
||||||
#if defined(USE_OPENSSL)
|
#if defined(USE_OPENSSL)
|
||||||
/* Initialize SSL server. */
|
/* Initialize SSL server. */
|
||||||
if (socket_ssl->xprt->prepare_srv) {
|
if (socket_ssl->xprt->prepare_srv) {
|
||||||
|
|
@ -14302,7 +13831,6 @@ lua_State *hlua_init_state(int thread_num)
|
||||||
hlua_class_function(L, "get_patref", hlua_get_patref);
|
hlua_class_function(L, "get_patref", hlua_get_patref);
|
||||||
hlua_class_function(L, "get_var", hlua_core_get_var);
|
hlua_class_function(L, "get_var", hlua_core_get_var);
|
||||||
hlua_class_function(L, "tcp", hlua_socket_new);
|
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, "event_sub", hlua_event_global_sub);
|
||||||
hlua_class_function(L, "log", hlua_log);
|
hlua_class_function(L, "log", hlua_log);
|
||||||
hlua_class_function(L, "Debug", hlua_log_debug);
|
hlua_class_function(L, "Debug", hlua_log_debug);
|
||||||
|
|
@ -14624,30 +14152,6 @@ lua_State *hlua_init_state(int thread_num)
|
||||||
/* Register previous table in the registry with reference and named entry. */
|
/* Register previous table in the registry with reference and named entry. */
|
||||||
class_http_msg_ref = hlua_register_metatable(L, CLASS_HTTP_MSG);
|
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
|
* Register class AppletTCP
|
||||||
|
|
@ -14799,6 +14303,28 @@ lua_State *hlua_init_state(int thread_num)
|
||||||
/* Register previous table in the registry with reference and named entry. */
|
/* Register previous table in the registry with reference and named entry. */
|
||||||
class_socket_ref = hlua_register_metatable(L, CLASS_SOCKET);
|
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);
|
lua_atpanic(L, hlua_panic_safe);
|
||||||
|
|
||||||
return L;
|
return L;
|
||||||
|
|
@ -14819,6 +14345,9 @@ void hlua_init(void) {
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (hlua_states[0])
|
||||||
|
return; /* already initialised */
|
||||||
|
|
||||||
/* Init post init function list head */
|
/* Init post init function list head */
|
||||||
for (i = 0; i < MAX_THREADS + 1; i++)
|
for (i = 0; i < MAX_THREADS + 1; i++)
|
||||||
LIST_INIT(&hlua_init_functions[i]);
|
LIST_INIT(&hlua_init_functions[i]);
|
||||||
|
|
@ -14884,10 +14413,16 @@ static void hlua_deinit()
|
||||||
{
|
{
|
||||||
int thr;
|
int thr;
|
||||||
struct hlua_reg_filter *reg_flt, *reg_flt_bck;
|
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)
|
list_for_each_entry_safe(reg_flt, reg_flt_bck, &referenced_filters, l)
|
||||||
release_hlua_reg_filter(reg_flt);
|
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++) {
|
for (thr = 0; thr < MAX_THREADS+1; thr++) {
|
||||||
if (hlua_states[thr])
|
if (hlua_states[thr])
|
||||||
lua_close(hlua_states[thr]);
|
lua_close(hlua_states[thr]);
|
||||||
|
|
@ -14907,3 +14442,14 @@ static void hlua_register_build_options(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
INITCALL0(STG_REGISTER, hlua_register_build_options);
|
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,6 +351,8 @@ int hpack_dht_insert(struct hpack_dht *dht, struct ist name, struct ist value)
|
||||||
else {
|
else {
|
||||||
/* need to defragment the table before inserting upfront */
|
/* need to defragment the table before inserting upfront */
|
||||||
dht = hpack_dht_defrag(dht);
|
dht = hpack_dht_defrag(dht);
|
||||||
|
if (!dht)
|
||||||
|
return -1;
|
||||||
wrap = dht->wrap + 1;
|
wrap = dht->wrap + 1;
|
||||||
head = dht->head + 1;
|
head = dht->head + 1;
|
||||||
dht->dte[head].addr = dht->dte[dht->front].addr - (name.len + value.len);
|
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