mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-04-02 17:41:31 +02:00
Compare commits
145 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22b7da1464 | ||
|
|
3636ebd062 | ||
|
|
253bf8cbae | ||
|
|
6228ec6a81 | ||
|
|
2457701299 | ||
|
|
490b465fd1 | ||
|
|
b26178396a | ||
|
|
3c42a7e9ac | ||
|
|
f1ed1de317 | ||
|
|
531a2b9f1a | ||
|
|
91ea5809e9 | ||
|
|
4dd224b5ef | ||
|
|
782894f5b8 | ||
|
|
621f21f6fd | ||
|
|
e8d9eb4f7a | ||
|
|
0f0574ee96 | ||
|
|
81f22cd68a | ||
|
|
068baf4ddf | ||
|
|
6ae22a50e5 | ||
|
|
f16c851625 | ||
|
|
ce4aab4fdb | ||
|
|
afa17f68a9 | ||
|
|
9d8f7a4459 | ||
|
|
3078a63335 | ||
|
|
10094fdd00 | ||
|
|
62fcc48bcf | ||
|
|
ea5cb23307 | ||
|
|
9a2db73e32 | ||
|
|
967228c211 | ||
|
|
b72bfedd68 | ||
|
|
011b085803 | ||
|
|
48e41e4ce0 | ||
|
|
1e08247961 | ||
|
|
7c3fe4d0c0 | ||
|
|
10ce550b47 | ||
|
|
7f6999b764 | ||
|
|
c49facbabe | ||
|
|
6fbccae1ab | ||
|
|
44c02854ca | ||
|
|
275bd9ec03 | ||
|
|
9e92352967 | ||
|
|
954e87ee01 | ||
|
|
51e1562a0d | ||
|
|
3e8b8aa6aa | ||
|
|
64e3029e8b | ||
|
|
b58f567ff3 | ||
|
|
978119caa6 | ||
|
|
dc7c8bd2f8 | ||
|
|
949aa36820 | ||
|
|
8c00df7448 | ||
|
|
2adcdbacc2 | ||
|
|
e4b8531d5a | ||
|
|
6a862009be | ||
|
|
c8bfd06b57 | ||
|
|
e264523112 | ||
|
|
eaf42ee886 | ||
|
|
397530b1e9 | ||
|
|
7c73b08a98 | ||
|
|
ee95a7539e | ||
|
|
daf378d2b4 | ||
|
|
b134065ea8 | ||
|
|
94d2f69b93 | ||
|
|
66965a60ba | ||
|
|
453a01387b | ||
|
|
25366f6dc1 | ||
|
|
d47415624b | ||
|
|
14a4168a84 | ||
|
|
226bb4bd28 | ||
|
|
ad87ab1f2e | ||
|
|
2b0c510aff | ||
|
|
631fd5f99b | ||
|
|
5dcfbc5fad | ||
|
|
e418e828aa | ||
|
|
50446c35a7 | ||
|
|
a336c467a0 | ||
|
|
e375f1061a | ||
|
|
cf3173d92b | ||
|
|
5280130343 | ||
|
|
d4eee1f206 | ||
|
|
08cc37a554 | ||
|
|
b7d1c2f91d | ||
|
|
20ae1eb79d | ||
|
|
4fd5cafe27 | ||
|
|
d26bd9f978 | ||
|
|
506cfcb5d4 | ||
|
|
418f0c0bbe | ||
|
|
27d7c69e87 | ||
|
|
d1c7e56585 | ||
|
|
4e99cddde4 | ||
|
|
0e36267aac | ||
|
|
1b0dfff552 | ||
|
|
d3ad730d5f | ||
|
|
cca9245416 | ||
|
|
07edaed191 | ||
|
|
1c1d9d2500 | ||
|
|
47987ccbd9 | ||
|
|
33041fe91f | ||
|
|
8e250bba8f | ||
|
|
c7564c19a2 | ||
|
|
efbf0f8ed1 | ||
|
|
52d8ee85e7 | ||
|
|
38a7d8599d | ||
|
|
82afd36b6c | ||
|
|
ada33006ef | ||
|
|
163eba5c8c | ||
|
|
61d68f14b2 | ||
|
|
125cbecfa9 | ||
|
|
a61ea0f414 | ||
|
|
cd363e0246 | ||
|
|
d257dd4563 | ||
|
|
39121ceca6 | ||
|
|
c9a9fa813b | ||
|
|
181cd8ba8a | ||
|
|
5acdda4eed | ||
|
|
92a24a4e87 | ||
|
|
467f911cea | ||
|
|
0213dd70c9 | ||
|
|
5ead611cc2 | ||
|
|
41c89e4fb6 | ||
|
|
b71f70d548 | ||
|
|
01b9b67d5c | ||
|
|
f8c96bf9cb | ||
|
|
4d6cba03f2 | ||
|
|
1c379cad88 | ||
|
|
3d9865a12c | ||
|
|
d72be950bd | ||
|
|
5a0fbbf1ca | ||
|
|
c6fc53aa99 | ||
|
|
2ca7601c2d | ||
|
|
d250b381dc | ||
|
|
5b184e4178 | ||
|
|
fedaf054c4 | ||
|
|
8e469ebf2e | ||
|
|
ff7b06badb | ||
|
|
5d0f5f8168 | ||
|
|
ed6a4bc807 | ||
|
|
282b9b7d16 | ||
|
|
6982c2539f | ||
|
|
9852d5be26 | ||
|
|
7d40b3134a | ||
|
|
8f6cb8f452 | ||
|
|
60c9e2975b | ||
|
|
5617e47f91 | ||
|
|
042b7ab763 | ||
|
|
7466f64c56 |
31
.github/matrix.py
vendored
31
.github/matrix.py
vendored
@ -12,6 +12,7 @@ import functools
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from os import environ
|
from os import environ
|
||||||
from packaging import version
|
from packaging import version
|
||||||
@ -33,13 +34,24 @@ def get_all_github_tags(url):
|
|||||||
headers = {}
|
headers = {}
|
||||||
if environ.get("GITHUB_TOKEN") is not None:
|
if environ.get("GITHUB_TOKEN") is not None:
|
||||||
headers["Authorization"] = "token {}".format(environ.get("GITHUB_TOKEN"))
|
headers["Authorization"] = "token {}".format(environ.get("GITHUB_TOKEN"))
|
||||||
request = urllib.request.Request(url, headers=headers)
|
all_tags = []
|
||||||
|
page = 1
|
||||||
|
sep = "&" if "?" in url else "?"
|
||||||
|
while True:
|
||||||
|
paginated_url = "{}{}per_page=100&page={}".format(url, sep, page)
|
||||||
|
request = urllib.request.Request(paginated_url, headers=headers)
|
||||||
try:
|
try:
|
||||||
tags = urllib.request.urlopen(request)
|
response = urllib.request.urlopen(request)
|
||||||
except:
|
except urllib.error.URLError:
|
||||||
return None
|
return all_tags if all_tags else None
|
||||||
tags = json.loads(tags.read().decode("utf-8"))
|
tags = json.loads(response.read().decode("utf-8"))
|
||||||
return [tag['name'] for tag in tags]
|
if not tags:
|
||||||
|
break
|
||||||
|
all_tags.extend([tag['name'] for tag in tags])
|
||||||
|
if len(tags) < 100:
|
||||||
|
break
|
||||||
|
page += 1
|
||||||
|
return all_tags if all_tags else None
|
||||||
|
|
||||||
@functools.lru_cache(5)
|
@functools.lru_cache(5)
|
||||||
def determine_latest_openssl(ssl):
|
def determine_latest_openssl(ssl):
|
||||||
@ -65,6 +77,8 @@ def determine_latest_aws_lc(ssl):
|
|||||||
if not tags:
|
if not tags:
|
||||||
return "AWS_LC_VERSION=failed_to_detect"
|
return "AWS_LC_VERSION=failed_to_detect"
|
||||||
valid_tags = list(filter(aws_lc_version_valid, tags))
|
valid_tags = list(filter(aws_lc_version_valid, tags))
|
||||||
|
if not valid_tags:
|
||||||
|
return "AWS_LC_VERSION=failed_to_detect"
|
||||||
latest_tag = max(valid_tags, key=aws_lc_version_string_to_num)
|
latest_tag = max(valid_tags, key=aws_lc_version_string_to_num)
|
||||||
return "AWS_LC_VERSION={}".format(latest_tag[1:])
|
return "AWS_LC_VERSION={}".format(latest_tag[1:])
|
||||||
|
|
||||||
@ -76,11 +90,12 @@ def aws_lc_fips_version_valid(version_string):
|
|||||||
|
|
||||||
@functools.lru_cache(5)
|
@functools.lru_cache(5)
|
||||||
def determine_latest_aws_lc_fips(ssl):
|
def determine_latest_aws_lc_fips(ssl):
|
||||||
# the AWS-LC-FIPS tags are at the end of the list, so let's get a lot
|
tags = get_all_github_tags("https://api.github.com/repos/aws/aws-lc/tags")
|
||||||
tags = get_all_github_tags("https://api.github.com/repos/aws/aws-lc/tags?per_page=200")
|
|
||||||
if not tags:
|
if not tags:
|
||||||
return "AWS_LC_FIPS_VERSION=failed_to_detect"
|
return "AWS_LC_FIPS_VERSION=failed_to_detect"
|
||||||
valid_tags = list(filter(aws_lc_fips_version_valid, tags))
|
valid_tags = list(filter(aws_lc_fips_version_valid, tags))
|
||||||
|
if not valid_tags:
|
||||||
|
return "AWS_LC_FIPS_VERSION=failed_to_detect"
|
||||||
latest_tag = max(valid_tags, key=aws_lc_fips_version_string_to_num)
|
latest_tag = max(valid_tags, key=aws_lc_fips_version_string_to_num)
|
||||||
return "AWS_LC_FIPS_VERSION={}".format(latest_tag[12:])
|
return "AWS_LC_FIPS_VERSION={}".format(latest_tag[12:])
|
||||||
|
|
||||||
|
|||||||
4
Makefile
4
Makefile
@ -643,7 +643,7 @@ ifneq ($(USE_OPENSSL:0=),)
|
|||||||
OPTIONS_OBJS += src/ssl_sock.o src/ssl_ckch.o src/ssl_ocsp.o src/ssl_crtlist.o \
|
OPTIONS_OBJS += src/ssl_sock.o src/ssl_ckch.o src/ssl_ocsp.o src/ssl_crtlist.o \
|
||||||
src/ssl_sample.o src/cfgparse-ssl.o src/ssl_gencert.o \
|
src/ssl_sample.o src/cfgparse-ssl.o src/ssl_gencert.o \
|
||||||
src/ssl_utils.o src/jwt.o src/ssl_clienthello.o src/jws.o src/acme.o \
|
src/ssl_utils.o src/jwt.o src/ssl_clienthello.o src/jws.o src/acme.o \
|
||||||
src/ssl_trace.o src/jwe.o
|
src/acme_resolvers.o src/ssl_trace.o src/jwe.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_ENGINE:0=),)
|
ifneq ($(USE_ENGINE:0=),)
|
||||||
@ -670,7 +670,7 @@ OPTIONS_OBJS += src/mux_quic.o src/h3.o src/quic_rx.o src/quic_tx.o \
|
|||||||
src/quic_cc_nocc.o src/quic_cc.o src/quic_pacing.o \
|
src/quic_cc_nocc.o src/quic_cc.o src/quic_pacing.o \
|
||||||
src/h3_stats.o src/quic_stats.o src/qpack-enc.o \
|
src/h3_stats.o src/quic_stats.o src/qpack-enc.o \
|
||||||
src/qpack-tbl.o src/quic_cc_drs.o src/quic_fctl.o \
|
src/qpack-tbl.o src/quic_cc_drs.o src/quic_fctl.o \
|
||||||
src/quic_enc.o
|
src/quic_enc.o src/mux_quic_qstrm.o src/xprt_qstrm.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_QUIC_OPENSSL_COMPAT:0=),)
|
ifneq ($(USE_QUIC_OPENSSL_COMPAT:0=),)
|
||||||
|
|||||||
@ -149,7 +149,7 @@ usage() {
|
|||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " -S, --master-socket <path> Use the master socket at <path> (default: ${SOCKET})"
|
echo " -S, --master-socket <path> Use the master socket at <path> (default: ${SOCKET})"
|
||||||
echo " -s, --socket <path> Use the stats socket at <path>"
|
echo " -s, --socket <path> Use the stats socket at <path>"
|
||||||
echo " -p, --path <path> Specifiy a base path for relative files (default: ${BASEPATH})"
|
echo " -p, --path <path> Specify a base path for relative files (default: ${BASEPATH})"
|
||||||
echo " -n, --dry-run Read certificates on the socket but don't dump them"
|
echo " -n, --dry-run Read certificates on the socket but don't dump them"
|
||||||
echo " -d, --debug Debug mode, set -x"
|
echo " -d, --debug Debug mode, set -x"
|
||||||
echo " -v, --verbose Verbose mode"
|
echo " -v, --verbose Verbose mode"
|
||||||
|
|||||||
@ -86,7 +86,7 @@ maintenance model and what the user wants is passed, then the LLM is invited to
|
|||||||
provide its opinion on the need for a backport and an explanation of the reason
|
provide its opinion on the need for a backport and an explanation of the reason
|
||||||
for its choice. This often helps the user to find a quick summary about the
|
for its choice. This often helps the user to find a quick summary about the
|
||||||
patch. All these outputs are then converted to a long HTML page with colors and
|
patch. All these outputs are then converted to a long HTML page with colors and
|
||||||
radio buttons, where patches are pre-selected based on this classification,
|
radio buttons, where patches are preselected based on this classification,
|
||||||
that the user can consult and adjust, read the commits if needed, and the
|
that the user can consult and adjust, read the commits if needed, and the
|
||||||
selected patches finally provide some copy-pastable commands in a text-area to
|
selected patches finally provide some copy-pastable commands in a text-area to
|
||||||
select commit IDs to work on, typically in a form that's suitable for a simple
|
select commit IDs to work on, typically in a form that's suitable for a simple
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -134,7 +134,7 @@ HATerm:
|
|||||||
- /?R=<enable> Enable sending random data if >0.
|
- /?R=<enable> Enable sending random data if >0.
|
||||||
|
|
||||||
Note that those arguments may be cumulated on one line separated by a set of
|
Note that those arguments may be cumulated on one line separated by a set of
|
||||||
delimitors among [&?,;/] :
|
delimiters among [&?,;/] :
|
||||||
- GET /?s=20k&c=1&t=700&K=30r HTTP/1.0
|
- GET /?s=20k&c=1&t=700&K=30r HTTP/1.0
|
||||||
- GET /?r=500?s=0?c=0?t=1000 HTTP/1.0
|
- GET /?r=500?s=0?c=0?t=1000 HTTP/1.0
|
||||||
|
|
||||||
|
|||||||
@ -539,10 +539,22 @@ message. These functions are used by HTX analyzers or by multiplexers.
|
|||||||
with the first block not removed, or NULL if everything was removed, and
|
with the first block not removed, or NULL if everything was removed, and
|
||||||
the amount of data drained.
|
the amount of data drained.
|
||||||
|
|
||||||
- htx_xfer_blks() transfers HTX blocks from an HTX message to another,
|
- htx_xfer() transfers HTX blocks from an HTX message to another, stopping
|
||||||
stopping after the first block of a specified type is transferred or when
|
when a specific amount of bytes, including meta-data, was copied. If the
|
||||||
a specific amount of bytes, including meta-data, was moved. If the tail
|
tail block is a DATA block, it may be partially copied. All other block
|
||||||
block is a DATA block, it may be partially moved. All other block are
|
are transferred at once. By default, copied blocks are removed from the
|
||||||
|
original HTX message and headers and trailers parts cannot be partially
|
||||||
|
copied. But flags can be set to change the default behavior:
|
||||||
|
|
||||||
|
- HTX_XFER_KEEP_SRC_BLKS: source blocks are not removed
|
||||||
|
- HTX_XFER_PARTIAL_HDRS_COPY: partial headers and trailers
|
||||||
|
part can be xferred
|
||||||
|
- HTX_XFER_HDRS_ONLY: Only the headers part is xferred
|
||||||
|
|
||||||
|
- htx_xfer_blks() [DEPRECATED] transfers HTX blocks from an HTX message to
|
||||||
|
another, stopping after the first block of a specified type is transferred
|
||||||
|
or when a specific amount of bytes, including meta-data, was moved. If the
|
||||||
|
tail block is a DATA block, it may be partially moved. All other block are
|
||||||
transferred at once or kept. This function returns a mixed value, with the
|
transferred at once or kept. This function returns a mixed value, with the
|
||||||
last block moved, or NULL if nothing was moved, and the amount of data
|
last block moved, or NULL if nothing was moved, and the amount of data
|
||||||
transferred. When HEADERS or TRAILERS blocks must be transferred, this
|
transferred. When HEADERS or TRAILERS blocks must be transferred, this
|
||||||
|
|||||||
@ -114,7 +114,7 @@ SHUT RDY ACT
|
|||||||
1 1 1 => shut pending
|
1 1 1 => shut pending
|
||||||
|
|
||||||
PB: we can land into final shut if one thread disables the FD while another
|
PB: we can land into final shut if one thread disables the FD while another
|
||||||
one that was waiting on it reports it as shut. Theorically it should be
|
one that was waiting on it reports it as shut. Theoretically it should be
|
||||||
implicitly ready though, since reported. But if no data is reported, it
|
implicitly ready though, since reported. But if no data is reported, it
|
||||||
will be reportedly shut only. And no event will be reported then. This
|
will be reportedly shut only. And no event will be reported then. This
|
||||||
might still make sense since it's not active, thus we don't want events.
|
might still make sense since it's not active, thus we don't want events.
|
||||||
|
|||||||
@ -1731,7 +1731,7 @@ add backend <name> from <defproxy> [mode <mode>] [guid <guid>] [ EXPERIMENTAL ]
|
|||||||
Only TCP or HTTP proxies can be created. All of the settings are inherited
|
Only TCP or HTTP proxies can be created. All of the settings are inherited
|
||||||
from <defproxy> default proxy instance. By default, it is mandatory to
|
from <defproxy> default proxy instance. By default, it is mandatory to
|
||||||
specify the backend mode via the argument of the same name, unless <defproxy>
|
specify the backend mode via the argument of the same name, unless <defproxy>
|
||||||
already defines it explicitely. It is also possible to use an optional GUID
|
already defines it explicitly. It is also possible to use an optional GUID
|
||||||
argument if wanted.
|
argument if wanted.
|
||||||
|
|
||||||
Servers can be added via the command "add server". The backend is initialized
|
Servers can be added via the command "add server". The backend is initialized
|
||||||
@ -1740,10 +1740,7 @@ add backend <name> from <defproxy> [mode <mode>] [guid <guid>] [ EXPERIMENTAL ]
|
|||||||
|
|
||||||
All named default proxies can be used, given that they validate the same
|
All named default proxies can be used, given that they validate the same
|
||||||
inheritance rules applied during configuration parsing. There is some
|
inheritance rules applied during configuration parsing. There is some
|
||||||
exceptions though, for example when the mode is neither TCP nor HTTP. Another
|
exceptions though, for example when the mode is neither TCP nor HTTP.
|
||||||
exception is that it is not yet possible to use a default proxies which
|
|
||||||
reference custom HTTP errors, for example via the errorfiles or http-rules
|
|
||||||
keywords.
|
|
||||||
|
|
||||||
This command is restricted and can only be issued on sockets configured for
|
This command is restricted and can only be issued on sockets configured for
|
||||||
level "admin". Moreover, this feature is still considered in development so it
|
level "admin". Moreover, this feature is still considered in development so it
|
||||||
@ -2133,7 +2130,7 @@ del backend <name>
|
|||||||
be attached to the backend instance.
|
be attached to the backend instance.
|
||||||
|
|
||||||
There is additional restrictions which prevent backend removal. First, a
|
There is additional restrictions which prevent backend removal. First, a
|
||||||
backend cannot be removed if it is explicitely referenced by config elements,
|
backend cannot be removed if it is explicitly referenced by config elements,
|
||||||
for example via a use_backend rule or in sample expressions. Some proxies
|
for example via a use_backend rule or in sample expressions. Some proxies
|
||||||
options are also incompatible with runtime deletion. Currently, this is the
|
options are also incompatible with runtime deletion. Currently, this is the
|
||||||
case when deprecated dispatch or option transparent are used. Also, a backend
|
case when deprecated dispatch or option transparent are used. Also, a backend
|
||||||
@ -2141,7 +2138,7 @@ del backend <name>
|
|||||||
impossible for now to remove a backend if QUIC servers were present in it.
|
impossible for now to remove a backend if QUIC servers were present in it.
|
||||||
|
|
||||||
It can be useful to use "wait be-removable" prior to this command to check
|
It can be useful to use "wait be-removable" prior to this command to check
|
||||||
for the aformentioned requisites. This also provides a methode to wait for
|
for the aforementioned requisites. This also provides a method to wait for
|
||||||
the final closure of the streams attached to the target backend.
|
the final closure of the streams attached to the target backend.
|
||||||
|
|
||||||
This command is restricted and can only be issued on sockets configured for
|
This command is restricted and can only be issued on sockets configured for
|
||||||
|
|||||||
69
examples/keylog-test.cfg
Normal file
69
examples/keylog-test.cfg
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Example: log HTTP traffic and TLS session keys to separate destinations
|
||||||
|
#
|
||||||
|
# "option httpslog" sends HTTP access logs to the /dev/log syslog server.
|
||||||
|
# TLS session keys are written to 2 ring buffers.
|
||||||
|
#
|
||||||
|
# Requirements:
|
||||||
|
# - HAProxy built with OpenSSL support
|
||||||
|
# - "tune.ssl.keylog on" in the global section
|
||||||
|
#
|
||||||
|
# Retrieve TLS session keys from the ring buffer via the CLI:
|
||||||
|
# For frontend connections:
|
||||||
|
#
|
||||||
|
# (echo "show events keylog-fc -w"; read) | socat /tmp/worker.socket -
|
||||||
|
#
|
||||||
|
# For backend connections:
|
||||||
|
#
|
||||||
|
# (echo "show events keylog-bc -w"; read) | socat /tmp/worker.socket -
|
||||||
|
#
|
||||||
|
# The result is in SSLKEYLOGFILE format and can be saved to a file and loaded
|
||||||
|
# into Wireshark to decrypt captured TLS traffic.
|
||||||
|
|
||||||
|
global
|
||||||
|
stats socket /tmp/worker.socket mode 0660
|
||||||
|
tune.ssl.keylog on
|
||||||
|
|
||||||
|
# Ring buffer for TLS session keys.
|
||||||
|
# "format raw" stores only the log message text, without any syslog envelope,
|
||||||
|
# producing output in the SSLKEYLOGFILE format directly.
|
||||||
|
ring keylog-fc
|
||||||
|
description "TLS session key frontend log"
|
||||||
|
format raw
|
||||||
|
maxlen 2000
|
||||||
|
size 1M
|
||||||
|
|
||||||
|
ring keylog-bc
|
||||||
|
description "TLS session key backend log"
|
||||||
|
format raw
|
||||||
|
maxlen 2000
|
||||||
|
size 1M
|
||||||
|
|
||||||
|
|
||||||
|
defaults
|
||||||
|
mode http
|
||||||
|
timeout client 30s
|
||||||
|
timeout server 30s
|
||||||
|
timeout connect 5s
|
||||||
|
|
||||||
|
log-profile keylog-fc
|
||||||
|
on any format "${HAPROXY_KEYLOG_FC_LOG_FMT}"
|
||||||
|
|
||||||
|
log-profile keylog-bc
|
||||||
|
on any format "${HAPROXY_KEYLOG_BC_LOG_FMT}"
|
||||||
|
|
||||||
|
frontend https-in
|
||||||
|
bind :443 ssl crt "common.pem"
|
||||||
|
|
||||||
|
option httpslog
|
||||||
|
|
||||||
|
# HTTPs access logs sent to the syslog server
|
||||||
|
log /dev/log format raw local0
|
||||||
|
|
||||||
|
# TLS session keys written to the ring buffer
|
||||||
|
log ring@keylog-fc profile keylog-fc local1
|
||||||
|
log ring@keylog-bc profile keylog-bc local1
|
||||||
|
|
||||||
|
default_backend be1
|
||||||
|
|
||||||
|
backend be1
|
||||||
|
server s1 10.0.0.123:443 ssl verify none
|
||||||
@ -2,17 +2,28 @@
|
|||||||
#ifndef _ACME_T_H_
|
#ifndef _ACME_T_H_
|
||||||
#define _ACME_T_H_
|
#define _ACME_T_H_
|
||||||
|
|
||||||
|
#include <haproxy/acme_resolvers-t.h>
|
||||||
#include <haproxy/istbuf.h>
|
#include <haproxy/istbuf.h>
|
||||||
#include <haproxy/openssl-compat.h>
|
#include <haproxy/openssl-compat.h>
|
||||||
|
|
||||||
|
#if defined(HAVE_ACME)
|
||||||
|
|
||||||
#define ACME_RETRY 5
|
#define ACME_RETRY 5
|
||||||
|
|
||||||
|
/* Readiness requirements for challenge */
|
||||||
|
#define ACME_RDY_NONE 0x00
|
||||||
|
#define ACME_RDY_CLI 0x01
|
||||||
|
#define ACME_RDY_DNS 0x02
|
||||||
|
|
||||||
/* acme section configuration */
|
/* acme section configuration */
|
||||||
struct acme_cfg {
|
struct acme_cfg {
|
||||||
char *filename; /* config filename */
|
char *filename; /* config filename */
|
||||||
int linenum; /* config linenum */
|
int linenum; /* config linenum */
|
||||||
char *name; /* section name */
|
char *name; /* section name */
|
||||||
int reuse_key; /* do we need to renew the private key */
|
int reuse_key; /* do we need to renew the private key */
|
||||||
|
int cond_ready; /* ready condition */
|
||||||
|
unsigned int dns_delay; /* delay in seconds before re-triggering DNS resolution (default: 300) */
|
||||||
|
unsigned int dns_timeout; /* time after which the DNS check shouldn't be retried (default: 600) */
|
||||||
char *directory; /* directory URL */
|
char *directory; /* directory URL */
|
||||||
char *map; /* storage for tokens + thumbprint */
|
char *map; /* storage for tokens + thumbprint */
|
||||||
struct {
|
struct {
|
||||||
@ -40,6 +51,10 @@ enum acme_st {
|
|||||||
ACME_NEWACCOUNT,
|
ACME_NEWACCOUNT,
|
||||||
ACME_NEWORDER,
|
ACME_NEWORDER,
|
||||||
ACME_AUTH,
|
ACME_AUTH,
|
||||||
|
ACME_CLI_WAIT, /* wait for the ACME_RDY_CLI */
|
||||||
|
ACME_RSLV_WAIT,
|
||||||
|
ACME_RSLV_TRIGGER,
|
||||||
|
ACME_RSLV_READY,
|
||||||
ACME_CHALLENGE,
|
ACME_CHALLENGE,
|
||||||
ACME_CHKCHALLENGE,
|
ACME_CHKCHALLENGE,
|
||||||
ACME_FINALIZE,
|
ACME_FINALIZE,
|
||||||
@ -58,6 +73,8 @@ struct acme_auth {
|
|||||||
struct ist auth; /* auth URI */
|
struct ist auth; /* auth URI */
|
||||||
struct ist chall; /* challenge URI */
|
struct ist chall; /* challenge URI */
|
||||||
struct ist token; /* token */
|
struct ist token; /* token */
|
||||||
|
int validated; /* already validated */
|
||||||
|
struct acme_rslv *rslv; /* acme dns-01 resolver */
|
||||||
int ready; /* is the challenge ready ? */
|
int ready; /* is the challenge ready ? */
|
||||||
void *next;
|
void *next;
|
||||||
};
|
};
|
||||||
@ -84,6 +101,8 @@ struct acme_ctx {
|
|||||||
X509_REQ *req;
|
X509_REQ *req;
|
||||||
struct ist finalize;
|
struct ist finalize;
|
||||||
struct ist certificate;
|
struct ist certificate;
|
||||||
|
unsigned int dnstasks; /* number of DNS tasks running for this ctx */
|
||||||
|
unsigned int dnsstarttime; /* time at which we started the DNS checks */
|
||||||
struct task *task;
|
struct task *task;
|
||||||
struct ebmb_node node;
|
struct ebmb_node node;
|
||||||
char name[VAR_ARRAY];
|
char name[VAR_ARRAY];
|
||||||
@ -101,4 +120,6 @@ struct acme_ctx {
|
|||||||
#define ACME_VERB_ADVANCED 4
|
#define ACME_VERB_ADVANCED 4
|
||||||
#define ACME_VERB_COMPLETE 5
|
#define ACME_VERB_COMPLETE 5
|
||||||
|
|
||||||
|
#endif /* ! HAVE_ACME */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
27
include/haproxy/acme_resolvers-t.h
Normal file
27
include/haproxy/acme_resolvers-t.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#ifndef _HAPROXY_ACME_RESOLVERS_T_H
|
||||||
|
#define _HAPROXY_ACME_RESOLVERS_T_H
|
||||||
|
|
||||||
|
#include <haproxy/obj_type-t.h>
|
||||||
|
#include <haproxy/resolvers-t.h>
|
||||||
|
|
||||||
|
struct dns_counters;
|
||||||
|
|
||||||
|
/* TXT records for dns-01 */
|
||||||
|
|
||||||
|
struct acme_rslv {
|
||||||
|
enum obj_type obj_type; /* OBJ_TYPE_ACME_RSLV */
|
||||||
|
unsigned int *dnstasks; /* number of running DNS resolution for the same acme_task */
|
||||||
|
char *hostname_dn;
|
||||||
|
int hostname_dn_len;
|
||||||
|
struct resolvers *resolvers;
|
||||||
|
struct resolv_requester *requester;
|
||||||
|
int result; /* RSLV_STATUS_* — NONE until done */
|
||||||
|
int error_code; /* RSLV_RESP_* from the error callback */
|
||||||
|
struct task *acme_task; /* ACME task to wake on completion, or NULL */
|
||||||
|
struct ist txt; /* first TXT record found */
|
||||||
|
int (*success_cb)(struct resolv_requester *, struct dns_counters *);
|
||||||
|
int (*error_cb)(struct resolv_requester *, int);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _HAPROXY_ACME_RESOLVERS_T_H */
|
||||||
18
include/haproxy/acme_resolvers.h
Normal file
18
include/haproxy/acme_resolvers.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#ifndef _HAPROXY_ACME_RESOLVERS_H
|
||||||
|
#define _HAPROXY_ACME_RESOLVERS_H
|
||||||
|
|
||||||
|
#include <haproxy/openssl-compat.h>
|
||||||
|
|
||||||
|
#if defined(HAVE_ACME)
|
||||||
|
|
||||||
|
#include <haproxy/acme_resolvers-t.h>
|
||||||
|
#include <haproxy/acme-t.h>
|
||||||
|
#include <haproxy/resolvers-t.h>
|
||||||
|
|
||||||
|
struct acme_rslv *acme_rslv_start(struct acme_auth *auth, unsigned int *dnstasks, char **errmsg);
|
||||||
|
void acme_rslv_free(struct acme_rslv *rslv);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _HAPROXY_ACME_RESOLVERS_H */
|
||||||
@ -151,6 +151,7 @@ struct act_rule {
|
|||||||
struct ist str; /* string param (reason, header name, ...) */
|
struct ist str; /* string param (reason, header name, ...) */
|
||||||
struct lf_expr fmt; /* log-format compatible expression */
|
struct lf_expr fmt; /* log-format compatible expression */
|
||||||
struct my_regex *re; /* used by replace-header/value/uri/path */
|
struct my_regex *re; /* used by replace-header/value/uri/path */
|
||||||
|
struct sample_expr *expr; /* sample expression used by HTTP action */
|
||||||
} http; /* args used by some HTTP rules */
|
} http; /* args used by some HTTP rules */
|
||||||
struct http_reply *http_reply; /* HTTP response to be used by return/deny/tarpit rules */
|
struct http_reply *http_reply; /* HTTP response to be used by return/deny/tarpit rules */
|
||||||
struct redirect_rule *redir; /* redirect rule or "http-request redirect" */
|
struct redirect_rule *redir; /* redirect rule or "http-request redirect" */
|
||||||
@ -198,6 +199,11 @@ struct act_rule {
|
|||||||
struct server *srv; /* target server to attach the connection */
|
struct server *srv; /* target server to attach the connection */
|
||||||
struct sample_expr *name; /* used to differentiate idle connections */
|
struct sample_expr *name; /* used to differentiate idle connections */
|
||||||
} attach_srv; /* 'attach-srv' rule */
|
} attach_srv; /* 'attach-srv' rule */
|
||||||
|
struct {
|
||||||
|
enum log_orig_id orig;
|
||||||
|
char *profile_name;
|
||||||
|
struct log_profile *profile;
|
||||||
|
} do_log; /* 'do-log' action */
|
||||||
struct {
|
struct {
|
||||||
int value;
|
int value;
|
||||||
struct sample_expr *expr;
|
struct sample_expr *expr;
|
||||||
|
|||||||
@ -99,8 +99,11 @@ static inline int be_is_eligible(const struct proxy *be)
|
|||||||
/* set the time of last session on the backend */
|
/* set the time of last session on the backend */
|
||||||
static inline void be_set_sess_last(struct proxy *be)
|
static inline void be_set_sess_last(struct proxy *be)
|
||||||
{
|
{
|
||||||
|
uint now_sec = ns_to_sec(now_ns);
|
||||||
|
|
||||||
if (be->be_counters.shared.tg)
|
if (be->be_counters.shared.tg)
|
||||||
HA_ATOMIC_STORE(&be->be_counters.shared.tg[tgid - 1]->last_sess, ns_to_sec(now_ns));
|
if (HA_ATOMIC_LOAD(&be->be_counters.shared.tg[tgid - 1]->last_sess) != now_sec)
|
||||||
|
HA_ATOMIC_STORE(&be->be_counters.shared.tg[tgid - 1]->last_sess, now_sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function returns non-zero if the designated server will be
|
/* This function returns non-zero if the designated server will be
|
||||||
|
|||||||
@ -59,6 +59,7 @@ enum chk_result {
|
|||||||
#define CHK_ST_FASTINTER 0x0400 /* force fastinter check */
|
#define CHK_ST_FASTINTER 0x0400 /* force fastinter check */
|
||||||
#define CHK_ST_READY 0x0800 /* check ready to migrate or run, see below */
|
#define CHK_ST_READY 0x0800 /* check ready to migrate or run, see below */
|
||||||
#define CHK_ST_SLEEPING 0x1000 /* check was sleeping, i.e. not currently bound to a thread, see below */
|
#define CHK_ST_SLEEPING 0x1000 /* check was sleeping, i.e. not currently bound to a thread, see below */
|
||||||
|
#define CHK_ST_USE_SMALL_BUFF 0x2000 /* Use small buffers if possible for the request */
|
||||||
|
|
||||||
/* 4 possible states for CHK_ST_SLEEPING and CHK_ST_READY:
|
/* 4 possible states for CHK_ST_SLEEPING and CHK_ST_READY:
|
||||||
* SLP RDY State Description
|
* SLP RDY State Description
|
||||||
@ -154,7 +155,7 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct tcpcheck_rule;
|
struct tcpcheck_rule;
|
||||||
struct tcpcheck_rules;
|
struct tcpcheck;
|
||||||
|
|
||||||
struct check {
|
struct check {
|
||||||
enum obj_type obj_type; /* object type == OBJ_TYPE_CHECK */
|
enum obj_type obj_type; /* object type == OBJ_TYPE_CHECK */
|
||||||
@ -173,7 +174,7 @@ struct check {
|
|||||||
signed char use_ssl; /* use SSL for health checks (1: on, 0: server mode, -1: off) */
|
signed char use_ssl; /* use SSL for health checks (1: on, 0: server mode, -1: off) */
|
||||||
int send_proxy; /* send a PROXY protocol header with checks */
|
int send_proxy; /* send a PROXY protocol header with checks */
|
||||||
int reuse_pool; /* try to reuse idle connections */
|
int reuse_pool; /* try to reuse idle connections */
|
||||||
struct tcpcheck_rules *tcpcheck_rules; /* tcp-check send / expect rules */
|
struct tcpcheck *tcpcheck; /* tcp-check to use to perform a health-check */
|
||||||
struct tcpcheck_rule *current_step; /* current step when using tcpcheck */
|
struct tcpcheck_rule *current_step; /* current step when using tcpcheck */
|
||||||
int inter, fastinter, downinter; /* checks: time in milliseconds */
|
int inter, fastinter, downinter; /* checks: time in milliseconds */
|
||||||
enum chk_result result; /* health-check result : CHK_RES_* */
|
enum chk_result result; /* health-check result : CHK_RES_* */
|
||||||
@ -188,6 +189,7 @@ struct check {
|
|||||||
char **envp; /* the environment to use if running a process-based check */
|
char **envp; /* the environment to use if running a process-based check */
|
||||||
struct pid_list *curpid; /* entry in pid_list used for current process-based test, or -1 if not in test */
|
struct pid_list *curpid; /* entry in pid_list used for current process-based test, or -1 if not in test */
|
||||||
struct sockaddr_storage addr; /* the address to check */
|
struct sockaddr_storage addr; /* the address to check */
|
||||||
|
struct protocol *proto; /* protocol used for check, may be different from the server's one */
|
||||||
char *pool_conn_name; /* conn name used on reuse */
|
char *pool_conn_name; /* conn name used on reuse */
|
||||||
char *sni; /* Server name */
|
char *sni; /* Server name */
|
||||||
char *alpn_str; /* ALPN to use for checks */
|
char *alpn_str; /* ALPN to use for checks */
|
||||||
|
|||||||
@ -78,7 +78,7 @@ struct task *process_chk(struct task *t, void *context, unsigned int state);
|
|||||||
struct task *srv_chk_io_cb(struct task *t, void *ctx, unsigned int state);
|
struct task *srv_chk_io_cb(struct task *t, void *ctx, unsigned int state);
|
||||||
|
|
||||||
int check_buf_available(void *target);
|
int check_buf_available(void *target);
|
||||||
struct buffer *check_get_buf(struct check *check, struct buffer *bptr);
|
struct buffer *check_get_buf(struct check *check, struct buffer *bptr, unsigned int small_buffer);
|
||||||
void check_release_buf(struct check *check, struct buffer *bptr);
|
void check_release_buf(struct check *check, struct buffer *bptr);
|
||||||
const char *init_check(struct check *check, int type);
|
const char *init_check(struct check *check, int type);
|
||||||
void free_check(struct check *check);
|
void free_check(struct check *check);
|
||||||
|
|||||||
@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
extern struct pool_head *pool_head_trash;
|
extern struct pool_head *pool_head_trash;
|
||||||
extern struct pool_head *pool_head_large_trash;
|
extern struct pool_head *pool_head_large_trash;
|
||||||
|
extern struct pool_head *pool_head_small_trash;
|
||||||
|
|
||||||
/* function prototypes */
|
/* function prototypes */
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ int chunk_strcmp(const struct buffer *chk, const char *str);
|
|||||||
int chunk_strcasecmp(const struct buffer *chk, const char *str);
|
int chunk_strcasecmp(const struct buffer *chk, const char *str);
|
||||||
struct buffer *get_trash_chunk(void);
|
struct buffer *get_trash_chunk(void);
|
||||||
struct buffer *get_large_trash_chunk(void);
|
struct buffer *get_large_trash_chunk(void);
|
||||||
|
struct buffer *get_small_trash_chunk(void);
|
||||||
struct buffer *get_trash_chunk_sz(size_t size);
|
struct buffer *get_trash_chunk_sz(size_t size);
|
||||||
struct buffer *get_larger_trash_chunk(struct buffer *chunk);
|
struct buffer *get_larger_trash_chunk(struct buffer *chunk);
|
||||||
int init_trash_buffers(int first);
|
int init_trash_buffers(int first);
|
||||||
@ -133,6 +135,29 @@ static forceinline struct buffer *alloc_large_trash_chunk(void)
|
|||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a small trash chunk from the reentrant pool. The buffer starts at
|
||||||
|
* the end of the chunk. This chunk must be freed using free_trash_chunk(). This
|
||||||
|
* call may fail and the caller is responsible for checking that the returned
|
||||||
|
* pointer is not NULL.
|
||||||
|
*/
|
||||||
|
static forceinline struct buffer *alloc_small_trash_chunk(void)
|
||||||
|
{
|
||||||
|
struct buffer *chunk;
|
||||||
|
|
||||||
|
if (!pool_head_small_trash)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
chunk = pool_alloc(pool_head_small_trash);
|
||||||
|
if (chunk) {
|
||||||
|
char *buf = (char *)chunk + sizeof(struct buffer);
|
||||||
|
*buf = 0;
|
||||||
|
chunk_init(chunk, buf,
|
||||||
|
pool_head_small_trash->size - sizeof(struct buffer));
|
||||||
|
}
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate a trash chunk accordingly to the requested size. This chunk must be
|
* Allocate a trash chunk accordingly to the requested size. This chunk must be
|
||||||
* freed using free_trash_chunk(). This call may fail and the caller is
|
* freed using free_trash_chunk(). This call may fail and the caller is
|
||||||
@ -140,7 +165,9 @@ static forceinline struct buffer *alloc_large_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 (likely(size <= pool_head_trash->size))
|
if (pool_head_small_trash && size <= pool_head_small_trash->size)
|
||||||
|
return alloc_small_trash_chunk();
|
||||||
|
else if (size <= pool_head_trash->size)
|
||||||
return alloc_trash_chunk();
|
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();
|
||||||
@ -153,10 +180,12 @@ static forceinline struct buffer *alloc_trash_chunk_sz(size_t size)
|
|||||||
*/
|
*/
|
||||||
static forceinline void free_trash_chunk(struct buffer *chunk)
|
static forceinline void free_trash_chunk(struct buffer *chunk)
|
||||||
{
|
{
|
||||||
if (likely(chunk && chunk->size == pool_head_trash->size - sizeof(struct buffer)))
|
if (pool_head_small_trash && chunk && chunk->size == pool_head_small_trash->size - sizeof(struct buffer))
|
||||||
pool_free(pool_head_trash, chunk);
|
pool_free(pool_head_small_trash, chunk);
|
||||||
else
|
else if (pool_head_large_trash && chunk && chunk->size == pool_head_large_trash->size - sizeof(struct buffer))
|
||||||
pool_free(pool_head_large_trash, chunk);
|
pool_free(pool_head_large_trash, chunk);
|
||||||
|
else
|
||||||
|
pool_free(pool_head_trash, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copies chunk <src> into <chk>. Returns 0 in case of failure. */
|
/* copies chunk <src> into <chk>. Returns 0 in case of failure. */
|
||||||
|
|||||||
@ -130,7 +130,8 @@ enum {
|
|||||||
|
|
||||||
CO_FL_OPT_TOS = 0x00000020, /* connection has a special sockopt tos */
|
CO_FL_OPT_TOS = 0x00000020, /* connection has a special sockopt tos */
|
||||||
|
|
||||||
/* unused : 0x00000040, 0x00000080 */
|
CO_FL_QSTRM_SEND = 0x00000040, /* connection uses QMux protocol, needs to exchange transport parameters before starting mux layer */
|
||||||
|
CO_FL_QSTRM_RECV = 0x00000080, /* connection uses QMux protocol, needs to exchange transport parameters before starting mux layer */
|
||||||
|
|
||||||
/* These flags indicate whether the Control and Transport layers are initialized */
|
/* These flags indicate whether the Control and Transport layers are initialized */
|
||||||
CO_FL_CTRL_READY = 0x00000100, /* FD was registered, fd_delete() needed */
|
CO_FL_CTRL_READY = 0x00000100, /* FD was registered, fd_delete() needed */
|
||||||
@ -212,13 +213,14 @@ static forceinline char *conn_show_flags(char *buf, size_t len, const char *deli
|
|||||||
/* flags */
|
/* flags */
|
||||||
_(CO_FL_SAFE_LIST, _(CO_FL_IDLE_LIST, _(CO_FL_CTRL_READY,
|
_(CO_FL_SAFE_LIST, _(CO_FL_IDLE_LIST, _(CO_FL_CTRL_READY,
|
||||||
_(CO_FL_REVERSED, _(CO_FL_ACT_REVERSING, _(CO_FL_OPT_MARK, _(CO_FL_OPT_TOS,
|
_(CO_FL_REVERSED, _(CO_FL_ACT_REVERSING, _(CO_FL_OPT_MARK, _(CO_FL_OPT_TOS,
|
||||||
|
_(CO_FL_QSTRM_SEND, _(CO_FL_QSTRM_RECV,
|
||||||
_(CO_FL_XPRT_READY, _(CO_FL_WANT_DRAIN, _(CO_FL_WAIT_ROOM, _(CO_FL_SSL_NO_CACHED_INFO, _(CO_FL_EARLY_SSL_HS,
|
_(CO_FL_XPRT_READY, _(CO_FL_WANT_DRAIN, _(CO_FL_WAIT_ROOM, _(CO_FL_SSL_NO_CACHED_INFO, _(CO_FL_EARLY_SSL_HS,
|
||||||
_(CO_FL_EARLY_DATA, _(CO_FL_SOCKS4_SEND, _(CO_FL_SOCKS4_RECV, _(CO_FL_SOCK_RD_SH,
|
_(CO_FL_EARLY_DATA, _(CO_FL_SOCKS4_SEND, _(CO_FL_SOCKS4_RECV, _(CO_FL_SOCK_RD_SH,
|
||||||
_(CO_FL_SOCK_WR_SH, _(CO_FL_ERROR, _(CO_FL_FDLESS, _(CO_FL_WAIT_L4_CONN,
|
_(CO_FL_SOCK_WR_SH, _(CO_FL_ERROR, _(CO_FL_FDLESS, _(CO_FL_WAIT_L4_CONN,
|
||||||
_(CO_FL_WAIT_L6_CONN, _(CO_FL_SEND_PROXY, _(CO_FL_ACCEPT_PROXY, _(CO_FL_ACCEPT_CIP,
|
_(CO_FL_WAIT_L6_CONN, _(CO_FL_SEND_PROXY, _(CO_FL_ACCEPT_PROXY, _(CO_FL_ACCEPT_CIP,
|
||||||
_(CO_FL_SSL_WAIT_HS, _(CO_FL_PRIVATE, _(CO_FL_RCVD_PROXY, _(CO_FL_SESS_IDLE,
|
_(CO_FL_SSL_WAIT_HS, _(CO_FL_PRIVATE, _(CO_FL_RCVD_PROXY, _(CO_FL_SESS_IDLE,
|
||||||
_(CO_FL_XPRT_TRACKED
|
_(CO_FL_XPRT_TRACKED
|
||||||
)))))))))))))))))))))))))))));
|
)))))))))))))))))))))))))))))));
|
||||||
/* epilogue */
|
/* epilogue */
|
||||||
_(~0U);
|
_(~0U);
|
||||||
return buf;
|
return buf;
|
||||||
@ -345,6 +347,7 @@ enum {
|
|||||||
XPRT_SSL = 1,
|
XPRT_SSL = 1,
|
||||||
XPRT_HANDSHAKE = 2,
|
XPRT_HANDSHAKE = 2,
|
||||||
XPRT_QUIC = 3,
|
XPRT_QUIC = 3,
|
||||||
|
XPRT_QSTRM = 4,
|
||||||
XPRT_ENTRIES /* must be last one */
|
XPRT_ENTRIES /* must be last one */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -356,6 +359,7 @@ enum {
|
|||||||
MX_FL_NO_UPG = 0x00000004, /* set if mux does not support any upgrade */
|
MX_FL_NO_UPG = 0x00000004, /* set if mux does not support any upgrade */
|
||||||
MX_FL_FRAMED = 0x00000008, /* mux working on top of a framed transport layer (QUIC) */
|
MX_FL_FRAMED = 0x00000008, /* mux working on top of a framed transport layer (QUIC) */
|
||||||
MX_FL_REVERSABLE = 0x00000010, /* mux supports connection reversal */
|
MX_FL_REVERSABLE = 0x00000010, /* mux supports connection reversal */
|
||||||
|
MX_FL_EXPERIMENTAL = 0x00000020, /* requires experimental support directives */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* PROTO token registration */
|
/* PROTO token registration */
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
#include <haproxy/listener-t.h>
|
#include <haproxy/listener-t.h>
|
||||||
#include <haproxy/obj_type.h>
|
#include <haproxy/obj_type.h>
|
||||||
#include <haproxy/pool-t.h>
|
#include <haproxy/pool-t.h>
|
||||||
|
#include <haproxy/protocol.h>
|
||||||
#include <haproxy/server.h>
|
#include <haproxy/server.h>
|
||||||
#include <haproxy/session-t.h>
|
#include <haproxy/session-t.h>
|
||||||
#include <haproxy/task-t.h>
|
#include <haproxy/task-t.h>
|
||||||
@ -609,13 +610,13 @@ void list_mux_proto(FILE *out);
|
|||||||
*/
|
*/
|
||||||
static inline const struct mux_proto_list *conn_get_best_mux_entry(
|
static inline const struct mux_proto_list *conn_get_best_mux_entry(
|
||||||
const struct ist mux_proto,
|
const struct ist mux_proto,
|
||||||
int proto_side, int proto_mode)
|
int proto_side, int proto_is_quic, int proto_mode)
|
||||||
{
|
{
|
||||||
struct mux_proto_list *item;
|
struct mux_proto_list *item;
|
||||||
struct mux_proto_list *fallback = NULL;
|
struct mux_proto_list *fallback = NULL;
|
||||||
|
|
||||||
list_for_each_entry(item, &mux_proto_list.list, list) {
|
list_for_each_entry(item, &mux_proto_list.list, list) {
|
||||||
if (!(item->side & proto_side) || !(item->mode & proto_mode))
|
if (!(item->side & proto_side) || !(item->mode & proto_mode) || (proto_is_quic && !(item->mux->flags & MX_FL_FRAMED)))
|
||||||
continue;
|
continue;
|
||||||
if (istlen(mux_proto) && isteq(mux_proto, item->token))
|
if (istlen(mux_proto) && isteq(mux_proto, item->token))
|
||||||
return item;
|
return item;
|
||||||
@ -640,7 +641,7 @@ static inline const struct mux_ops *conn_get_best_mux(struct connection *conn,
|
|||||||
{
|
{
|
||||||
const struct mux_proto_list *item;
|
const struct mux_proto_list *item;
|
||||||
|
|
||||||
item = conn_get_best_mux_entry(mux_proto, proto_side, proto_mode);
|
item = conn_get_best_mux_entry(mux_proto, proto_side, proto_is_quic(conn->ctrl), proto_mode);
|
||||||
|
|
||||||
return item ? item->mux : NULL;
|
return item ? item->mux : NULL;
|
||||||
}
|
}
|
||||||
@ -690,6 +691,12 @@ static inline int conn_is_ssl(struct connection *conn)
|
|||||||
return !!conn_get_ssl_sock_ctx(conn);
|
return !!conn_get_ssl_sock_ctx(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns true if connection runs over QUIC. */
|
||||||
|
static inline int conn_is_quic(const struct connection *conn)
|
||||||
|
{
|
||||||
|
return conn->flags & CO_FL_FDLESS;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns true if connection must be reversed. */
|
/* Returns true if connection must be reversed. */
|
||||||
static inline int conn_is_reverse(const struct connection *conn)
|
static inline int conn_is_reverse(const struct connection *conn)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
extern struct pool_head *pool_head_buffer;
|
extern struct pool_head *pool_head_buffer;
|
||||||
extern struct pool_head *pool_head_large_buffer;
|
extern struct pool_head *pool_head_large_buffer;
|
||||||
|
extern struct pool_head *pool_head_small_buffer;
|
||||||
|
|
||||||
int init_buffer(void);
|
int init_buffer(void);
|
||||||
void buffer_dump(FILE *o, struct buffer *b, int from, int to);
|
void buffer_dump(FILE *o, struct buffer *b, int from, int to);
|
||||||
@ -66,6 +67,12 @@ static inline int b_is_large_sz(size_t sz)
|
|||||||
return (pool_head_large_buffer && sz == pool_head_large_buffer->size);
|
return (pool_head_large_buffer && sz == pool_head_large_buffer->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return 1 if <sz> is the size of a small buffer */
|
||||||
|
static inline int b_is_small_sz(size_t sz)
|
||||||
|
{
|
||||||
|
return (pool_head_small_buffer && sz == pool_head_small_buffer->size);
|
||||||
|
}
|
||||||
|
|
||||||
/* Return 1 if <bug> is a default buffer */
|
/* Return 1 if <bug> is a default buffer */
|
||||||
static inline int b_is_default(struct buffer *buf)
|
static inline int b_is_default(struct buffer *buf)
|
||||||
{
|
{
|
||||||
@ -78,6 +85,12 @@ static inline int b_is_large(struct buffer *buf)
|
|||||||
return b_is_large_sz(b_size(buf));
|
return b_is_large_sz(b_size(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return 1 if <buf> is a small buffer */
|
||||||
|
static inline int b_is_small(struct buffer *buf)
|
||||||
|
{
|
||||||
|
return b_is_small_sz(b_size(buf));
|
||||||
|
}
|
||||||
|
|
||||||
/**************************************************/
|
/**************************************************/
|
||||||
/* Functions below are used for buffer allocation */
|
/* Functions below are used for buffer allocation */
|
||||||
/**************************************************/
|
/**************************************************/
|
||||||
@ -172,6 +185,8 @@ static inline char *__b_get_emergency_buf(void)
|
|||||||
* than the default buffers */ \
|
* than the default buffers */ \
|
||||||
if (unlikely(b_is_large_sz(sz))) \
|
if (unlikely(b_is_large_sz(sz))) \
|
||||||
pool_free(pool_head_large_buffer, area); \
|
pool_free(pool_head_large_buffer, area); \
|
||||||
|
else if (unlikely(b_is_small_sz(sz))) \
|
||||||
|
pool_free(pool_head_small_buffer, area); \
|
||||||
else if (th_ctx->emergency_bufs_left < global.tune.reserved_bufs) \
|
else if (th_ctx->emergency_bufs_left < global.tune.reserved_bufs) \
|
||||||
th_ctx->emergency_bufs[th_ctx->emergency_bufs_left++] = area; \
|
th_ctx->emergency_bufs[th_ctx->emergency_bufs_left++] = area; \
|
||||||
else \
|
else \
|
||||||
@ -185,6 +200,35 @@ static inline char *__b_get_emergency_buf(void)
|
|||||||
__b_free((_buf)); \
|
__b_free((_buf)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
static inline struct buffer *b_alloc_small(struct buffer *buf)
|
||||||
|
{
|
||||||
|
char *area = NULL;
|
||||||
|
|
||||||
|
if (!buf->size) {
|
||||||
|
area = pool_alloc(pool_head_small_buffer);
|
||||||
|
if (!area)
|
||||||
|
return NULL;
|
||||||
|
buf->area = area;
|
||||||
|
buf->size = global.tune.bufsize_small;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct buffer *b_alloc_large(struct buffer *buf)
|
||||||
|
{
|
||||||
|
char *area = NULL;
|
||||||
|
|
||||||
|
if (!buf->size) {
|
||||||
|
area = pool_alloc(pool_head_large_buffer);
|
||||||
|
if (!area)
|
||||||
|
return NULL;
|
||||||
|
buf->area = area;
|
||||||
|
buf->size = global.tune.bufsize_large;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
/* Offer one or multiple buffer currently belonging to target <from> to whoever
|
/* Offer one or multiple buffer currently belonging to target <from> to whoever
|
||||||
* needs one. Any pointer is valid for <from>, including NULL. Its purpose is
|
* needs one. Any pointer is valid for <from>, including NULL. Its purpose is
|
||||||
* to avoid passing a buffer to oneself in case of failed allocations (e.g.
|
* to avoid passing a buffer to oneself in case of failed allocations (e.g.
|
||||||
|
|||||||
@ -290,6 +290,36 @@ static inline int http_status_matches(const long *array, uint status)
|
|||||||
return ha_bit_test(status - 100, array);
|
return ha_bit_test(status - 100, array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function returns 1 if the header is one of the immutable headers.
|
||||||
|
* Forbidden headers are the ones that must not be rewritten. Function returns
|
||||||
|
* 0 if a header can be rewritten
|
||||||
|
*/
|
||||||
|
static inline int is_immutable_header(struct ist hdr)
|
||||||
|
{
|
||||||
|
switch (hdr.len) {
|
||||||
|
case 6:
|
||||||
|
return isteqi(hdr, ist("expect"));
|
||||||
|
case 7:
|
||||||
|
return isteqi(hdr, ist("trailer")) ||
|
||||||
|
isteqi(hdr, ist("upgrade"));
|
||||||
|
case 10:
|
||||||
|
return isteqi(hdr, ist("connection")) ||
|
||||||
|
isteqi(hdr, ist("keep-alive"));
|
||||||
|
case 14:
|
||||||
|
return isteqi(hdr, ist("content-length"));
|
||||||
|
case 16:
|
||||||
|
return isteqi(hdr, ist("proxy-connection"));
|
||||||
|
case 17:
|
||||||
|
return isteqi(hdr, ist("transfer-encoding"));
|
||||||
|
case 18:
|
||||||
|
return isteqi(hdr, ist("proxy-authenticate"));
|
||||||
|
case 19:
|
||||||
|
return isteqi(hdr, ist("proxy-authorization"));
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _HAPROXY_HTTP_H */
|
#endif /* _HAPROXY_HTTP_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -93,4 +93,22 @@ struct http_errors {
|
|||||||
struct list list; /* http-errors list */
|
struct list list; /* http-errors list */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Indicates the keyword origin of an http-error definition. This is used in
|
||||||
|
* <conf_errors> type to indicate which part of the internal union should be
|
||||||
|
* manipulated.
|
||||||
|
*/
|
||||||
|
enum http_err_directive {
|
||||||
|
HTTP_ERR_DIRECTIVE_SECTION = 0, /* "errorfiles" keyword referencing a http-errors section */
|
||||||
|
HTTP_ERR_DIRECTIVE_INLINE, /* "errorfile" keyword with inline error definition */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Used with "errorfiles" directives. It indicates for each known HTTP error
|
||||||
|
* status codes if they are defined in the target http-errors section.
|
||||||
|
*/
|
||||||
|
enum http_err_import {
|
||||||
|
HTTP_ERR_IMPORT_NO = 0,
|
||||||
|
HTTP_ERR_IMPORT_IMPLICIT, /* import every errcode defined in a section */
|
||||||
|
HTTP_ERR_IMPORT_EXPLICIT, /* import a specific errcode from a section */
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _HAPROXY_HTTP_HTX_T_H */
|
#endif /* _HAPROXY_HTTP_HTX_T_H */
|
||||||
|
|||||||
@ -78,6 +78,7 @@ struct buffer *http_load_errorfile(const char *file, char **errmsg);
|
|||||||
struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg);
|
struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg);
|
||||||
struct buffer *http_parse_errorfile(int status, const char *file, char **errmsg);
|
struct buffer *http_parse_errorfile(int status, const char *file, char **errmsg);
|
||||||
struct buffer *http_parse_errorloc(int errloc, int status, const char *url, char **errmsg);
|
struct buffer *http_parse_errorloc(int errloc, int status, const char *url, char **errmsg);
|
||||||
|
int proxy_check_http_errors(struct proxy *px);
|
||||||
int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx, char **errmsg);
|
int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx, char **errmsg);
|
||||||
void proxy_release_conf_errors(struct proxy *px);
|
void proxy_release_conf_errors(struct proxy *px);
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,16 @@ size_t htx_add_data(struct htx *htx, const struct ist data);
|
|||||||
struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data);
|
struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data);
|
||||||
void htx_move_blk_before(struct htx *htx, struct htx_blk **blk, struct htx_blk **ref);
|
void htx_move_blk_before(struct htx *htx, struct htx_blk **blk, struct htx_blk **ref);
|
||||||
int htx_append_msg(struct htx *dst, const struct htx *src);
|
int htx_append_msg(struct htx *dst, const struct htx *src);
|
||||||
|
struct buffer *htx_move_to_small_buffer(struct buffer *dst, struct buffer *src);
|
||||||
|
struct buffer *htx_move_to_large_buffer(struct buffer *dst, struct buffer *src);
|
||||||
|
struct buffer *htx_copy_to_small_buffer(struct buffer *dst, struct buffer *src);
|
||||||
|
struct buffer *htx_copy_to_large_buffer(struct buffer *dst, struct buffer *src);
|
||||||
|
|
||||||
|
#define HTX_XFER_DEFAULT 0x00000000 /* Default XFER: no partial xfer / remove blocks from source */
|
||||||
|
#define HTX_XFER_KEEP_SRC_BLKS 0x00000001 /* Don't remove xfer blocks from source messages during xfer */
|
||||||
|
#define HTX_XFER_PARTIAL_HDRS_COPY 0x00000002 /* Allow partial copy of headers and trailers part */
|
||||||
|
#define HTX_XFER_HDRS_ONLY 0x00000003 /* Only Transfer header blocks (start-line, header and EOH) */
|
||||||
|
size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int flags);
|
||||||
|
|
||||||
/* Functions and macros to get parts of the start-line or length of these
|
/* Functions and macros to get parts of the start-line or length of these
|
||||||
* parts. Request and response start-lines are both composed of 3 parts.
|
* parts. Request and response start-lines are both composed of 3 parts.
|
||||||
|
|||||||
@ -42,6 +42,8 @@ extern char clf_tcp_log_format[];
|
|||||||
extern char default_http_log_format[];
|
extern char default_http_log_format[];
|
||||||
extern char clf_http_log_format[];
|
extern char clf_http_log_format[];
|
||||||
extern char default_https_log_format[];
|
extern char default_https_log_format[];
|
||||||
|
extern char keylog_format_fc[];
|
||||||
|
extern char keylog_format_bc[];
|
||||||
|
|
||||||
extern char default_rfc5424_sd_log_format[];
|
extern char default_rfc5424_sd_log_format[];
|
||||||
|
|
||||||
|
|||||||
@ -81,9 +81,19 @@ struct qcc {
|
|||||||
struct quic_fctl fc; /* stream flow control applied on sending */
|
struct quic_fctl fc; /* stream flow control applied on sending */
|
||||||
uint64_t buf_in_flight; /* sum of currently allocated Tx buffer sizes */
|
uint64_t buf_in_flight; /* sum of currently allocated Tx buffer sizes */
|
||||||
struct list frms; /* list of STREAM frames ready for sent */
|
struct list frms; /* list of STREAM frames ready for sent */
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
/* quic */
|
||||||
struct quic_pacer pacer; /* engine used to pace emission */
|
struct quic_pacer pacer; /* engine used to pace emission */
|
||||||
int paced_sent_ctr; /* counter for when emission is interrupted due to pacing */
|
int paced_sent_ctr; /* counter for when emission is interrupted due to pacing */
|
||||||
|
};
|
||||||
|
/* qstrm */
|
||||||
|
struct buffer qstrm_buf;
|
||||||
|
};
|
||||||
} tx;
|
} tx;
|
||||||
|
struct {
|
||||||
|
struct buffer qstrm_buf;
|
||||||
|
} rx;
|
||||||
|
|
||||||
uint64_t largest_bidi_r; /* largest remote bidi stream ID opened. */
|
uint64_t largest_bidi_r; /* largest remote bidi stream ID opened. */
|
||||||
uint64_t largest_uni_r; /* largest remote uni stream ID opened. */
|
uint64_t largest_uni_r; /* largest remote uni stream ID opened. */
|
||||||
@ -164,13 +174,16 @@ struct qcs {
|
|||||||
struct bdata_ctr data; /* data utilization counter. Note that <tot> is now used for now as accounting may be difficult with ncbuf. */
|
struct bdata_ctr data; /* data utilization counter. Note that <tot> is now used for now as accounting may be difficult with ncbuf. */
|
||||||
} rx;
|
} rx;
|
||||||
struct {
|
struct {
|
||||||
|
union {
|
||||||
|
struct qc_stream_desc *stream; /* quic */
|
||||||
|
struct buffer qstrm_buf; /* qstrm */
|
||||||
|
};
|
||||||
struct quic_fctl fc; /* stream flow control applied on sending */
|
struct quic_fctl fc; /* stream flow control applied on sending */
|
||||||
struct quic_frame *msd_frm; /* MAX_STREAM_DATA frame prepared */
|
struct quic_frame *msd_frm; /* MAX_STREAM_DATA frame prepared */
|
||||||
} tx;
|
} tx;
|
||||||
|
|
||||||
struct eb64_node by_id;
|
struct eb64_node by_id;
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
struct qc_stream_desc *stream;
|
|
||||||
|
|
||||||
struct list el_recv; /* element of qcc.recv_list */
|
struct list el_recv; /* element of qcc.recv_list */
|
||||||
struct list el_send; /* element of qcc.send_list */
|
struct list el_send; /* element of qcc.send_list */
|
||||||
|
|||||||
14
include/haproxy/mux_quic_priv.h
Normal file
14
include/haproxy/mux_quic_priv.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef _HAPROXY_MUX_QUIC_PRIV_H
|
||||||
|
#define _HAPROXY_MUX_QUIC_PRIV_H
|
||||||
|
|
||||||
|
/* This header file should only be used by QUIC-MUX layer internally. */
|
||||||
|
|
||||||
|
#include <haproxy/mux_quic-t.h>
|
||||||
|
|
||||||
|
void qcs_idle_open(struct qcs *qcs);
|
||||||
|
void qcs_close_local(struct qcs *qcs);
|
||||||
|
int qcs_is_completed(struct qcs *qcs);
|
||||||
|
|
||||||
|
uint64_t qcs_prep_bytes(const struct qcs *qcs);
|
||||||
|
|
||||||
|
#endif /* _HAPROXY_MUX_QUIC_PRIV_H */
|
||||||
10
include/haproxy/mux_quic_qstrm.h
Normal file
10
include/haproxy/mux_quic_qstrm.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef _HAPROXY_MUX_QUIC_QSTRM_H
|
||||||
|
#define _HAPROXY_MUX_QUIC_QSTRM_H
|
||||||
|
|
||||||
|
#include <haproxy/mux_quic.h>
|
||||||
|
|
||||||
|
int qcc_qstrm_recv(struct qcc *qcc);
|
||||||
|
|
||||||
|
int qcc_qstrm_send_frames(struct qcc *qcc, struct list *frms);
|
||||||
|
|
||||||
|
#endif /* _HAPROXY_MUX_QUIC_QSTRM_H */
|
||||||
@ -47,6 +47,7 @@ enum obj_type {
|
|||||||
OBJ_TYPE_DGRAM, /* object is a struct quic_dgram */
|
OBJ_TYPE_DGRAM, /* object is a struct quic_dgram */
|
||||||
#endif
|
#endif
|
||||||
OBJ_TYPE_HATERM, /* object is a struct hstream */
|
OBJ_TYPE_HATERM, /* object is a struct hstream */
|
||||||
|
OBJ_TYPE_ACME_RSLV, /* object is a struct acme_rslv */
|
||||||
OBJ_TYPE_ENTRIES /* last one : number of entries */
|
OBJ_TYPE_ENTRIES /* last one : number of entries */
|
||||||
} __attribute__((packed)) ;
|
} __attribute__((packed)) ;
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
#ifndef _HAPROXY_OBJ_TYPE_H
|
#ifndef _HAPROXY_OBJ_TYPE_H
|
||||||
#define _HAPROXY_OBJ_TYPE_H
|
#define _HAPROXY_OBJ_TYPE_H
|
||||||
|
|
||||||
|
#include <haproxy/acme_resolvers-t.h>
|
||||||
#include <haproxy/api.h>
|
#include <haproxy/api.h>
|
||||||
#include <haproxy/applet-t.h>
|
#include <haproxy/applet-t.h>
|
||||||
#include <haproxy/check-t.h>
|
#include <haproxy/check-t.h>
|
||||||
@ -56,6 +57,7 @@ static inline const char *obj_type_name(const enum obj_type *t)
|
|||||||
case OBJ_TYPE_SC: return "SC";
|
case OBJ_TYPE_SC: return "SC";
|
||||||
case OBJ_TYPE_STREAM: return "STREAM";
|
case OBJ_TYPE_STREAM: return "STREAM";
|
||||||
case OBJ_TYPE_CHECK: return "CHECK";
|
case OBJ_TYPE_CHECK: return "CHECK";
|
||||||
|
case OBJ_TYPE_ACME_RSLV: return "ACME_RSLV";
|
||||||
#ifdef USE_QUIC
|
#ifdef USE_QUIC
|
||||||
case OBJ_TYPE_DGRAM: return "DGRAM";
|
case OBJ_TYPE_DGRAM: return "DGRAM";
|
||||||
#endif
|
#endif
|
||||||
@ -203,6 +205,18 @@ static inline struct hstream *objt_hstream(enum obj_type *t)
|
|||||||
return __objt_hstream(t);
|
return __objt_hstream(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct acme_rslv *__objt_acme_rslv(enum obj_type *t)
|
||||||
|
{
|
||||||
|
return container_of(t, struct acme_rslv, obj_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct acme_rslv *objt_acme_rslv(enum obj_type *t)
|
||||||
|
{
|
||||||
|
if (!t || *t != OBJ_TYPE_ACME_RSLV)
|
||||||
|
return NULL;
|
||||||
|
return __objt_acme_rslv(t);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_QUIC
|
#ifdef USE_QUIC
|
||||||
static inline struct quic_dgram *__objt_dgram(enum obj_type *t)
|
static inline struct quic_dgram *__objt_dgram(enum obj_type *t)
|
||||||
{
|
{
|
||||||
@ -231,6 +245,7 @@ static inline void *obj_base_ptr(enum obj_type *t)
|
|||||||
case OBJ_TYPE_SC: return __objt_sc(t);
|
case OBJ_TYPE_SC: return __objt_sc(t);
|
||||||
case OBJ_TYPE_STREAM: return __objt_stream(t);
|
case OBJ_TYPE_STREAM: return __objt_stream(t);
|
||||||
case OBJ_TYPE_CHECK: return __objt_check(t);
|
case OBJ_TYPE_CHECK: return __objt_check(t);
|
||||||
|
case OBJ_TYPE_ACME_RSLV: return __objt_acme_rslv(t);
|
||||||
#ifdef USE_QUIC
|
#ifdef USE_QUIC
|
||||||
case OBJ_TYPE_DGRAM: return __objt_dgram(t);
|
case OBJ_TYPE_DGRAM: return __objt_dgram(t);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -124,6 +124,12 @@ static inline int real_family(int ss_family)
|
|||||||
return fam ? fam->real_family : AF_UNSPEC;
|
return fam ? fam->real_family : AF_UNSPEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int proto_is_quic(const struct protocol *proto)
|
||||||
|
{
|
||||||
|
return (proto->proto_type == PROTO_TYPE_DGRAM &&
|
||||||
|
proto->xprt_type == PROTO_TYPE_STREAM);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _HAPROXY_PROTOCOL_H */
|
#endif /* _HAPROXY_PROTOCOL_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -117,10 +117,9 @@ enum PR_SRV_STATE_FILE {
|
|||||||
#define PR_O_HTTP_DROP_REQ_TRLS 0x04000000 /* Drop the request trailers when forwarding to the server */
|
#define PR_O_HTTP_DROP_REQ_TRLS 0x04000000 /* Drop the request trailers when forwarding to the server */
|
||||||
#define PR_O_HTTP_DROP_RES_TRLS 0x08000000 /* Drop response trailers when forwarding to the client */
|
#define PR_O_HTTP_DROP_RES_TRLS 0x08000000 /* Drop response trailers when forwarding to the client */
|
||||||
|
|
||||||
#define PR_O_TCPCHK_SSL 0x10000000 /* at least one TCPCHECK connect rule requires SSL */
|
/* unused: 0x10000000 */
|
||||||
#define PR_O_CONTSTATS 0x20000000 /* continuous counters */
|
#define PR_O_CONTSTATS 0x20000000 /* continuous counters */
|
||||||
#define PR_O_DISABLE404 0x40000000 /* Disable a server on a 404 response to a health-check */
|
/* unused: 0x40000000..0x80000000 */
|
||||||
/* unused: 0x80000000 */
|
|
||||||
|
|
||||||
/* bits for proxy->options2 */
|
/* bits for proxy->options2 */
|
||||||
#define PR_O2_SPLIC_REQ 0x00000001 /* transfer requests using linux kernel's splice() */
|
#define PR_O2_SPLIC_REQ 0x00000001 /* transfer requests using linux kernel's splice() */
|
||||||
@ -145,7 +144,7 @@ enum PR_SRV_STATE_FILE {
|
|||||||
|
|
||||||
#define PR_O2_NODELAY 0x00020000 /* fully interactive mode, never delay outgoing data */
|
#define PR_O2_NODELAY 0x00020000 /* fully interactive mode, never delay outgoing data */
|
||||||
#define PR_O2_USE_PXHDR 0x00040000 /* use Proxy-Connection for proxy requests */
|
#define PR_O2_USE_PXHDR 0x00040000 /* use Proxy-Connection for proxy requests */
|
||||||
#define PR_O2_CHK_SNDST 0x00080000 /* send the state of each server along with HTTP health checks */
|
/* unused: 0x00080000 */
|
||||||
|
|
||||||
#define PR_O2_SRC_ADDR 0x00100000 /* get the source ip and port for logs */
|
#define PR_O2_SRC_ADDR 0x00100000 /* get the source ip and port for logs */
|
||||||
|
|
||||||
@ -156,14 +155,17 @@ enum PR_SRV_STATE_FILE {
|
|||||||
#define PR_O2_RSTRICT_REQ_HDR_NAMES_NOOP 0x01000000 /* preserve request header names containing chars outside of [0-9a-zA-Z-] charset */
|
#define PR_O2_RSTRICT_REQ_HDR_NAMES_NOOP 0x01000000 /* preserve request header names containing chars outside of [0-9a-zA-Z-] charset */
|
||||||
#define PR_O2_RSTRICT_REQ_HDR_NAMES_MASK 0x01c00000 /* mask for restrict-http-header-names option */
|
#define PR_O2_RSTRICT_REQ_HDR_NAMES_MASK 0x01c00000 /* mask for restrict-http-header-names option */
|
||||||
|
|
||||||
/* unused : 0x02000000 ... 0x08000000 */
|
|
||||||
|
|
||||||
/* server health checks */
|
/* server health checks */
|
||||||
#define PR_O2_CHK_NONE 0x00000000 /* no L7 health checks configured (TCP by default) */
|
#define PR_O2_CHK_NONE 0x00000000 /* no L7 health checks configured (TCP by default) */
|
||||||
#define PR_O2_TCPCHK_CHK 0x90000000 /* use TCPCHK check for server health */
|
#define PR_O2_TCPCHK_CHK 0x02000000 /* use TCPCHK check for server health */
|
||||||
#define PR_O2_EXT_CHK 0xA0000000 /* use external command for server health */
|
#define PR_O2_EXT_CHK 0x04000000 /* use external command for server health */
|
||||||
/* unused: 0xB0000000 to 0xF000000, reserved for health checks */
|
#define PR_O2_CHK_ANY 0x06000000 /* Mask to cover any check */
|
||||||
#define PR_O2_CHK_ANY 0xF0000000 /* Mask to cover any check */
|
|
||||||
|
#define PR_O2_USE_SBUF_QUEUE 0x08000000 /* use small buffer for request when stream are queued*/
|
||||||
|
#define PR_O2_USE_SBUF_L7_RETRY 0x10000000 /* use small buffer for request when L7 retires are enabled */
|
||||||
|
#define PR_O2_USE_SBUF_CHECK 0x20000000 /* use small buffer for request's healthchecks */
|
||||||
|
#define PR_O2_USE_SBUF_ALL 0x38000000 /* all flags for use-large-buffer option */
|
||||||
|
/* unused : 0x40000000 ... 0x80000000 */
|
||||||
/* end of proxy->options2 */
|
/* end of proxy->options2 */
|
||||||
|
|
||||||
/* bits for proxy->options3 */
|
/* bits for proxy->options3 */
|
||||||
@ -241,12 +243,12 @@ enum PR_SRV_STATE_FILE {
|
|||||||
/* Proxy flags */
|
/* Proxy flags */
|
||||||
#define PR_FL_DISABLED 0x00000001 /* The proxy was disabled in the configuration (not at runtime) */
|
#define PR_FL_DISABLED 0x00000001 /* The proxy was disabled in the configuration (not at runtime) */
|
||||||
#define PR_FL_STOPPED 0x00000002 /* The proxy was stopped */
|
#define PR_FL_STOPPED 0x00000002 /* The proxy was stopped */
|
||||||
#define PR_FL_DEF_EXPLICIT_MODE 0x00000004 /* Proxy mode is explicitely defined - only used for defaults instance */
|
#define PR_FL_DEF_EXPLICIT_MODE 0x00000004 /* Proxy mode is explicitly defined - only used for defaults instance */
|
||||||
#define PR_FL_EXPLICIT_REF 0x00000008 /* The default proxy is explicitly referenced by another proxy */
|
#define PR_FL_EXPLICIT_REF 0x00000008 /* The default proxy is explicitly referenced by another proxy */
|
||||||
#define PR_FL_IMPLICIT_REF 0x00000010 /* The default proxy is implicitly referenced by another proxy */
|
#define PR_FL_IMPLICIT_REF 0x00000010 /* The default proxy is implicitly referenced by another proxy */
|
||||||
#define PR_FL_PAUSED 0x00000020 /* The proxy was paused at run time (reversible) */
|
#define PR_FL_PAUSED 0x00000020 /* The proxy was paused at run time (reversible) */
|
||||||
#define PR_FL_CHECKED 0x00000040 /* The proxy configuration was fully checked (including postparsing checks) */
|
#define PR_FL_CHECKED 0x00000040 /* The proxy configuration was fully checked (including postparsing checks) */
|
||||||
#define PR_FL_BE_UNPUBLISHED 0x00000080 /* The proxy cannot be targetted by content switching rules */
|
#define PR_FL_BE_UNPUBLISHED 0x00000080 /* The proxy cannot be targeted by content switching rules */
|
||||||
#define PR_FL_DELETED 0x00000100 /* Proxy has been deleted and must be manipulated with care */
|
#define PR_FL_DELETED 0x00000100 /* Proxy has been deleted and must be manipulated with care */
|
||||||
#define PR_FL_NON_PURGEABLE 0x00000200 /* Proxy referenced by config elements which prevent its runtime removal. */
|
#define PR_FL_NON_PURGEABLE 0x00000200 /* Proxy referenced by config elements which prevent its runtime removal. */
|
||||||
|
|
||||||
@ -442,7 +444,7 @@ struct proxy {
|
|||||||
struct stktable *table; /* table for storing sticking streams */
|
struct stktable *table; /* table for storing sticking streams */
|
||||||
|
|
||||||
struct task *task; /* the associated task, mandatory to manage rate limiting, stopping and resource shortage, NULL if disabled */
|
struct task *task; /* the associated task, mandatory to manage rate limiting, stopping and resource shortage, NULL if disabled */
|
||||||
struct tcpcheck_rules tcpcheck_rules; /* tcp-check send / expect rules */
|
struct tcpcheck tcpcheck; /* tcp-check to use to perform a health-check */
|
||||||
char *check_command; /* Command to use for external agent checks */
|
char *check_command; /* Command to use for external agent checks */
|
||||||
char *check_path; /* PATH environment to use for external agent checks */
|
char *check_path; /* PATH environment to use for external agent checks */
|
||||||
struct http_reply *replies[HTTP_ERR_SIZE]; /* HTTP replies for known errors */
|
struct http_reply *replies[HTTP_ERR_SIZE]; /* HTTP replies for known errors */
|
||||||
|
|||||||
@ -32,7 +32,6 @@
|
|||||||
#include <import/ebtree-t.h>
|
#include <import/ebtree-t.h>
|
||||||
#include <haproxy/buf-t.h>
|
#include <haproxy/buf-t.h>
|
||||||
#include <haproxy/list.h>
|
#include <haproxy/list.h>
|
||||||
#include <haproxy/quic_stream-t.h>
|
|
||||||
#include <haproxy/quic_token.h>
|
#include <haproxy/quic_token.h>
|
||||||
|
|
||||||
extern struct pool_head *pool_head_quic_frame;
|
extern struct pool_head *pool_head_quic_frame;
|
||||||
@ -87,6 +86,7 @@ enum quic_frame_type {
|
|||||||
* defined in quic_frame.c. Do not forget to complete the associated function
|
* defined in quic_frame.c. Do not forget to complete the associated function
|
||||||
* quic_frame_type_is_known() and both qf_builder()/qf_parser().
|
* quic_frame_type_is_known() and both qf_builder()/qf_parser().
|
||||||
*/
|
*/
|
||||||
|
extern const uint64_t QUIC_FT_QX_TRANSPORT_PARAMETERS;
|
||||||
|
|
||||||
#define QUIC_FT_PKT_TYPE_I_BITMASK (1 << QUIC_PACKET_TYPE_INITIAL)
|
#define QUIC_FT_PKT_TYPE_I_BITMASK (1 << QUIC_PACKET_TYPE_INITIAL)
|
||||||
#define QUIC_FT_PKT_TYPE_0_BITMASK (1 << QUIC_PACKET_TYPE_0RTT)
|
#define QUIC_FT_PKT_TYPE_0_BITMASK (1 << QUIC_PACKET_TYPE_0RTT)
|
||||||
@ -169,7 +169,7 @@ struct qf_new_token {
|
|||||||
|
|
||||||
struct qf_stream {
|
struct qf_stream {
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
struct qc_stream_desc *stream;
|
void *stream;
|
||||||
|
|
||||||
/* used only on TX when constructing frames.
|
/* used only on TX when constructing frames.
|
||||||
* Data cleared when processing ACK related to this STREAM frame.
|
* Data cleared when processing ACK related to this STREAM frame.
|
||||||
@ -252,6 +252,10 @@ struct qf_connection_close_app {
|
|||||||
unsigned char reason_phrase[QUIC_CC_REASON_PHRASE_MAXLEN];
|
unsigned char reason_phrase[QUIC_CC_REASON_PHRASE_MAXLEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct qf_qx_transport_parameters {
|
||||||
|
struct quic_transport_params params;
|
||||||
|
};
|
||||||
|
|
||||||
struct quic_frame {
|
struct quic_frame {
|
||||||
struct list list; /* List elem from parent elem (typically a Tx packet instance, a PKTNS or a MUX element). */
|
struct list list; /* List elem from parent elem (typically a Tx packet instance, a PKTNS or a MUX element). */
|
||||||
struct quic_tx_packet *pkt; /* Last Tx packet used to send the frame. */
|
struct quic_tx_packet *pkt; /* Last Tx packet used to send the frame. */
|
||||||
@ -279,6 +283,7 @@ struct quic_frame {
|
|||||||
struct qf_path_challenge_response path_challenge_response;
|
struct qf_path_challenge_response path_challenge_response;
|
||||||
struct qf_connection_close connection_close;
|
struct qf_connection_close connection_close;
|
||||||
struct qf_connection_close_app connection_close_app;
|
struct qf_connection_close_app connection_close_app;
|
||||||
|
struct qf_qx_transport_parameters qmux_transport_params;
|
||||||
};
|
};
|
||||||
struct quic_frame *origin; /* Parent frame. Set if frame is a duplicate (used for retransmission). */
|
struct quic_frame *origin; /* Parent frame. Set if frame is a duplicate (used for retransmission). */
|
||||||
struct list reflist; /* List head containing duplicated children frames. */
|
struct list reflist; /* List head containing duplicated children frames. */
|
||||||
|
|||||||
@ -34,14 +34,25 @@
|
|||||||
|
|
||||||
const char *quic_frame_type_string(enum quic_frame_type ft);
|
const char *quic_frame_type_string(enum quic_frame_type ft);
|
||||||
|
|
||||||
int qc_build_frm(unsigned char **pos, const unsigned char *end,
|
int qc_build_frm(struct quic_frame *frm,
|
||||||
struct quic_frame *frm, struct quic_tx_packet *pkt,
|
unsigned char **pos, const unsigned char *end,
|
||||||
struct quic_conn *conn);
|
struct quic_conn *conn);
|
||||||
|
|
||||||
int qc_parse_frm(struct quic_frame *frm, struct quic_rx_packet *pkt,
|
int qc_build_frm_pkt(struct quic_frame *frm, struct quic_tx_packet *pkt,
|
||||||
|
unsigned char **pos, const unsigned char *end,
|
||||||
|
struct quic_conn *qc);
|
||||||
|
|
||||||
|
int qc_parse_frm_type(struct quic_frame *frm,
|
||||||
const unsigned char **pos, const unsigned char *end,
|
const unsigned char **pos, const unsigned char *end,
|
||||||
struct quic_conn *conn);
|
struct quic_conn *conn);
|
||||||
|
|
||||||
|
int qc_parse_frm_pkt(const struct quic_frame *frm,
|
||||||
|
const struct quic_rx_packet *pkt, int *flags);
|
||||||
|
|
||||||
|
int qc_parse_frm_payload(struct quic_frame *frm,
|
||||||
|
const unsigned char **pos, const unsigned char *end,
|
||||||
|
struct quic_conn *qc);
|
||||||
|
|
||||||
void qc_release_frm(struct quic_conn *qc, struct quic_frame *frm);
|
void qc_release_frm(struct quic_conn *qc, struct quic_frame *frm);
|
||||||
|
|
||||||
/* Return the length of <frm> frame if succeeded, -1 if not (unknown frames
|
/* Return the length of <frm> frame if succeeded, -1 if not (unknown frames
|
||||||
|
|||||||
@ -124,12 +124,5 @@ struct quic_early_transport_params {
|
|||||||
uint64_t initial_max_streams_uni;
|
uint64_t initial_max_streams_uni;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Return type for QUIC TP decode function */
|
|
||||||
enum quic_tp_dec_err {
|
|
||||||
QUIC_TP_DEC_ERR_NONE = 0, /* no error */
|
|
||||||
QUIC_TP_DEC_ERR_INVAL, /* invalid value as per RFC 9000 */
|
|
||||||
QUIC_TP_DEC_ERR_TRUNC, /* field encoding too small or too large */
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* USE_QUIC */
|
#endif /* USE_QUIC */
|
||||||
#endif /* _HAPROXY_QUIC_TP_T_H */
|
#endif /* _HAPROXY_QUIC_TP_T_H */
|
||||||
|
|||||||
@ -129,6 +129,12 @@ static inline void quic_transport_params_dump(struct buffer *b,
|
|||||||
quic_tp_version_info_dump(b, &p->version_information, local);
|
quic_tp_version_info_dump(b, &p->version_information, local);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int quic_transport_param_enc_int(unsigned char **buf,
|
||||||
|
const unsigned char *end,
|
||||||
|
uint64_t type, uint64_t val);
|
||||||
|
int quic_transport_params_decode(struct quic_transport_params *p, int server,
|
||||||
|
const unsigned char *buf, const unsigned char *end);
|
||||||
|
|
||||||
static inline void quic_early_transport_params_dump(struct buffer *b,
|
static inline void quic_early_transport_params_dump(struct buffer *b,
|
||||||
const struct quic_conn *qc,
|
const struct quic_conn *qc,
|
||||||
const struct quic_early_transport_params *p)
|
const struct quic_early_transport_params *p)
|
||||||
|
|||||||
@ -72,6 +72,7 @@ extern struct pool_head *resolv_requester_pool;
|
|||||||
/* dns record types (non exhaustive list) */
|
/* dns record types (non exhaustive list) */
|
||||||
#define DNS_RTYPE_A 1 /* IPv4 address */
|
#define DNS_RTYPE_A 1 /* IPv4 address */
|
||||||
#define DNS_RTYPE_CNAME 5 /* canonical name */
|
#define DNS_RTYPE_CNAME 5 /* canonical name */
|
||||||
|
#define DNS_RTYPE_TXT 16 /* TXT */
|
||||||
#define DNS_RTYPE_AAAA 28 /* IPv6 address */
|
#define DNS_RTYPE_AAAA 28 /* IPv6 address */
|
||||||
#define DNS_RTYPE_SRV 33 /* SRV record */
|
#define DNS_RTYPE_SRV 33 /* SRV record */
|
||||||
#define DNS_RTYPE_OPT 41 /* OPT */
|
#define DNS_RTYPE_OPT 41 /* OPT */
|
||||||
|
|||||||
@ -220,8 +220,12 @@ static inline void srv_inc_sess_ctr(struct server *s)
|
|||||||
/* set the time of last session on the designated server */
|
/* set the time of last session on the designated server */
|
||||||
static inline void srv_set_sess_last(struct server *s)
|
static inline void srv_set_sess_last(struct server *s)
|
||||||
{
|
{
|
||||||
if (s->counters.shared.tg)
|
if (s->counters.shared.tg) {
|
||||||
HA_ATOMIC_STORE(&s->counters.shared.tg[tgid - 1]->last_sess, ns_to_sec(now_ns));
|
uint now_sec = ns_to_sec(now_ms);
|
||||||
|
|
||||||
|
if (HA_ATOMIC_LOAD(&s->counters.shared.tg[tgid - 1]->last_sess) != now_sec)
|
||||||
|
HA_ATOMIC_STORE(&s->counters.shared.tg[tgid - 1]->last_sess, now_sec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns the current server throttle rate between 0 and 100% */
|
/* returns the current server throttle rate between 0 and 100% */
|
||||||
|
|||||||
@ -339,7 +339,7 @@ struct global_ssl {
|
|||||||
char **passphrase_cmd;
|
char **passphrase_cmd;
|
||||||
int passphrase_cmd_args_cnt;
|
int passphrase_cmd_args_cnt;
|
||||||
|
|
||||||
unsigned int certificate_compression:1; /* allow to explicitely disable certificate compression */
|
unsigned int certificate_compression:1; /* allow to explicitly disable certificate compression */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The order here matters for picking a default context,
|
/* The order here matters for picking a default context,
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
#define STAT_F_CHUNKED 0x00000040 /* use chunked encoding (HTTP/1.1) */
|
#define STAT_F_CHUNKED 0x00000040 /* use chunked encoding (HTTP/1.1) */
|
||||||
#define STAT_F_JSON_SCHM 0x00000080 /* dump the json schema */
|
#define STAT_F_JSON_SCHM 0x00000080 /* dump the json schema */
|
||||||
|
|
||||||
#define STAT_F_HIDEVER 0x00000100 /* conf: do not report the version and reldate */
|
#define STAT_F_SHOWVER 0x00000100 /* conf: report the version and reldate */
|
||||||
#define STAT_F_SHNODE 0x00000200 /* conf: show node name */
|
#define STAT_F_SHNODE 0x00000200 /* conf: show node name */
|
||||||
#define STAT_F_SHDESC 0x00000400 /* conf: show description */
|
#define STAT_F_SHDESC 0x00000400 /* conf: show description */
|
||||||
#define STAT_F_SHLGNDS 0x00000800 /* conf: show legends */
|
#define STAT_F_SHLGNDS 0x00000800 /* conf: show legends */
|
||||||
|
|||||||
@ -73,7 +73,9 @@ enum se_flags {
|
|||||||
SE_FL_DETACHED = 0x00000010, /* The endpoint is detached (no mux/no applet) */
|
SE_FL_DETACHED = 0x00000010, /* The endpoint is detached (no mux/no applet) */
|
||||||
SE_FL_ORPHAN = 0x00000020, /* The endpoint is orphan (no stream connector) */
|
SE_FL_ORPHAN = 0x00000020, /* The endpoint is orphan (no stream connector) */
|
||||||
|
|
||||||
/* unused: 0x00000040 .. 0x00000080 */
|
SE_FL_APP_STARTED= 0x00000040, /* the application layer has really started */
|
||||||
|
|
||||||
|
/* unused: 0x00000080 */
|
||||||
|
|
||||||
SE_FL_SHRD = 0x00000100, /* read shut, draining extra data */
|
SE_FL_SHRD = 0x00000100, /* read shut, draining extra data */
|
||||||
SE_FL_SHRR = 0x00000200, /* read shut, resetting extra data */
|
SE_FL_SHRR = 0x00000200, /* read shut, resetting extra data */
|
||||||
@ -135,12 +137,12 @@ static forceinline char *se_show_flags(char *buf, size_t len, const char *delim,
|
|||||||
_(0);
|
_(0);
|
||||||
/* flags */
|
/* flags */
|
||||||
_(SE_FL_T_MUX, _(SE_FL_T_APPLET, _(SE_FL_DETACHED, _(SE_FL_ORPHAN,
|
_(SE_FL_T_MUX, _(SE_FL_T_APPLET, _(SE_FL_DETACHED, _(SE_FL_ORPHAN,
|
||||||
_(SE_FL_SHRD, _(SE_FL_SHRR, _(SE_FL_SHWN, _(SE_FL_SHWS,
|
_(SE_FL_APP_STARTED, _(SE_FL_SHRD, _(SE_FL_SHRR, _(SE_FL_SHWN, _(SE_FL_SHWS,
|
||||||
_(SE_FL_NOT_FIRST, _(SE_FL_WEBSOCKET, _(SE_FL_EOI, _(SE_FL_EOS,
|
_(SE_FL_NOT_FIRST, _(SE_FL_WEBSOCKET, _(SE_FL_EOI, _(SE_FL_EOS,
|
||||||
_(SE_FL_ERROR, _(SE_FL_ERR_PENDING, _(SE_FL_RCV_MORE,
|
_(SE_FL_ERROR, _(SE_FL_ERR_PENDING, _(SE_FL_RCV_MORE,
|
||||||
_(SE_FL_WANT_ROOM, _(SE_FL_EXP_NO_DATA, _(SE_FL_MAY_FASTFWD_PROD, _(SE_FL_MAY_FASTFWD_CONS,
|
_(SE_FL_WANT_ROOM, _(SE_FL_EXP_NO_DATA, _(SE_FL_MAY_FASTFWD_PROD, _(SE_FL_MAY_FASTFWD_CONS,
|
||||||
_(SE_FL_WAIT_FOR_HS, _(SE_FL_KILL_CONN, _(SE_FL_WAIT_DATA,
|
_(SE_FL_WAIT_FOR_HS, _(SE_FL_KILL_CONN, _(SE_FL_WAIT_DATA,
|
||||||
_(SE_FL_WONT_CONSUME, _(SE_FL_HAVE_NO_DATA, _(SE_FL_APPLET_NEED_CONN)))))))))))))))))))))))));
|
_(SE_FL_WONT_CONSUME, _(SE_FL_HAVE_NO_DATA, _(SE_FL_APPLET_NEED_CONN))))))))))))))))))))))))));
|
||||||
/* epilogue */
|
/* epilogue */
|
||||||
_(~0U);
|
_(~0U);
|
||||||
return buf;
|
return buf;
|
||||||
|
|||||||
@ -45,8 +45,7 @@ void se_shutdown(struct sedesc *sedesc, enum se_shut_mode mode);
|
|||||||
|
|
||||||
struct stconn *sc_new_from_endp(struct sedesc *sedesc, struct session *sess, struct buffer *input);
|
struct stconn *sc_new_from_endp(struct sedesc *sedesc, struct session *sess, struct buffer *input);
|
||||||
struct stconn *sc_new_from_strm(struct stream *strm, unsigned int flags);
|
struct stconn *sc_new_from_strm(struct stream *strm, unsigned int flags);
|
||||||
struct stconn *sc_new_from_check(struct check *check, unsigned int flags);
|
struct stconn *sc_new_from_check(struct check *check);
|
||||||
struct stconn *sc_new_from_haterm(struct sedesc *sd, struct session *sess, struct buffer *input);
|
|
||||||
void sc_free(struct stconn *sc);
|
void sc_free(struct stconn *sc);
|
||||||
|
|
||||||
int sc_attach_mux(struct stconn *sc, void *target, void *ctx);
|
int sc_attach_mux(struct stconn *sc, void *target, void *ctx);
|
||||||
|
|||||||
@ -130,7 +130,6 @@ struct notification {
|
|||||||
* on return.
|
* on return.
|
||||||
*/
|
*/
|
||||||
#define TASK_COMMON \
|
#define TASK_COMMON \
|
||||||
struct { \
|
|
||||||
unsigned int state; /* task state : bitfield of TASK_ */ \
|
unsigned int state; /* task state : bitfield of TASK_ */ \
|
||||||
int tid; /* tid of task/tasklet. <0 = local for tasklet, unbound for task */ \
|
int tid; /* tid of task/tasklet. <0 = local for tasklet, unbound for task */ \
|
||||||
struct task *(*process)(struct task *t, void *ctx, unsigned int state); /* the function which processes the task */ \
|
struct task *(*process)(struct task *t, void *ctx, unsigned int state); /* the function which processes the task */ \
|
||||||
@ -139,11 +138,14 @@ struct notification {
|
|||||||
uint32_t wake_date; /* date of the last task wakeup */ \
|
uint32_t wake_date; /* date of the last task wakeup */ \
|
||||||
unsigned int calls; /* number of times process was called */ \
|
unsigned int calls; /* number of times process was called */ \
|
||||||
TASK_DEBUG_STORAGE; \
|
TASK_DEBUG_STORAGE; \
|
||||||
}
|
short last_run; /* 16-bit now_ms of last run */
|
||||||
|
/* a 16- or 48-bit hole remains here and is used by task */
|
||||||
|
|
||||||
/* The base for all tasks */
|
/* The base for all tasks */
|
||||||
struct task {
|
struct task {
|
||||||
TASK_COMMON; /* must be at the beginning! */
|
TASK_COMMON; /* must be at the beginning! */
|
||||||
|
short nice; /* task prio from -1024 to +1024 */
|
||||||
|
int expire; /* next expiration date for this task, in ticks */
|
||||||
struct eb32_node rq; /* ebtree node used to hold the task in the run queue */
|
struct eb32_node rq; /* ebtree node used to hold the task in the run queue */
|
||||||
/* WARNING: the struct task is often aliased as a struct tasklet when
|
/* WARNING: the struct task is often aliased as a struct tasklet when
|
||||||
* it is NOT in the run queue. The tasklet has its struct list here
|
* it is NOT in the run queue. The tasklet has its struct list here
|
||||||
@ -151,14 +153,12 @@ struct task {
|
|||||||
* ever reorder these fields without taking this into account!
|
* ever reorder these fields without taking this into account!
|
||||||
*/
|
*/
|
||||||
struct eb32_node wq; /* ebtree node used to hold the task in the wait queue */
|
struct eb32_node wq; /* ebtree node used to hold the task in the wait queue */
|
||||||
int expire; /* next expiration date for this task, in ticks */
|
|
||||||
short nice; /* task prio from -1024 to +1024 */
|
|
||||||
/* 16-bit hole here */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* lightweight tasks, without priority, mainly used for I/Os */
|
/* lightweight tasks, without priority, mainly used for I/Os */
|
||||||
struct tasklet {
|
struct tasklet {
|
||||||
TASK_COMMON; /* must be at the beginning! */
|
TASK_COMMON; /* must be at the beginning! */
|
||||||
|
/* 48-bit hole here */
|
||||||
struct list list;
|
struct list list;
|
||||||
/* WARNING: the struct task is often aliased as a struct tasklet when
|
/* WARNING: the struct task is often aliased as a struct tasklet when
|
||||||
* it is not in the run queue. The task has its struct rq here where
|
* it is not in the run queue. The task has its struct rq here where
|
||||||
|
|||||||
@ -104,10 +104,15 @@ enum tcpcheck_rule_type {
|
|||||||
TCPCHK_ACT_ACTION_KW, /* custom registered action_kw rule. */
|
TCPCHK_ACT_ACTION_KW, /* custom registered action_kw rule. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define TCPCHK_FL_NONE 0x00000000
|
||||||
|
#define TCPCHK_FL_UNUSED_TCP_RS 0x00000001 /* An unused tcp-check ruleset exists for the current proxy */
|
||||||
|
#define TCPCHK_FL_UNUSED_HTTP_RS 0x00000002 /* An unused http-check ruleset exists for the current proxy */
|
||||||
|
#define TCPCHK_FL_UNUSED_RS 0x00000003 /* Mask for unused ruleset */
|
||||||
|
#define TCPCHK_FL_USE_SSL 0x00000004 /* tcp-check uses SSL connection */
|
||||||
|
|
||||||
#define TCPCHK_RULES_NONE 0x00000000
|
#define TCPCHK_RULES_NONE 0x00000000
|
||||||
#define TCPCHK_RULES_UNUSED_TCP_RS 0x00000001 /* An unused tcp-check ruleset exists */
|
#define TCPCHK_RULES_DISABLE404 0x00000001 /* Disable a server on a 404 response wht HTTP health checks */
|
||||||
#define TCPCHK_RULES_UNUSED_HTTP_RS 0x00000002 /* An unused http-check ruleset exists */
|
#define TCPCHK_RULES_SNDST 0x00000002 /* send the state of each server along with HTTP health checks */
|
||||||
#define TCPCHK_RULES_UNUSED_RS 0x00000003 /* Mask for unused ruleset */
|
|
||||||
|
|
||||||
#define TCPCHK_RULES_PGSQL_CHK 0x00000010
|
#define TCPCHK_RULES_PGSQL_CHK 0x00000010
|
||||||
#define TCPCHK_RULES_REDIS_CHK 0x00000020
|
#define TCPCHK_RULES_REDIS_CHK 0x00000020
|
||||||
@ -121,6 +126,7 @@ enum tcpcheck_rule_type {
|
|||||||
/* Unused 0x000000A0..0x00000FF0 (reserved for future proto) */
|
/* Unused 0x000000A0..0x00000FF0 (reserved for future proto) */
|
||||||
#define TCPCHK_RULES_TCP_CHK 0x00000FF0
|
#define TCPCHK_RULES_TCP_CHK 0x00000FF0
|
||||||
#define TCPCHK_RULES_PROTO_CHK 0x00000FF0 /* Mask to cover protocol check */
|
#define TCPCHK_RULES_PROTO_CHK 0x00000FF0 /* Mask to cover protocol check */
|
||||||
|
#define TCPCHK_RULES_MAY_USE_SBUF 0x00001000 /* checks may try to use small buffers if possible for the request */
|
||||||
|
|
||||||
struct check;
|
struct check;
|
||||||
struct tcpcheck_connect {
|
struct tcpcheck_connect {
|
||||||
@ -227,18 +233,24 @@ struct tcpcheck_var {
|
|||||||
struct list list; /* element to chain tcp-check vars */
|
struct list list; /* element to chain tcp-check vars */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* a list of tcp-check rules */
|
|
||||||
struct tcpcheck_rules {
|
|
||||||
unsigned int flags; /* flags applied to the rules */
|
|
||||||
struct list *list; /* the list of tcpcheck_rules */
|
|
||||||
struct list preset_vars; /* The list of variable to preset before executing the ruleset */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* A list of tcp-check rules with a name */
|
/* A list of tcp-check rules with a name */
|
||||||
struct tcpcheck_ruleset {
|
struct tcpcheck_ruleset {
|
||||||
struct list rules; /* the list of tcpcheck_rule */
|
struct list rules; /* the list of tcpcheck_rule */
|
||||||
|
unsigned int flags; /* flags applied to the rules */
|
||||||
struct ebpt_node node; /* node in the shared tree */
|
struct ebpt_node node; /* node in the shared tree */
|
||||||
|
struct {
|
||||||
|
struct list preset_vars; /* The list of variable to preset for healthcheck sections */
|
||||||
|
unsigned int flags; /* TCPCHECK_FL_* for healthcheck sections */
|
||||||
|
const char *file; /* file where the section appears */
|
||||||
|
int line; /* line where the section appears */
|
||||||
|
} conf; /* config information */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct tcpcheck {
|
||||||
|
struct tcpcheck_ruleset *rs; /* The tcp-check ruleset to use */
|
||||||
|
char *healthcheck; /* name of the healthcheck section (NULL if not used) */
|
||||||
|
struct list preset_vars; /* The list of variable to preset before executing the ruleset */
|
||||||
|
unsigned int flags; /* TCPCHECK_FL_* */
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _HAPROXY_CHECKS_T_H */
|
#endif /* _HAPROXY_CHECKS_T_H */
|
||||||
|
|||||||
@ -36,7 +36,7 @@ extern struct action_kw_list tcp_check_keywords;
|
|||||||
extern struct pool_head *pool_head_tcpcheck_rule;
|
extern struct pool_head *pool_head_tcpcheck_rule;
|
||||||
|
|
||||||
int tcpcheck_get_step_id(const struct check *check, const struct tcpcheck_rule *rule);
|
int tcpcheck_get_step_id(const struct check *check, const struct tcpcheck_rule *rule);
|
||||||
struct tcpcheck_rule *get_first_tcpcheck_rule(const struct tcpcheck_rules *rules);
|
struct tcpcheck_rule *get_first_tcpcheck_rule(const struct tcpcheck_ruleset *rs);
|
||||||
|
|
||||||
struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name);
|
struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name);
|
||||||
struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name);
|
struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name);
|
||||||
@ -50,9 +50,9 @@ void free_tcpcheck_var(struct tcpcheck_var *var);
|
|||||||
int dup_tcpcheck_vars(struct list *dst, const struct list *src);
|
int dup_tcpcheck_vars(struct list *dst, const struct list *src);
|
||||||
void free_tcpcheck_vars(struct list *vars);
|
void free_tcpcheck_vars(struct list *vars);
|
||||||
|
|
||||||
int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str);
|
int add_tcpcheck_expect_str(struct tcpcheck_ruleset *rs, const char *str);
|
||||||
int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs);
|
int add_tcpcheck_send_strs(struct tcpcheck_ruleset *rs, const char * const *strs);
|
||||||
int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg);
|
int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_ruleset *rs, char **errmsg);
|
||||||
|
|
||||||
void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr);
|
void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr);
|
||||||
|
|
||||||
@ -83,10 +83,6 @@ struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct pro
|
|||||||
struct list *rules, unsigned int proto,
|
struct list *rules, unsigned int proto,
|
||||||
const char *file, int line, char **errmsg);
|
const char *file, int line, char **errmsg);
|
||||||
|
|
||||||
int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
|
|
||||||
const struct proxy *defpx, const char *file, int line,
|
|
||||||
char **errmsg);
|
|
||||||
|
|
||||||
int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, const struct proxy *defpx,
|
int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, const struct proxy *defpx,
|
||||||
const char *file, int line);
|
const char *file, int line);
|
||||||
int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, const struct proxy *defpx,
|
int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, const struct proxy *defpx,
|
||||||
@ -106,6 +102,8 @@ int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, co
|
|||||||
int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, const struct proxy *defpx,
|
int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, const struct proxy *defpx,
|
||||||
const char *file, int line);
|
const char *file, int line);
|
||||||
|
|
||||||
|
int check_server_tcpcheck(struct server *srv);
|
||||||
|
|
||||||
void tcp_check_keywords_register(struct action_kw_list *kw_list);
|
void tcp_check_keywords_register(struct action_kw_list *kw_list);
|
||||||
|
|
||||||
/* Return the struct action_kw associated to a keyword */
|
/* Return the struct action_kw associated to a keyword */
|
||||||
@ -133,6 +131,22 @@ static inline int tcpchk_rules_type_to_proto_mode(int tcpchk_rules_type)
|
|||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const char *tcpcheck_ruleset_type_to_str(struct tcpcheck_ruleset *rs)
|
||||||
|
{
|
||||||
|
switch (rs->flags & TCPCHK_RULES_PROTO_CHK) {
|
||||||
|
case TCPCHK_RULES_PGSQL_CHK: return "PGSQL"; break;
|
||||||
|
case TCPCHK_RULES_REDIS_CHK: return "REDIS"; break;
|
||||||
|
case TCPCHK_RULES_SMTP_CHK: return "SMTP"; break;
|
||||||
|
case TCPCHK_RULES_HTTP_CHK: return "HTTP"; break;
|
||||||
|
case TCPCHK_RULES_MYSQL_CHK: return "MYSQL"; break;
|
||||||
|
case TCPCHK_RULES_LDAP_CHK: return "LDAP"; break;
|
||||||
|
case TCPCHK_RULES_SSL3_CHK: return "SSL3"; break;
|
||||||
|
case TCPCHK_RULES_AGENT_CHK: return "AGENT"; break;
|
||||||
|
case TCPCHK_RULES_SPOP_CHK: return "SPOP"; break;
|
||||||
|
case TCPCHK_RULES_TCP_CHK: return "TCP"; break;
|
||||||
|
default: return "???"; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif /* _HAPROXY_TCPCHECK_H */
|
#endif /* _HAPROXY_TCPCHECK_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
7
include/haproxy/xprt_qstrm.h
Normal file
7
include/haproxy/xprt_qstrm.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#ifndef _HAPROXY_XPRT_QSTRM_H
|
||||||
|
#define _HAPROXY_XPRT_QSTRM_H
|
||||||
|
|
||||||
|
const struct quic_transport_params *xprt_qstrm_lparams(const void *context);
|
||||||
|
const struct quic_transport_params *xprt_qstrm_rparams(const void *context);
|
||||||
|
|
||||||
|
#endif /* _HAPROXY_XPRT_QSTRM_H */
|
||||||
@ -1587,7 +1587,7 @@ struct XXH3_state_s {
|
|||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* simple alias to pre-selected XXH3_128bits variant
|
* simple alias to preselected XXH3_128bits variant
|
||||||
*/
|
*/
|
||||||
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed);
|
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed);
|
||||||
|
|
||||||
|
|||||||
267
reg-tests/checks/healthcheck-section.vtc
Normal file
267
reg-tests/checks/healthcheck-section.vtc
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
varnishtest "Health-checks: tests of healthcheck sections"
|
||||||
|
feature ignore_unknown_macro
|
||||||
|
#REQUIRE_OPTION=OPENSSL
|
||||||
|
#REGTEST_TYPE=slow
|
||||||
|
|
||||||
|
server s1 {
|
||||||
|
rxreq
|
||||||
|
expect req.method == OPTIONS
|
||||||
|
expect req.url == /
|
||||||
|
expect req.proto == HTTP/1.0
|
||||||
|
expect req.http.host == <undef>
|
||||||
|
txresp
|
||||||
|
} -start
|
||||||
|
|
||||||
|
server s2 {
|
||||||
|
rxreq
|
||||||
|
expect req.method == GET
|
||||||
|
expect req.url == /status
|
||||||
|
expect req.proto == HTTP/1.1
|
||||||
|
expect req.http.host == "www.haproxy.org"
|
||||||
|
txresp
|
||||||
|
} -start
|
||||||
|
|
||||||
|
server s3 {
|
||||||
|
rxreq
|
||||||
|
expect req.method == GET
|
||||||
|
expect req.url == /health
|
||||||
|
expect req.proto == HTTP/1.1
|
||||||
|
txresp
|
||||||
|
} -start
|
||||||
|
|
||||||
|
server s4 {
|
||||||
|
rxreq
|
||||||
|
expect req.method == GET
|
||||||
|
expect req.url == /req1
|
||||||
|
expect req.proto == HTTP/1.1
|
||||||
|
expect req.http.x-test == "server=srv"
|
||||||
|
expect req.http.x-haproxy-server-state ~ "UP.+name=be1/srv4"
|
||||||
|
expect req.bodylen == 0
|
||||||
|
txresp
|
||||||
|
|
||||||
|
accept
|
||||||
|
rxreq
|
||||||
|
expect req.method == GET
|
||||||
|
expect req.url == /req2
|
||||||
|
expect req.proto == HTTP/1.1
|
||||||
|
expect req.http.x-test == "server="
|
||||||
|
expect req.http.x-haproxy-server-state ~ "UP.+name=be1/srv4"
|
||||||
|
expect req.http.content-length == 17
|
||||||
|
expect req.bodylen == 17
|
||||||
|
expect req.body == "health-check body"
|
||||||
|
txresp
|
||||||
|
|
||||||
|
accept
|
||||||
|
rxreq
|
||||||
|
expect req.method == GET
|
||||||
|
expect req.url == /req3
|
||||||
|
expect req.proto == HTTP/1.0
|
||||||
|
expect req.http.x-test == <undef>
|
||||||
|
expect req.http.x-haproxy-server-state ~ "UP.+name=be1/srv4"
|
||||||
|
expect req.bodylen == 0
|
||||||
|
txresp
|
||||||
|
|
||||||
|
accept
|
||||||
|
rxreq
|
||||||
|
expect req.method == GET
|
||||||
|
expect req.url == /
|
||||||
|
expect req.proto == HTTP/1.0
|
||||||
|
expect req.http.x-test == <undef>
|
||||||
|
expect req.http.x-haproxy-server-state ~ "UP.+name=be1/srv4"
|
||||||
|
expect req.bodylen == 24
|
||||||
|
expect req.body == "health-check on be1-srv4"
|
||||||
|
txresp
|
||||||
|
} -start
|
||||||
|
|
||||||
|
# REDIS
|
||||||
|
server s5 {
|
||||||
|
recv 14
|
||||||
|
send "+PONG\r\n"
|
||||||
|
} -start
|
||||||
|
|
||||||
|
# TCP-CHECK
|
||||||
|
server s6 {
|
||||||
|
rxreq
|
||||||
|
expect req.method == GET
|
||||||
|
expect req.url == /
|
||||||
|
expect req.proto == HTTP/1.0
|
||||||
|
expect req.http.host == "www.haproxy.org"
|
||||||
|
txresp
|
||||||
|
} -start
|
||||||
|
|
||||||
|
# PgSQL
|
||||||
|
server s8 {
|
||||||
|
recv 23
|
||||||
|
sendhex "52000000170000000A534352414D2D5348412D3235360000"
|
||||||
|
} -start
|
||||||
|
|
||||||
|
# SMTP
|
||||||
|
server s9 {
|
||||||
|
send "220 smtp-check.vtc SMTP Server\r\n"
|
||||||
|
recv 17
|
||||||
|
send "250-smtp-check.vtc\r\n"
|
||||||
|
send "250-KEYWORD\r\n"
|
||||||
|
send "250 LAST KEYWORD\r\n"
|
||||||
|
recv 6
|
||||||
|
send "221 smtp-check.vtc closing\r\n"
|
||||||
|
} -start
|
||||||
|
|
||||||
|
# MySQL
|
||||||
|
server s10 {
|
||||||
|
sendhex "4A0000000A382E302E3139000A0000006F3C025E6249410D00FFFFFF0200FFC715000000000000000000007C182159106E2761144322200063616368696E675F736861325F70617373776F726400"
|
||||||
|
recv 47
|
||||||
|
sendhex "0700000200000002000000"
|
||||||
|
} -start
|
||||||
|
|
||||||
|
# LDAP
|
||||||
|
server s11 {
|
||||||
|
recv 14
|
||||||
|
sendhex "308400000010020101 61 84000000070A01"
|
||||||
|
sendhex "00 04000400"
|
||||||
|
} -start
|
||||||
|
|
||||||
|
# SPOP
|
||||||
|
server s12 {
|
||||||
|
recv 82
|
||||||
|
sendhex "00000036 65 00000001 0000 0776657273696F6E 0803322E30 0E6D61782D6672616D652D73697A65 03FCF0 060C6361706162696C6974696573 0800"
|
||||||
|
} -start
|
||||||
|
|
||||||
|
syslog S1 -level notice {
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv[1-4] succeeded"
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv[1-4] succeeded"
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv[1-4] succeeded"
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv[1-4] succeeded"
|
||||||
|
} -start
|
||||||
|
|
||||||
|
|
||||||
|
syslog S2 -level notice {
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9] succeeded"
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||||
|
recv
|
||||||
|
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||||
|
} -start
|
||||||
|
|
||||||
|
haproxy h1 -conf {
|
||||||
|
global
|
||||||
|
.if feature(THREAD)
|
||||||
|
thread-groups 1
|
||||||
|
.endif
|
||||||
|
|
||||||
|
.if !ssllib_name_startswith(AWS-LC)
|
||||||
|
tune.ssl.default-dh-param 2048
|
||||||
|
.endif
|
||||||
|
|
||||||
|
defaults
|
||||||
|
mode tcp
|
||||||
|
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||||
|
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||||
|
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||||
|
|
||||||
|
healthcheck http-status
|
||||||
|
type httpchk GET /status HTTP/1.1 www.haproxy.org
|
||||||
|
|
||||||
|
healthcheck http-health
|
||||||
|
type httpchk
|
||||||
|
http-check send meth GET uri /health ver HTTP/1.1
|
||||||
|
|
||||||
|
healthcheck http-complex
|
||||||
|
http-check send-state
|
||||||
|
http-check connect addr ${s4_addr}:${s4_port}
|
||||||
|
http-check set-var(check.server) "str(srv)"
|
||||||
|
http-check set-var(check.path) "str(/req1)"
|
||||||
|
http-check send meth GET uri-lf "%[var(check.path)]" ver HTTP/1.1 hdr x-test "server=%[var(check.server)]"
|
||||||
|
http-check expect status 200
|
||||||
|
http-check connect addr ${s4_addr} port ${s4_port}
|
||||||
|
http-check unset-var(check.server)
|
||||||
|
http-check set-var(check.path) "str(/req2)"
|
||||||
|
http-check send meth GET uri-lf "%[var(check.path)]" ver HTTP/1.1 hdr x-test "server=%[var(check.server)]" body "health-check body"
|
||||||
|
http-check expect rstatus "^2[0-9]{2}"
|
||||||
|
http-check connect addr ${s4_addr} port ${s4_port}
|
||||||
|
http-check set-var(check.path) "str(/req3)"
|
||||||
|
http-check send meth GET uri-lf "%[var(check.path)]"
|
||||||
|
http-check expect rstatus "^2[0-9]{2}"
|
||||||
|
http-check connect addr ${s4_addr} port ${s4_port}
|
||||||
|
http-check unset-var(check.path)
|
||||||
|
http-check send meth GET uri-lf "%[var(check.path)]" body-lf "health-check on %[be_name]-%[srv_name]"
|
||||||
|
## implicit expect rule
|
||||||
|
type httpchk
|
||||||
|
|
||||||
|
healthcheck tcpchk
|
||||||
|
type tcp-check
|
||||||
|
tcp-check connect
|
||||||
|
tcp-check send GET\ /\ HTTP/1.0\r\n
|
||||||
|
tcp-check send Host:\ www.haproxy.org\r\n
|
||||||
|
tcp-check send \r\n
|
||||||
|
tcp-check expect rstring (2..|3..)
|
||||||
|
tcp-check connect addr ${h1_li6_addr} port ${h1_li6_port} ssl
|
||||||
|
tcp-check send GET\ /\ HTTP/1.0\r\n
|
||||||
|
tcp-check send Host:\ www.haproxy.org\r\n
|
||||||
|
tcp-check send \r\n
|
||||||
|
tcp-check expect rstring (2..|3..)
|
||||||
|
|
||||||
|
healthcheck redis
|
||||||
|
type redis-check
|
||||||
|
|
||||||
|
healthcheck sslchk
|
||||||
|
type ssl-hello-chk
|
||||||
|
|
||||||
|
healthcheck pgchk
|
||||||
|
type pgsql-check user postgres
|
||||||
|
|
||||||
|
healthcheck smtpchk
|
||||||
|
type smtpchk EHLO domain.tld
|
||||||
|
|
||||||
|
healthcheck mysqlchk
|
||||||
|
type mysql-check user user
|
||||||
|
|
||||||
|
healthcheck ldapchk
|
||||||
|
type ldap-check
|
||||||
|
|
||||||
|
healthcheck spopchk
|
||||||
|
type spop-check
|
||||||
|
|
||||||
|
listen li6
|
||||||
|
mode http
|
||||||
|
bind "fd@${li6}" ssl crt ${testdir}/certs/common.pem
|
||||||
|
http-request return status 200
|
||||||
|
|
||||||
|
backend be1
|
||||||
|
log ${S1_addr}:${S1_port} daemon
|
||||||
|
option log-health-checks
|
||||||
|
option httpchk
|
||||||
|
server srv1 ${s1_addr}:${s1_port} check inter 100ms rise 1 fall 1
|
||||||
|
server srv2 ${s2_addr}:${s2_port} check inter 100ms rise 1 fall 1 healthcheck http-status
|
||||||
|
server srv3 ${s3_addr}:${s3_port} check inter 100ms rise 1 fall 1 healthcheck http-health
|
||||||
|
server srv4 ${s4_addr}:${s4_port} check inter 100ms rise 1 fall 1 healthcheck http-complex
|
||||||
|
|
||||||
|
backend be2
|
||||||
|
log ${S2_addr}:${S2_port} daemon
|
||||||
|
option log-health-checks
|
||||||
|
server srv5 ${s5_addr}:${s5_port} check inter 100ms rise 1 fall 1 healthcheck redis
|
||||||
|
server srv6 ${s6_addr}:${s6_port} check inter 100ms rise 1 fall 1 healthcheck tcpchk verify none
|
||||||
|
server srv7 ${h1_li6_addr}:${h1_li6_port} check inter 100ms rise 1 fall 1 healthcheck sslchk
|
||||||
|
server srv8 ${s8_addr}:${s8_port} check inter 100ms rise 1 fall 1 healthcheck pgchk
|
||||||
|
server srv9 ${s9_addr}:${s9_port} check inter 100ms rise 1 fall 1 healthcheck smtpchk
|
||||||
|
server srv10 ${s10_addr}:${s10_port} check inter 100ms rise 1 fall 1 healthcheck mysqlchk
|
||||||
|
server srv11 ${s11_addr}:${s11_port} check inter 100ms rise 1 fall 1 healthcheck ldapchk
|
||||||
|
server srv12 ${s12_addr}:${s12_port} check inter 100ms rise 1 fall 1 healthcheck spopchk
|
||||||
|
} -start
|
||||||
|
|
||||||
|
syslog S1 -wait
|
||||||
|
syslog S2 -wait
|
||||||
@ -336,6 +336,16 @@ dump_commit_matrix | column -t | \
|
|||||||
elif [ -n "$SINCELAST" ]; then
|
elif [ -n "$SINCELAST" ]; then
|
||||||
echo "Found ${#since_last[@]} commit(s) added to branch $REF since last backported commit $last_bkp:"
|
echo "Found ${#since_last[@]} commit(s) added to branch $REF since last backported commit $last_bkp:"
|
||||||
echo
|
echo
|
||||||
|
if [ -z "$QUIET" ]; then
|
||||||
|
for c in "${since_last[@]}"; do
|
||||||
|
echo "$(git log -1 --pretty=" %h | %s" "$c")"
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
echo "In order to show and/or apply them all to current branch :"
|
||||||
|
echo
|
||||||
|
echo " git show --pretty=format:'%C(yellow)commit %H%C(normal)%nAuthor: %an <%ae>%nDate: %aD%n%n%C(green)%C(bold)git cherry-pick -sx %h%n%n%w(0,4,4)%B%N' ${since_last[@]}"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
echo " git cherry-pick -sx ${since_last[@]}"
|
echo " git cherry-pick -sx ${since_last[@]}"
|
||||||
echo
|
echo
|
||||||
elif [ -n "$MISSING" -a ${#left_commits[@]} -eq 0 ]; then
|
elif [ -n "$MISSING" -a ${#left_commits[@]} -eq 0 ]; then
|
||||||
|
|||||||
391
src/acme.c
391
src/acme.c
@ -4,6 +4,7 @@
|
|||||||
* Implements the ACMEv2 RFC 8555 protocol
|
* Implements the ACMEv2 RFC 8555 protocol
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "haproxy/ticks.h"
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -14,7 +15,9 @@
|
|||||||
|
|
||||||
#include <haproxy/acme-t.h>
|
#include <haproxy/acme-t.h>
|
||||||
|
|
||||||
|
#include <haproxy/acme_resolvers.h>
|
||||||
#include <haproxy/base64.h>
|
#include <haproxy/base64.h>
|
||||||
|
#include <haproxy/intops.h>
|
||||||
#include <haproxy/cfgparse.h>
|
#include <haproxy/cfgparse.h>
|
||||||
#include <haproxy/cli.h>
|
#include <haproxy/cli.h>
|
||||||
#include <haproxy/errors.h>
|
#include <haproxy/errors.h>
|
||||||
@ -23,6 +26,7 @@
|
|||||||
#include <haproxy/list.h>
|
#include <haproxy/list.h>
|
||||||
#include <haproxy/log.h>
|
#include <haproxy/log.h>
|
||||||
#include <haproxy/pattern.h>
|
#include <haproxy/pattern.h>
|
||||||
|
#include <haproxy/resolvers.h>
|
||||||
#include <haproxy/sink.h>
|
#include <haproxy/sink.h>
|
||||||
#include <haproxy/ssl_ckch.h>
|
#include <haproxy/ssl_ckch.h>
|
||||||
#include <haproxy/ssl_gencert.h>
|
#include <haproxy/ssl_gencert.h>
|
||||||
@ -117,6 +121,10 @@ static void acme_trace(enum trace_level level, uint64_t mask, const struct trace
|
|||||||
case ACME_NEWACCOUNT: chunk_appendf(&trace_buf, "ACME_NEWACCOUNT"); break;
|
case ACME_NEWACCOUNT: chunk_appendf(&trace_buf, "ACME_NEWACCOUNT"); break;
|
||||||
case ACME_NEWORDER: chunk_appendf(&trace_buf, "ACME_NEWORDER"); break;
|
case ACME_NEWORDER: chunk_appendf(&trace_buf, "ACME_NEWORDER"); break;
|
||||||
case ACME_AUTH: chunk_appendf(&trace_buf, "ACME_AUTH"); break;
|
case ACME_AUTH: chunk_appendf(&trace_buf, "ACME_AUTH"); break;
|
||||||
|
case ACME_CLI_WAIT : chunk_appendf(&trace_buf, "ACME_CLI_WAIT"); break;
|
||||||
|
case ACME_RSLV_WAIT: chunk_appendf(&trace_buf, "ACME_RSLV_WAIT"); break;
|
||||||
|
case ACME_RSLV_TRIGGER: chunk_appendf(&trace_buf, "ACME_RSLV_TRIGGER"); break;
|
||||||
|
case ACME_RSLV_READY: chunk_appendf(&trace_buf, "ACME_RSLV_READY"); break;
|
||||||
case ACME_CHALLENGE: chunk_appendf(&trace_buf, "ACME_CHALLENGE"); break;
|
case ACME_CHALLENGE: chunk_appendf(&trace_buf, "ACME_CHALLENGE"); break;
|
||||||
case ACME_CHKCHALLENGE: chunk_appendf(&trace_buf, "ACME_CHKCHALLENGE"); break;
|
case ACME_CHKCHALLENGE: chunk_appendf(&trace_buf, "ACME_CHKCHALLENGE"); break;
|
||||||
case ACME_FINALIZE: chunk_appendf(&trace_buf, "ACME_FINALIZE"); break;
|
case ACME_FINALIZE: chunk_appendf(&trace_buf, "ACME_FINALIZE"); break;
|
||||||
@ -190,6 +198,8 @@ struct acme_cfg *new_acme_cfg(const char *name)
|
|||||||
ret->linenum = 0;
|
ret->linenum = 0;
|
||||||
|
|
||||||
ret->challenge = strdup("http-01"); /* default value */
|
ret->challenge = strdup("http-01"); /* default value */
|
||||||
|
ret->dns_delay = 30; /* default DNS re-trigger delay in seconds */
|
||||||
|
ret->dns_timeout = 600; /* default DNS retry timeout */
|
||||||
|
|
||||||
/* The default generated keys are EC-384 */
|
/* The default generated keys are EC-384 */
|
||||||
ret->key.type = EVP_PKEY_EC;
|
ret->key.type = EVP_PKEY_EC;
|
||||||
@ -266,7 +276,6 @@ static int cfg_parse_acme(const char *file, int linenum, char **args, int kwm)
|
|||||||
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
||||||
|
|
||||||
if (strcmp(args[0], "acme") == 0) {
|
if (strcmp(args[0], "acme") == 0) {
|
||||||
struct acme_cfg *tmp_acme = acme_cfgs;
|
|
||||||
|
|
||||||
if (alertif_too_many_args(1, file, linenum, args, &err_code))
|
if (alertif_too_many_args(1, file, linenum, args, &err_code))
|
||||||
goto out;
|
goto out;
|
||||||
@ -292,7 +301,7 @@ static int cfg_parse_acme(const char *file, int linenum, char **args, int kwm)
|
|||||||
* name */
|
* name */
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
ha_alert("parsing [%s:%d]: acme section '%s' already exists (%s:%d).\n",
|
ha_alert("parsing [%s:%d]: acme section '%s' already exists (%s:%d).\n",
|
||||||
file, linenum, args[1], tmp_acme->filename, tmp_acme->linenum);
|
file, linenum, args[1], cur_acme->filename, cur_acme->linenum);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,6 +429,18 @@ static int cfg_parse_acme_kws(char **args, int section_type, struct proxy *curpx
|
|||||||
ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
|
ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* require the CLI by default */
|
||||||
|
if ((strcasecmp("dns-01", args[1]) == 0) && (cur_acme->cond_ready == 0)) {
|
||||||
|
cur_acme->cond_ready = ACME_RDY_CLI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((strcasecmp("http-01", args[1]) == 0) && (cur_acme->cond_ready != 0)) {
|
||||||
|
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section, \"http-01\" is not compatible with the \"challenge-ready\" option\n", file, linenum, args[0], cursection);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (strcmp(args[0], "map") == 0) {
|
} else if (strcmp(args[0], "map") == 0) {
|
||||||
/* save the map name for thumbprint + token storage */
|
/* save the map name for thumbprint + token storage */
|
||||||
if (!*args[1]) {
|
if (!*args[1]) {
|
||||||
@ -437,6 +458,99 @@ static int cfg_parse_acme_kws(char **args, int section_type, struct proxy *curpx
|
|||||||
ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
|
ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(args[0], "challenge-ready") == 0) {
|
||||||
|
char *str = args[1];
|
||||||
|
char *saveptr;
|
||||||
|
|
||||||
|
if (!*args[1]) {
|
||||||
|
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires an argument\n", file, linenum, args[0], cursection);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (alertif_too_many_args(1, file, linenum, args, &err_code))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
cur_acme->cond_ready = 0;
|
||||||
|
|
||||||
|
while ((str = strtok_r(str, ",", &saveptr))) {
|
||||||
|
|
||||||
|
if (strcmp(str, "cli") == 0) {
|
||||||
|
/* wait for the CLI-ready to run the challenge */
|
||||||
|
cur_acme->cond_ready |= ACME_RDY_CLI;
|
||||||
|
} else if (strcmp(str, "dns") == 0) {
|
||||||
|
/* wait for the DNS-check to run the challenge */
|
||||||
|
cur_acme->cond_ready |= ACME_RDY_DNS;
|
||||||
|
} else if (strcmp(str, "none") == 0) {
|
||||||
|
if (cur_acme->cond_ready || (saveptr && *saveptr)) {
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' can't combine 'none' with other keywords.\n", file, linenum, args[0], cursection);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
cur_acme->cond_ready = ACME_RDY_NONE;
|
||||||
|
} else {
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires parameter separated by commas: 'cli', 'dns' or 'none'\n", file, linenum, args[0], cursection);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
str = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((strcasecmp("http-01", cur_acme->challenge) == 0) && (cur_acme->cond_ready != 0)) {
|
||||||
|
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section, \"http-01\" is not compatible with the \"challenge-ready\" option\n", file, linenum, args[0], cursection);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (strcmp(args[0], "dns-delay") == 0) {
|
||||||
|
const char *res;
|
||||||
|
|
||||||
|
if (!*args[1]) {
|
||||||
|
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires an argument\n", file, linenum, args[0], cursection);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (alertif_too_many_args(1, file, linenum, args, &err_code))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
res = parse_time_err(args[1], &cur_acme->dns_delay, TIME_UNIT_S);
|
||||||
|
if (res == PARSE_TIME_OVER) {
|
||||||
|
ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to '%s'\n", file, linenum, args[1], args[0]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
} else if (res == PARSE_TIME_UNDER) {
|
||||||
|
ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to '%s'\n", file, linenum, args[1], args[0]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
} else if (res) {
|
||||||
|
ha_alert("parsing [%s:%d]: unexpected character '%c' in argument to '%s'\n", file, linenum, *res, args[0]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else if (strcmp(args[0], "dns-timeout") == 0) {
|
||||||
|
const char *res;
|
||||||
|
|
||||||
|
if (!*args[1]) {
|
||||||
|
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires an argument\n", file, linenum, args[0], cursection);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (alertif_too_many_args(1, file, linenum, args, &err_code))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
res = parse_time_err(args[1], &cur_acme->dns_timeout, TIME_UNIT_S);
|
||||||
|
if (res == PARSE_TIME_OVER) {
|
||||||
|
ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to '%s'\n", file, linenum, args[1], args[0]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
} else if (res == PARSE_TIME_UNDER) {
|
||||||
|
ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to '%s'\n", file, linenum, args[1], args[0]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
} else if (res) {
|
||||||
|
ha_alert("parsing [%s:%d]: unexpected character '%c' in argument to '%s'\n", file, linenum, *res, args[0]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
} else if (strcmp(args[0], "reuse-key") == 0) {
|
} else if (strcmp(args[0], "reuse-key") == 0) {
|
||||||
if (!*args[1]) {
|
if (!*args[1]) {
|
||||||
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires an argument\n", file, linenum, args[0], cursection);
|
ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section requires an argument\n", file, linenum, args[0], cursection);
|
||||||
@ -841,6 +955,9 @@ static struct cfg_kw_list cfg_kws_acme = {ILH, {
|
|||||||
{ CFG_ACME, "curves", cfg_parse_acme_cfg_key },
|
{ CFG_ACME, "curves", cfg_parse_acme_cfg_key },
|
||||||
{ CFG_ACME, "map", cfg_parse_acme_kws },
|
{ CFG_ACME, "map", cfg_parse_acme_kws },
|
||||||
{ CFG_ACME, "reuse-key", cfg_parse_acme_kws },
|
{ CFG_ACME, "reuse-key", cfg_parse_acme_kws },
|
||||||
|
{ CFG_ACME, "challenge-ready", cfg_parse_acme_kws },
|
||||||
|
{ CFG_ACME, "dns-delay", cfg_parse_acme_kws },
|
||||||
|
{ CFG_ACME, "dns-timeout", cfg_parse_acme_kws },
|
||||||
{ CFG_ACME, "acme-vars", cfg_parse_acme_vars_provider },
|
{ CFG_ACME, "acme-vars", cfg_parse_acme_vars_provider },
|
||||||
{ CFG_ACME, "provider-name", cfg_parse_acme_vars_provider },
|
{ CFG_ACME, "provider-name", cfg_parse_acme_vars_provider },
|
||||||
{ CFG_GLOBAL, "acme.scheduler", cfg_parse_global_acme_sched },
|
{ CFG_GLOBAL, "acme.scheduler", cfg_parse_global_acme_sched },
|
||||||
@ -879,6 +996,7 @@ static void acme_ctx_destroy(struct acme_ctx *ctx)
|
|||||||
istfree(&auth->chall);
|
istfree(&auth->chall);
|
||||||
istfree(&auth->token);
|
istfree(&auth->token);
|
||||||
istfree(&auth->dns);
|
istfree(&auth->dns);
|
||||||
|
acme_rslv_free(auth->rslv);
|
||||||
next = auth->next;
|
next = auth->next;
|
||||||
free(auth);
|
free(auth);
|
||||||
auth = next;
|
auth = next;
|
||||||
@ -1188,7 +1306,7 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1261,7 +1379,7 @@ int acme_res_chkorder(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1344,7 +1462,6 @@ int acme_req_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
csr->data = ret;
|
csr->data = ret;
|
||||||
|
|
||||||
chunk_printf(req_in, "{ \"csr\": \"%.*s\" }", (int)csr->data, csr->area);
|
chunk_printf(req_in, "{ \"csr\": \"%.*s\" }", (int)csr->data, csr->area);
|
||||||
OPENSSL_free(data);
|
|
||||||
|
|
||||||
|
|
||||||
if (acme_jws_payload(req_in, ctx->nonce, ctx->finalize, ctx->cfg->account.pkey, ctx->kid, req_out, errmsg) != 0)
|
if (acme_jws_payload(req_in, ctx->nonce, ctx->finalize, ctx->cfg->account.pkey, ctx->kid, req_out, errmsg) != 0)
|
||||||
@ -1358,6 +1475,7 @@ int acme_req_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
error:
|
error:
|
||||||
memprintf(errmsg, "couldn't request the finalize URL");
|
memprintf(errmsg, "couldn't request the finalize URL");
|
||||||
out:
|
out:
|
||||||
|
OPENSSL_free(data);
|
||||||
free_trash_chunk(req_in);
|
free_trash_chunk(req_in);
|
||||||
free_trash_chunk(req_out);
|
free_trash_chunk(req_out);
|
||||||
free_trash_chunk(csr);
|
free_trash_chunk(csr);
|
||||||
@ -1391,7 +1509,7 @@ int acme_res_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1492,7 +1610,7 @@ enum acme_ret acme_res_challenge(struct task *task, struct acme_ctx *ctx, struct
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1618,7 +1736,7 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1654,6 +1772,19 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||||||
|
|
||||||
auth->dns = istdup(ist2(t2->area, t2->data));
|
auth->dns = istdup(ist2(t2->area, t2->data));
|
||||||
|
|
||||||
|
ret = mjson_get_string(hc->res.buf.area, hc->res.buf.data, "$.status", trash.area, trash.size);
|
||||||
|
if (ret == -1) {
|
||||||
|
memprintf(errmsg, "couldn't get a \"status\" from Authorization URL \"%s\"", auth->auth.ptr);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
trash.data = ret;
|
||||||
|
|
||||||
|
/* if auth is already valid we need to skip solving challenges */
|
||||||
|
if (strncasecmp("valid", trash.area, trash.data) == 0) {
|
||||||
|
auth->validated = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* get the multiple challenges and select the one from the configuration */
|
/* get the multiple challenges and select the one from the configuration */
|
||||||
for (i = 0; ; i++) {
|
for (i = 0; ; i++) {
|
||||||
int ret;
|
int ret;
|
||||||
@ -1717,8 +1848,13 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* replace the token by the TXT entry */
|
||||||
|
istfree(&auth->token);
|
||||||
|
auth->token = istdup(ist2(dns_record->area, dns_record->data));
|
||||||
|
|
||||||
|
if (ctx->cfg->cond_ready & ACME_RDY_CLI)
|
||||||
send_log(NULL, LOG_NOTICE,"acme: %s: dns-01 requires to set the \"_acme-challenge.%.*s\" TXT record to \"%.*s\" and use the \"acme challenge_ready %s domain %.*s\" command over the CLI\n",
|
send_log(NULL, LOG_NOTICE,"acme: %s: dns-01 requires to set the \"_acme-challenge.%.*s\" TXT record to \"%.*s\" and use the \"acme challenge_ready %s domain %.*s\" command over the CLI\n",
|
||||||
ctx->store->path, (int)auth->dns.len, auth->dns.ptr, (int)dns_record->data, dns_record->area, ctx->store->path, (int)auth->dns.len, auth->dns.ptr);
|
ctx->store->path, (int)auth->dns.len, auth->dns.ptr, (int)auth->token.len, auth->token.ptr, ctx->store->path, (int)auth->dns.len, auth->dns.ptr);
|
||||||
|
|
||||||
/* dump to the "dpapi" sink */
|
/* dump to the "dpapi" sink */
|
||||||
line[nmsg++] = ist("acme deploy ");
|
line[nmsg++] = ist("acme deploy ");
|
||||||
@ -1761,6 +1897,7 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -1849,7 +1986,7 @@ int acme_res_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
/* get the order URL */
|
/* get the order URL */
|
||||||
if (isteqi(hdr->n, ist("Location"))) {
|
if (isteqi(hdr->n, ist("Location"))) {
|
||||||
@ -1901,11 +2038,6 @@ int acme_res_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if the challenge is not dns-01, consider that the challenge
|
|
||||||
* is ready because computed by HAProxy */
|
|
||||||
if (strcasecmp(ctx->cfg->challenge, "dns-01") != 0)
|
|
||||||
auth->ready = 1;
|
|
||||||
|
|
||||||
auth->next = ctx->auths;
|
auth->next = ctx->auths;
|
||||||
ctx->auths = auth;
|
ctx->auths = auth;
|
||||||
ctx->next_auth = auth;
|
ctx->next_auth = auth;
|
||||||
@ -2009,7 +2141,7 @@ int acme_res_account(struct task *task, struct acme_ctx *ctx, int newaccount, ch
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||||
istfree(&ctx->nonce);
|
istfree(&ctx->nonce);
|
||||||
@ -2254,6 +2386,9 @@ re:
|
|||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
if ((ctx->next_auth = ctx->next_auth->next) == NULL) {
|
if ((ctx->next_auth = ctx->next_auth->next) == NULL) {
|
||||||
|
if (strcasecmp(ctx->cfg->challenge, "dns-01") == 0 && ctx->cfg->cond_ready)
|
||||||
|
st = ACME_CLI_WAIT;
|
||||||
|
else
|
||||||
st = ACME_CHALLENGE;
|
st = ACME_CHALLENGE;
|
||||||
ctx->next_auth = ctx->auths;
|
ctx->next_auth = ctx->auths;
|
||||||
}
|
}
|
||||||
@ -2261,11 +2396,170 @@ re:
|
|||||||
goto nextreq;
|
goto nextreq;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ACME_CLI_WAIT: {
|
||||||
|
struct acme_auth *auth;
|
||||||
|
int all_cond_ready = ctx->cfg->cond_ready;
|
||||||
|
|
||||||
|
for (auth = ctx->auths; auth != NULL; auth = auth->next) {
|
||||||
|
all_cond_ready &= auth->ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if everything is ready, let's do the challenge request */
|
||||||
|
if ((all_cond_ready & ctx->cfg->cond_ready) == ctx->cfg->cond_ready) {
|
||||||
|
st = ACME_CHALLENGE;
|
||||||
|
ctx->http_state = ACME_HTTP_REQ;
|
||||||
|
ctx->state = st;
|
||||||
|
goto nextreq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we need to wait for the CLI, let's wait */
|
||||||
|
if ((ctx->cfg->cond_ready & ACME_RDY_CLI) && !(all_cond_ready & ACME_RDY_CLI))
|
||||||
|
goto wait;
|
||||||
|
|
||||||
|
/* next step */
|
||||||
|
st = ACME_RSLV_WAIT;
|
||||||
|
goto nextreq;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ACME_RSLV_WAIT: {
|
||||||
|
struct acme_auth *auth;
|
||||||
|
int all_cond_ready = ctx->cfg->cond_ready;
|
||||||
|
|
||||||
|
for (auth = ctx->auths; auth != NULL; auth = auth->next) {
|
||||||
|
all_cond_ready &= auth->ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if everything is ready, let's do the challenge request */
|
||||||
|
if ((all_cond_ready & ctx->cfg->cond_ready) == ctx->cfg->cond_ready) {
|
||||||
|
st = ACME_CHALLENGE;
|
||||||
|
goto nextreq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the next resolution would be triggered too
|
||||||
|
* late according to the dns_timeout and abort is
|
||||||
|
* necessary. */
|
||||||
|
if (ctx->dnsstarttime && ns_to_sec(now_ns) + ctx->cfg->dns_delay > ctx->dnsstarttime + ctx->cfg->dns_timeout) {
|
||||||
|
memprintf(&errmsg, "dns-01: Couldn't resolve the TXT records in %ds.", ctx->cfg->dns_timeout);
|
||||||
|
goto abort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we don't need to wait, we can trigger the resolution
|
||||||
|
* after the delay */
|
||||||
|
st = ACME_RSLV_TRIGGER;
|
||||||
|
ctx->http_state = ACME_HTTP_REQ;
|
||||||
|
ctx->state = st;
|
||||||
|
send_log(NULL, LOG_NOTICE, "acme: %s: dns-01: triggering the resolution in %ds\n",
|
||||||
|
ctx->store->path, ctx->cfg->dns_delay);
|
||||||
|
|
||||||
|
task->expire = tick_add(now_ms, ctx->cfg->dns_delay * 1000);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ACME_RSLV_TRIGGER: {
|
||||||
|
struct acme_auth *auth;
|
||||||
|
|
||||||
|
/* set the start time of the DNS checks so we can apply
|
||||||
|
* the timeout */
|
||||||
|
if (ctx->dnsstarttime == 0)
|
||||||
|
ctx->dnsstarttime = ns_to_sec(now_ns);
|
||||||
|
|
||||||
|
/* if it was trigger by the CLI, still wait dns_delay if
|
||||||
|
* not everything is ready, or skip and to to
|
||||||
|
* ACME_CHALLENGE */
|
||||||
|
if (!(state & TASK_WOKEN_TIMER)) {
|
||||||
|
int all_ready = 1;
|
||||||
|
|
||||||
|
for (auth = ctx->auths; auth != NULL; auth = auth->next) {
|
||||||
|
if (auth->ready == ctx->cfg->cond_ready)
|
||||||
|
continue;
|
||||||
|
all_ready = 0;
|
||||||
|
}
|
||||||
|
if (all_ready) {
|
||||||
|
st = ACME_CHALLENGE;
|
||||||
|
ctx->http_state = ACME_HTTP_REQ;
|
||||||
|
ctx->state = st;
|
||||||
|
goto nextreq;
|
||||||
|
} else {
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* on timer expiry, re-trigger resolution for non-ready auths */
|
||||||
|
for (auth = ctx->auths; auth != NULL; auth = auth->next) {
|
||||||
|
if (auth->ready == ctx->cfg->cond_ready)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
HA_ATOMIC_INC(&ctx->dnstasks);
|
||||||
|
|
||||||
|
auth->rslv = acme_rslv_start(auth, &ctx->dnstasks, &errmsg);
|
||||||
|
if (!auth->rslv)
|
||||||
|
goto abort;
|
||||||
|
auth->rslv->acme_task = task;
|
||||||
|
}
|
||||||
|
st = ACME_RSLV_READY;
|
||||||
|
goto wait;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ACME_RSLV_READY: {
|
||||||
|
struct acme_auth *auth;
|
||||||
|
int all_ready = 1;
|
||||||
|
|
||||||
|
/* if triggered by the CLI, wait for the DNS tasks to
|
||||||
|
* finish
|
||||||
|
*/
|
||||||
|
if (HA_ATOMIC_LOAD(&ctx->dnstasks) != 0)
|
||||||
|
goto wait;
|
||||||
|
|
||||||
|
/* triggered by the latest DNS task */
|
||||||
|
for (auth = ctx->auths; auth != NULL; auth = auth->next) {
|
||||||
|
if (auth->ready == ctx->cfg->cond_ready)
|
||||||
|
continue;
|
||||||
|
if (auth->rslv->result != RSLV_STATUS_VALID) {
|
||||||
|
send_log(NULL, LOG_NOTICE, "acme: %s: dns-01: Couldn't get the TXT record for \"_acme-challenge.%.*s\", expected \"%.*s\" (status=%d)\n",
|
||||||
|
ctx->store->path, (int)auth->dns.len, auth->dns.ptr,
|
||||||
|
(int)auth->token.len, auth->token.ptr,
|
||||||
|
auth->rslv->result);
|
||||||
|
all_ready = 0;
|
||||||
|
} else {
|
||||||
|
if (isteq(auth->rslv->txt, auth->token)) {
|
||||||
|
auth->ready |= ACME_RDY_DNS;
|
||||||
|
} else {
|
||||||
|
send_log(NULL, LOG_NOTICE, "acme: %s: dns-01: TXT record mismatch for \"_acme-challenge.%.*s\": expected \"%.*s\", got \"%.*s\"\n",
|
||||||
|
ctx->store->path, (int)auth->dns.len, auth->dns.ptr,
|
||||||
|
(int)auth->token.len, auth->token.ptr,
|
||||||
|
(int)auth->rslv->txt.len, auth->rslv->txt.ptr);
|
||||||
|
all_ready = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acme_rslv_free(auth->rslv);
|
||||||
|
auth->rslv = NULL;
|
||||||
|
}
|
||||||
|
if (all_ready) {
|
||||||
|
st = ACME_CHALLENGE;
|
||||||
|
ctx->next_auth = ctx->auths;
|
||||||
|
goto nextreq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not all ready yet, retry after dns-delay */
|
||||||
|
st = ACME_RSLV_WAIT;
|
||||||
|
ctx->http_state = ACME_HTTP_REQ;
|
||||||
|
ctx->state = st;
|
||||||
|
goto nextreq;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ACME_CHALLENGE:
|
case ACME_CHALLENGE:
|
||||||
if (http_st == ACME_HTTP_REQ) {
|
if (http_st == ACME_HTTP_REQ) {
|
||||||
|
/* if challenge is already validated we skip this stage */
|
||||||
|
if (ctx->next_auth->validated) {
|
||||||
|
if ((ctx->next_auth = ctx->next_auth->next) == NULL) {
|
||||||
|
st = ACME_CHKCHALLENGE;
|
||||||
|
ctx->next_auth = ctx->auths;
|
||||||
|
}
|
||||||
|
goto nextreq;
|
||||||
|
}
|
||||||
|
|
||||||
/* if the challenge is not ready, wait to be wakeup */
|
/* if the challenge is not ready, wait to be wakeup */
|
||||||
if (!ctx->next_auth->ready)
|
if (ctx->next_auth->ready != ctx->cfg->cond_ready)
|
||||||
goto wait;
|
goto wait;
|
||||||
|
|
||||||
if (acme_req_challenge(task, ctx, ctx->next_auth, &errmsg) != 0)
|
if (acme_req_challenge(task, ctx, ctx->next_auth, &errmsg) != 0)
|
||||||
@ -2292,6 +2586,14 @@ re:
|
|||||||
break;
|
break;
|
||||||
case ACME_CHKCHALLENGE:
|
case ACME_CHKCHALLENGE:
|
||||||
if (http_st == ACME_HTTP_REQ) {
|
if (http_st == ACME_HTTP_REQ) {
|
||||||
|
/* if challenge is already validated we skip this stage */
|
||||||
|
if (ctx->next_auth->validated) {
|
||||||
|
if ((ctx->next_auth = ctx->next_auth->next) == NULL)
|
||||||
|
st = ACME_FINALIZE;
|
||||||
|
|
||||||
|
goto nextreq;
|
||||||
|
}
|
||||||
|
|
||||||
if (acme_post_as_get(task, ctx, ctx->next_auth->chall, &errmsg) != 0)
|
if (acme_post_as_get(task, ctx, ctx->next_auth->chall, &errmsg) != 0)
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
@ -2526,9 +2828,9 @@ X509_REQ *acme_x509_req(EVP_PKEY *pkey, char **san)
|
|||||||
{
|
{
|
||||||
struct buffer *san_trash = NULL;
|
struct buffer *san_trash = NULL;
|
||||||
X509_REQ *x = NULL;
|
X509_REQ *x = NULL;
|
||||||
X509_NAME *nm;
|
X509_NAME *nm = NULL;
|
||||||
STACK_OF(X509_EXTENSION) *exts = NULL;
|
STACK_OF(X509_EXTENSION) *exts = NULL;
|
||||||
X509_EXTENSION *ext_san;
|
X509_EXTENSION *ext_san = NULL;
|
||||||
char *str_san = NULL;
|
char *str_san = NULL;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
@ -2559,26 +2861,36 @@ X509_REQ *acme_x509_req(EVP_PKEY *pkey, char **san)
|
|||||||
for (i = 0; san[i]; i++) {
|
for (i = 0; san[i]; i++) {
|
||||||
chunk_appendf(san_trash, "%sDNS:%s", i ? "," : "", san[i]);
|
chunk_appendf(san_trash, "%sDNS:%s", i ? "," : "", san[i]);
|
||||||
}
|
}
|
||||||
str_san = my_strndup(san_trash->area, san_trash->data);
|
if ((str_san = my_strndup(san_trash->area, san_trash->data)) == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
if ((ext_san = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, str_san)) == NULL)
|
if ((ext_san = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, str_san)) == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (!sk_X509_EXTENSION_push(exts, ext_san))
|
if (!sk_X509_EXTENSION_push(exts, ext_san))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
ext_san = NULL; /* handle double-free upon error */
|
||||||
|
|
||||||
if (!X509_REQ_add_extensions(x, exts))
|
if (!X509_REQ_add_extensions(x, exts))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
|
||||||
|
|
||||||
if (!X509_REQ_sign(x, pkey, EVP_sha256()))
|
if (!X509_REQ_sign(x, pkey, EVP_sha256()))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||||
|
X509_NAME_free(nm);
|
||||||
|
free(str_san);
|
||||||
free_trash_chunk(san_trash);
|
free_trash_chunk(san_trash);
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
X509_EXTENSION_free(ext_san);
|
||||||
|
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||||
|
X509_REQ_free(x);
|
||||||
|
X509_NAME_free(nm);
|
||||||
|
free(str_san);
|
||||||
free_trash_chunk(san_trash);
|
free_trash_chunk(san_trash);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -2627,7 +2939,7 @@ EVP_PKEY *acme_gen_tmp_pkey()
|
|||||||
/* start an ACME task */
|
/* start an ACME task */
|
||||||
static int acme_start_task(struct ckch_store *store, char **errmsg)
|
static int acme_start_task(struct ckch_store *store, char **errmsg)
|
||||||
{
|
{
|
||||||
struct task *task;
|
struct task *task = NULL;
|
||||||
struct acme_ctx *ctx = NULL;
|
struct acme_ctx *ctx = NULL;
|
||||||
struct acme_cfg *cfg;
|
struct acme_cfg *cfg;
|
||||||
struct ckch_store *newstore = NULL;
|
struct ckch_store *newstore = NULL;
|
||||||
@ -2712,6 +3024,8 @@ err:
|
|||||||
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
||||||
acme_ctx_destroy(ctx);
|
acme_ctx_destroy(ctx);
|
||||||
}
|
}
|
||||||
|
if (task)
|
||||||
|
task_destroy(task);
|
||||||
memprintf(errmsg, "%sCan't start the ACME client.", *errmsg ? *errmsg : "");
|
memprintf(errmsg, "%sCan't start the ACME client.", *errmsg ? *errmsg : "");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -2721,7 +3035,10 @@ static int cli_acme_renew_parse(char **args, char *payload, struct appctx *appct
|
|||||||
struct ckch_store *store = NULL;
|
struct ckch_store *store = NULL;
|
||||||
char *errmsg = NULL;
|
char *errmsg = NULL;
|
||||||
|
|
||||||
if (!*args[1]) {
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!*args[2]) {
|
||||||
memprintf(&errmsg, ": not enough parameters\n");
|
memprintf(&errmsg, ": not enough parameters\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -2755,13 +3072,16 @@ static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx
|
|||||||
const char *crt;
|
const char *crt;
|
||||||
const char *dns;
|
const char *dns;
|
||||||
struct acme_ctx *ctx = NULL;
|
struct acme_ctx *ctx = NULL;
|
||||||
struct acme_auth *auth;
|
struct acme_auth *auth = NULL;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
int remain = 0;
|
int remain = 0;
|
||||||
struct ebmb_node *node = NULL;
|
struct ebmb_node *node = NULL;
|
||||||
|
|
||||||
if (!*args[2] && !*args[3] && !*args[4]) {
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
memprintf(&msg, ": not enough parameters\n");
|
return 1;
|
||||||
|
|
||||||
|
if (!*args[2] || !*args[3] || !*args[4]) {
|
||||||
|
memprintf(&msg, "Not enough parameters: \"acme challenge_ready <certfile> domain <domain>\"\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2772,17 +3092,18 @@ static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx
|
|||||||
node = ebst_lookup(&acme_tasks, crt);
|
node = ebst_lookup(&acme_tasks, crt);
|
||||||
if (node) {
|
if (node) {
|
||||||
ctx = ebmb_entry(node, struct acme_ctx, node);
|
ctx = ebmb_entry(node, struct acme_ctx, node);
|
||||||
|
if (ctx->cfg->cond_ready & ACME_RDY_CLI)
|
||||||
auth = ctx->auths;
|
auth = ctx->auths;
|
||||||
while (auth) {
|
while (auth) {
|
||||||
if (strncmp(dns, auth->dns.ptr, auth->dns.len) == 0) {
|
if (strncmp(dns, auth->dns.ptr, auth->dns.len) == 0) {
|
||||||
if (!auth->ready) {
|
if (!(auth->ready & ACME_RDY_CLI)) {
|
||||||
auth->ready = 1;
|
auth->ready |= ACME_RDY_CLI;
|
||||||
found++;
|
found++;
|
||||||
} else {
|
} else {
|
||||||
memprintf(&msg, "ACME challenge for crt \"%s\" and dns \"%s\" was already READY !\n", crt, dns);
|
memprintf(&msg, "ACME challenge for crt \"%s\" and dns \"%s\" was already READY !\n", crt, dns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auth->ready == 0)
|
if ((auth->ready & ACME_RDY_CLI) == 0)
|
||||||
remain++;
|
remain++;
|
||||||
auth = auth->next;
|
auth = auth->next;
|
||||||
}
|
}
|
||||||
@ -2790,7 +3111,7 @@ static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx
|
|||||||
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
||||||
if (!found) {
|
if (!found) {
|
||||||
if (!msg)
|
if (!msg)
|
||||||
memprintf(&msg, "Couldn't find the ACME task using crt \"%s\" and dns \"%s\" !\n", crt, dns);
|
memprintf(&msg, "Couldn't find an ACME task using crt \"%s\" and dns \"%s\" to set as ready!\n", crt, dns);
|
||||||
goto err;
|
goto err;
|
||||||
} else {
|
} else {
|
||||||
if (!remain) {
|
if (!remain) {
|
||||||
@ -2882,8 +3203,12 @@ end:
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cli_acme_ps(char **args, char *payload, struct appctx *appctx, void *private)
|
static int cli_acme_parse_status(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2891,7 +3216,7 @@ static int cli_acme_ps(char **args, char *payload, struct appctx *appctx, void *
|
|||||||
|
|
||||||
static struct cli_kw_list cli_kws = {{ },{
|
static struct cli_kw_list cli_kws = {{ },{
|
||||||
{ { "acme", "renew", NULL }, "acme renew <certfile> : renew a certificate using the ACME protocol", cli_acme_renew_parse, NULL, NULL, NULL, 0 },
|
{ { "acme", "renew", NULL }, "acme renew <certfile> : renew a certificate using the ACME protocol", cli_acme_renew_parse, NULL, NULL, NULL, 0 },
|
||||||
{ { "acme", "status", NULL }, "acme status : show status of certificates configured with ACME", cli_acme_ps, cli_acme_status_io_handler, NULL, NULL, 0 },
|
{ { "acme", "status", NULL }, "acme status : show status of certificates configured with ACME", cli_acme_parse_status, cli_acme_status_io_handler, NULL, NULL, 0 },
|
||||||
{ { "acme", "challenge_ready", NULL }, "acme challenge_ready <certfile> domain <domain> : notify HAProxy that the ACME challenge is ready", cli_acme_chall_ready_parse, NULL, NULL, NULL, 0 },
|
{ { "acme", "challenge_ready", NULL }, "acme challenge_ready <certfile> domain <domain> : notify HAProxy that the ACME challenge is ready", cli_acme_chall_ready_parse, NULL, NULL, NULL, 0 },
|
||||||
{ { NULL }, NULL, NULL, NULL }
|
{ { NULL }, NULL, NULL, NULL }
|
||||||
}};
|
}};
|
||||||
|
|||||||
166
src/acme_resolvers.c
Normal file
166
src/acme_resolvers.c
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implements the DNS resolution pre-check for dns-01
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <haproxy/openssl-compat.h>
|
||||||
|
|
||||||
|
#if defined(HAVE_ACME)
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <haproxy/acme_resolvers.h>
|
||||||
|
#include <haproxy/applet.h>
|
||||||
|
#include <haproxy/obj_type.h>
|
||||||
|
#include <haproxy/resolvers.h>
|
||||||
|
#include <haproxy/tools.h>
|
||||||
|
|
||||||
|
/* success callback, copy the TXT string to rslv->txt */
|
||||||
|
static int acme_rslv_success_cb(struct resolv_requester *req, struct dns_counters *counters)
|
||||||
|
{
|
||||||
|
struct acme_rslv *rslv = objt_acme_rslv(req->owner);
|
||||||
|
struct resolv_resolution *res;
|
||||||
|
struct eb32_node *eb32;
|
||||||
|
struct resolv_answer_item *item;
|
||||||
|
|
||||||
|
if (!rslv)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
rslv->result = RSLV_STATUS_INVALID;
|
||||||
|
|
||||||
|
res = req->resolution;
|
||||||
|
if (!res)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* XXX: must fail on multiple TXT entries for the same dn */
|
||||||
|
|
||||||
|
/* copy the data from the response tree */
|
||||||
|
for (eb32 = eb32_first(&res->response.answer_tree); eb32 != NULL; eb32 = eb32_next(eb32)) {
|
||||||
|
item = eb32_entry(eb32, typeof(*item), link);
|
||||||
|
/* only handle 1 entry */
|
||||||
|
if (item->type == DNS_RTYPE_TXT) {
|
||||||
|
int len = item->data_len;
|
||||||
|
|
||||||
|
if (len > DNS_MAX_NAME_SIZE)
|
||||||
|
len = DNS_MAX_NAME_SIZE;
|
||||||
|
rslv->txt = istdup(ist2(item->data.target, len));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rslv->result = RSLV_STATUS_VALID;
|
||||||
|
done:
|
||||||
|
/* if there's no other DNS task for this acme task, wake up acme_task */
|
||||||
|
if (HA_ATOMIC_SUB_FETCH(rslv->dnstasks, 1) == 0) {
|
||||||
|
if (rslv->acme_task)
|
||||||
|
task_wakeup(rslv->acme_task, TASK_WOKEN_MSG);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* error callback, set the error code to rslv->result */
|
||||||
|
static int acme_rslv_error_cb(struct resolv_requester *req, int error_code)
|
||||||
|
{
|
||||||
|
struct acme_rslv *rslv = objt_acme_rslv(req->owner);
|
||||||
|
|
||||||
|
if (!rslv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rslv->result = error_code;
|
||||||
|
if (HA_ATOMIC_SUB_FETCH(rslv->dnstasks, 1) == 0) {
|
||||||
|
if (rslv->acme_task)
|
||||||
|
task_wakeup(rslv->acme_task, TASK_WOKEN_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unlink from the resolver and free the acme_rslv */
|
||||||
|
void acme_rslv_free(struct acme_rslv *rslv)
|
||||||
|
{
|
||||||
|
if (!rslv)
|
||||||
|
return;
|
||||||
|
if (rslv->requester)
|
||||||
|
resolv_unlink_resolution(rslv->requester);
|
||||||
|
free(rslv->hostname_dn);
|
||||||
|
istfree(&rslv->txt);
|
||||||
|
free(rslv);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct acme_rslv *acme_rslv_start(struct acme_auth *auth, unsigned int *dnstasks, char **errmsg)
|
||||||
|
{
|
||||||
|
struct acme_rslv *rslv = NULL;
|
||||||
|
struct resolvers *resolvers;
|
||||||
|
char hostname[DNS_MAX_NAME_SIZE + 1];
|
||||||
|
char dn[DNS_MAX_NAME_SIZE + 1];
|
||||||
|
int hostname_len;
|
||||||
|
int dn_len;
|
||||||
|
|
||||||
|
/* XXX: allow to change the resolvers section to use */
|
||||||
|
resolvers = find_resolvers_by_id("default");
|
||||||
|
if (!resolvers) {
|
||||||
|
memprintf(errmsg, "couldn't find the \"default\" resolvers section!\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dns-01 TXT record lives at _acme-challenge.<domain> */
|
||||||
|
hostname_len = snprintf(hostname, sizeof(hostname), "_acme-challenge.%.*s",
|
||||||
|
(int)auth->dns.len, auth->dns.ptr);
|
||||||
|
if (hostname_len < 0 || hostname_len >= (int)sizeof(hostname)) {
|
||||||
|
memprintf(errmsg, "hostname \"_acme-challenge.%.*s\" too long!\n", (int)auth->dns.len, auth->dns.ptr);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
dn_len = resolv_str_to_dn_label(hostname, hostname_len, dn, sizeof(dn));
|
||||||
|
if (dn_len <= 0) {
|
||||||
|
memprintf(errmsg, "couldn't convert hostname \"_acme-challenge.%.*s\" into dn label\n", (int)auth->dns.len, auth->dns.ptr);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rslv = calloc(1, sizeof(*rslv));
|
||||||
|
if (!rslv) {
|
||||||
|
memprintf(errmsg, "Could not allocate memory\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rslv->obj_type = OBJ_TYPE_ACME_RSLV;
|
||||||
|
rslv->resolvers = resolvers;
|
||||||
|
rslv->hostname_dn = strdup(dn);
|
||||||
|
rslv->hostname_dn_len = dn_len;
|
||||||
|
rslv->result = RSLV_STATUS_NONE;
|
||||||
|
rslv->success_cb = acme_rslv_success_cb;
|
||||||
|
rslv->error_cb = acme_rslv_error_cb;
|
||||||
|
rslv->dnstasks = dnstasks;
|
||||||
|
|
||||||
|
if (!rslv->hostname_dn) {
|
||||||
|
memprintf(errmsg, "Could not allocate memory\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolv_link_resolution(rslv, OBJ_TYPE_ACME_RSLV, 0) < 0) {
|
||||||
|
memprintf(errmsg, "Could not create resolution task for \"%.*s\"\n", hostname_len, hostname);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolv_trigger_resolution(rslv->requester);
|
||||||
|
|
||||||
|
return rslv;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (rslv)
|
||||||
|
free(rslv->hostname_dn);
|
||||||
|
free(rslv);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_ACME */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* c-indent-level: 8
|
||||||
|
* c-basic-offset: 8
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@ -511,7 +511,7 @@ size_t appctx_htx_rcv_buf(struct appctx *appctx, struct buffer *buf, size_t coun
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
htx_xfer_blks(buf_htx, appctx_htx, count, HTX_BLK_UNUSED);
|
htx_xfer(buf_htx, appctx_htx, count, HTX_XFER_DEFAULT);
|
||||||
buf_htx->flags |= (appctx_htx->flags & (HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR));
|
buf_htx->flags |= (appctx_htx->flags & (HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR));
|
||||||
if (htx_is_empty(appctx_htx)) {
|
if (htx_is_empty(appctx_htx)) {
|
||||||
buf_htx->flags |= (appctx_htx->flags & HTX_FL_EOM);
|
buf_htx->flags |= (appctx_htx->flags & HTX_FL_EOM);
|
||||||
@ -608,7 +608,7 @@ size_t appctx_htx_snd_buf(struct appctx *appctx, struct buffer *buf, size_t coun
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
htx_xfer_blks(appctx_htx, buf_htx, count, HTX_BLK_UNUSED);
|
htx_xfer(appctx_htx, buf_htx, count, HTX_XFER_DEFAULT);
|
||||||
if (htx_is_empty(buf_htx)) {
|
if (htx_is_empty(buf_htx)) {
|
||||||
appctx_htx->flags |= (buf_htx->flags & HTX_FL_EOM);
|
appctx_htx->flags |= (buf_htx->flags & HTX_FL_EOM);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2067,7 +2067,7 @@ int connect_server(struct stream *s)
|
|||||||
* available.
|
* available.
|
||||||
*
|
*
|
||||||
* This check must be performed before conn_prepare()
|
* This check must be performed before conn_prepare()
|
||||||
* to ensure consistency accross the whole stack, in
|
* to ensure consistency across the whole stack, in
|
||||||
* particular for QUIC between quic-conn and mux layer.
|
* particular for QUIC between quic-conn and mux layer.
|
||||||
*/
|
*/
|
||||||
if (IS_HTX_STRM(s) && srv->use_ssl &&
|
if (IS_HTX_STRM(s) && srv->use_ssl &&
|
||||||
@ -2124,6 +2124,11 @@ int connect_server(struct stream *s)
|
|||||||
srv_conn->flags |= CO_FL_SOCKS4;
|
srv_conn->flags |= CO_FL_SOCKS4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (srv && srv->mux_proto && isteq(srv->mux_proto->token, ist("qmux"))) {
|
||||||
|
srv_conn->flags |= (CO_FL_QSTRM_RECV|CO_FL_QSTRM_SEND);
|
||||||
|
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)
|
||||||
/* if websocket stream, try to update connection ALPN. */
|
/* if websocket stream, try to update connection ALPN. */
|
||||||
if (unlikely(s->flags & SF_WEBSOCKET) &&
|
if (unlikely(s->flags & SF_WEBSOCKET) &&
|
||||||
@ -3063,7 +3068,7 @@ int be_downtime(struct proxy *px) {
|
|||||||
|
|
||||||
/* Checks if <px> backend supports the addition of servers at runtime. Either a
|
/* Checks if <px> backend supports the addition of servers at runtime. Either a
|
||||||
* backend or a defaults proxy are supported. If proxy is incompatible, <msg>
|
* backend or a defaults proxy are supported. If proxy is incompatible, <msg>
|
||||||
* will be allocated to contain a textual explaination.
|
* will be allocated to contain a textual explanation.
|
||||||
*/
|
*/
|
||||||
int be_supports_dynamic_srv(struct proxy *px, char **msg)
|
int be_supports_dynamic_srv(struct proxy *px, char **msg)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -71,7 +71,7 @@ static void srv_diag_cookies(int *ret, struct server *srv, struct eb_root *cooki
|
|||||||
static void srv_diag_check_reuse(int *ret, struct server *srv, struct proxy *px)
|
static void srv_diag_check_reuse(int *ret, struct server *srv, struct proxy *px)
|
||||||
{
|
{
|
||||||
if (srv->do_check && srv->check.reuse_pool) {
|
if (srv->do_check && srv->check.reuse_pool) {
|
||||||
if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_HTTP_CHK) {
|
if (px->tcpcheck.rs && (px->tcpcheck.rs->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_HTTP_CHK) {
|
||||||
diag_warning(ret, "parsing [%s:%d] : 'server %s': check-reuse-pool is ineffective for non http-check rulesets.\n",
|
diag_warning(ret, "parsing [%s:%d] : 'server %s': check-reuse-pool is ineffective for non http-check rulesets.\n",
|
||||||
srv->conf.file, srv->conf.line, srv->id);
|
srv->conf.file, srv->conf.line, srv->id);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1358,14 +1358,15 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
err_code |= warnif_misplaced_http_req(curproxy, file, linenum, args[0], NULL);
|
if (warnif_misplaced_http_req(curproxy, file, linenum, args[0], NULL))
|
||||||
|
err_code |= ERR_WARN;
|
||||||
|
|
||||||
if (curproxy->cap & PR_CAP_FE)
|
if (curproxy->cap & PR_CAP_FE)
|
||||||
where |= SMP_VAL_FE_HRQ_HDR;
|
where |= SMP_VAL_FE_HRQ_HDR;
|
||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRQ_HDR;
|
where |= SMP_VAL_BE_HRQ_HDR;
|
||||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
LIST_APPEND(&curproxy->http_req_rules, &rule->list);
|
LIST_APPEND(&curproxy->http_req_rules, &rule->list);
|
||||||
@ -1400,7 +1401,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRS_HDR;
|
where |= SMP_VAL_BE_HRS_HDR;
|
||||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
LIST_APPEND(&curproxy->http_res_rules, &rule->list);
|
LIST_APPEND(&curproxy->http_res_rules, &rule->list);
|
||||||
@ -1434,7 +1435,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRS_HDR;
|
where |= SMP_VAL_BE_HRS_HDR;
|
||||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
LIST_APPEND(&curproxy->http_after_res_rules, &rule->list);
|
LIST_APPEND(&curproxy->http_after_res_rules, &rule->list);
|
||||||
@ -1491,14 +1492,15 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
LIST_APPEND(&curproxy->redirect_rules, &rule->list);
|
LIST_APPEND(&curproxy->redirect_rules, &rule->list);
|
||||||
err_code |= warnif_misplaced_redirect(curproxy, file, linenum, args[0], NULL);
|
if (warnif_misplaced_redirect(curproxy, file, linenum, args[0], NULL))
|
||||||
|
err_code |= ERR_WARN;
|
||||||
|
|
||||||
if (curproxy->cap & PR_CAP_FE)
|
if (curproxy->cap & PR_CAP_FE)
|
||||||
where |= SMP_VAL_FE_HRQ_HDR;
|
where |= SMP_VAL_FE_HRQ_HDR;
|
||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRQ_HDR;
|
where |= SMP_VAL_BE_HRQ_HDR;
|
||||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
}
|
}
|
||||||
else if (strcmp(args[0], "use_backend") == 0) {
|
else if (strcmp(args[0], "use_backend") == 0) {
|
||||||
@ -1528,7 +1530,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_FE_SET_BCK, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, SMP_VAL_FE_SET_BCK, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
}
|
}
|
||||||
else if (*args[2]) {
|
else if (*args[2]) {
|
||||||
@ -1591,7 +1593,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_SET_SRV, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_SET_SRV, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
rule = calloc(1, sizeof(*rule));
|
rule = calloc(1, sizeof(*rule));
|
||||||
@ -1646,7 +1648,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
* where force-persist is applied.
|
* where force-persist is applied.
|
||||||
*/
|
*/
|
||||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_REQ_CNT, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_REQ_CNT, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
rule = calloc(1, sizeof(*rule));
|
rule = calloc(1, sizeof(*rule));
|
||||||
@ -1814,7 +1816,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_STO_RUL, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_STO_RUL, &errmsg);
|
||||||
else
|
else
|
||||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_SET_SRV, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_SET_SRV, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
rule = calloc(1, sizeof(*rule));
|
rule = calloc(1, sizeof(*rule));
|
||||||
@ -1872,7 +1874,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRQ_HDR;
|
where |= SMP_VAL_BE_HRQ_HDR;
|
||||||
err_code |= warnif_cond_conflicts(cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
rule = calloc(1, sizeof(*rule));
|
rule = calloc(1, sizeof(*rule));
|
||||||
@ -1952,7 +1954,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRQ_HDR;
|
where |= SMP_VAL_BE_HRQ_HDR;
|
||||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
LIST_APPEND(&curproxy->uri_auth->http_req_rules, &rule->list);
|
LIST_APPEND(&curproxy->uri_auth->http_req_rules, &rule->list);
|
||||||
|
|
||||||
@ -1974,7 +1976,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
if (!stats_check_init_uri_auth(&curproxy->uri_auth))
|
if (!stats_check_init_uri_auth(&curproxy->uri_auth))
|
||||||
goto alloc_error;
|
goto alloc_error;
|
||||||
} else if (strcmp(args[1], "hide-version") == 0) {
|
} else if (strcmp(args[1], "hide-version") == 0) {
|
||||||
if (!stats_set_flag(&curproxy->uri_auth, STAT_F_HIDEVER))
|
if (curproxy->uri_auth)
|
||||||
|
curproxy->uri_auth->flags &= ~STAT_F_SHOWVER;
|
||||||
|
} else if (strcmp(args[1], "show-version") == 0) {
|
||||||
|
if (!stats_set_flag(&curproxy->uri_auth, STAT_F_SHOWVER))
|
||||||
goto alloc_error;
|
goto alloc_error;
|
||||||
} else if (strcmp(args[1], "show-legends") == 0) {
|
} else if (strcmp(args[1], "show-legends") == 0) {
|
||||||
if (!stats_set_flag(&curproxy->uri_auth, STAT_F_SHLGNDS))
|
if (!stats_set_flag(&curproxy->uri_auth, STAT_F_SHLGNDS))
|
||||||
@ -2041,7 +2046,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stats_error_parsing:
|
stats_error_parsing:
|
||||||
ha_alert("parsing [%s:%d]: %s '%s', expects 'admin', 'uri', 'realm', 'auth', 'scope', 'enable', 'hide-version', 'show-node', 'show-desc' or 'show-legends'.\n",
|
ha_alert("parsing [%s:%d]: %s '%s', expects 'admin', 'uri', 'realm', 'auth', 'scope', 'enable', 'hide-version', 'show-node', 'show-desc' , 'show-legends' or 'show-version'.\n",
|
||||||
file, linenum, *args[1]?"unknown stats parameter":"missing keyword in", args[*args[1]?1:0]);
|
file, linenum, *args[1]?"unknown stats parameter":"missing keyword in", args[*args[1]?1:0]);
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
goto out;
|
goto out;
|
||||||
@ -2200,6 +2205,42 @@ stats_error_parsing:
|
|||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(args[1], "use-small-buffers") == 0) {
|
||||||
|
unsigned int flags = PR_O2_USE_SBUF_ALL;
|
||||||
|
|
||||||
|
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[1], NULL)) {
|
||||||
|
err_code |= ERR_WARN;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*(args[2])) {
|
||||||
|
int cur_arg;
|
||||||
|
|
||||||
|
flags = 0;
|
||||||
|
for (cur_arg = 2; *(args[cur_arg]); cur_arg++) {
|
||||||
|
if (strcmp(args[cur_arg], "queue") == 0)
|
||||||
|
flags |= PR_O2_USE_SBUF_QUEUE;
|
||||||
|
else if (strcmp(args[cur_arg], "l7-retries") == 0)
|
||||||
|
flags |= PR_O2_USE_SBUF_L7_RETRY;
|
||||||
|
else if (strcmp(args[cur_arg], "check") == 0)
|
||||||
|
flags |= PR_O2_USE_SBUF_CHECK;
|
||||||
|
else {
|
||||||
|
ha_alert("parsing [%s:%d] : invalid parameter '%s'. option '%s' expects 'queue', 'l7-retries' or 'check' value.\n",
|
||||||
|
file, linenum, args[cur_arg], args[1]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (kwm == KWM_STD) {
|
||||||
|
curproxy->options2 &= ~PR_O2_USE_SBUF_ALL;
|
||||||
|
curproxy->options2 |= flags;
|
||||||
|
}
|
||||||
|
else if (kwm == KWM_NO) {
|
||||||
|
curproxy->options2 &= ~flags;
|
||||||
|
}
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (kwm != KWM_STD) {
|
if (kwm != KWM_STD) {
|
||||||
ha_alert("parsing [%s:%d]: negation/default is not supported for option '%s'.\n",
|
ha_alert("parsing [%s:%d]: negation/default is not supported for option '%s'.\n",
|
||||||
@ -2557,7 +2598,8 @@ stats_error_parsing:
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
err_code |= warnif_misplaced_monitor(curproxy, file, linenum, args[0], args[1]);
|
if (warnif_misplaced_monitor(curproxy, file, linenum, args[0], args[1]))
|
||||||
|
err_code |= ERR_WARN;
|
||||||
if ((cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + 2, &errmsg)) == NULL) {
|
if ((cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + 2, &errmsg)) == NULL) {
|
||||||
ha_alert("parsing [%s:%d] : error detected while parsing a '%s %s' condition : %s.\n",
|
ha_alert("parsing [%s:%d] : error detected while parsing a '%s %s' condition : %s.\n",
|
||||||
file, linenum, args[0], args[1], errmsg);
|
file, linenum, args[0], args[1], errmsg);
|
||||||
|
|||||||
@ -521,7 +521,7 @@ static int ssl_parse_global_keylog(char **args, int section_type, struct proxy *
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Allow to explicitely disable certificate compression when set to "off" */
|
/* Allow to explicitly disable certificate compression when set to "off" */
|
||||||
#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
|
#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
|
||||||
static int ssl_parse_certificate_compression(char **args, int section_type, struct proxy *curpx,
|
static int ssl_parse_certificate_compression(char **args, int section_type, struct proxy *curpx,
|
||||||
const struct proxy *defpx, const char *file, int line,
|
const struct proxy *defpx, const char *file, int line,
|
||||||
|
|||||||
@ -63,6 +63,7 @@
|
|||||||
#include <haproxy/global.h>
|
#include <haproxy/global.h>
|
||||||
#include <haproxy/http_ana.h>
|
#include <haproxy/http_ana.h>
|
||||||
#include <haproxy/http_rules.h>
|
#include <haproxy/http_rules.h>
|
||||||
|
#include <haproxy/http_htx.h>
|
||||||
#include <haproxy/lb_chash.h>
|
#include <haproxy/lb_chash.h>
|
||||||
#include <haproxy/lb_fas.h>
|
#include <haproxy/lb_fas.h>
|
||||||
#include <haproxy/lb_fwlc.h>
|
#include <haproxy/lb_fwlc.h>
|
||||||
@ -2318,6 +2319,18 @@ int check_config_validity()
|
|||||||
"Please fix either value to remove this warning.\n",
|
"Please fix either value to remove this warning.\n",
|
||||||
global.tune.bufsize_large, global.tune.bufsize);
|
global.tune.bufsize_large, global.tune.bufsize);
|
||||||
global.tune.bufsize_large = 0;
|
global.tune.bufsize_large = 0;
|
||||||
|
err_code |= ERR_WARN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global.tune.bufsize_small > 0) {
|
||||||
|
if (global.tune.bufsize_small == global.tune.bufsize)
|
||||||
|
global.tune.bufsize_small = 0;
|
||||||
|
else if (global.tune.bufsize_small > global.tune.bufsize) {
|
||||||
|
ha_warning("invalid small buffer size %d bytes which is greater to default bufsize %d bytes.\n",
|
||||||
|
global.tune.bufsize_small, global.tune.bufsize);
|
||||||
|
global.tune.bufsize_small = 0;
|
||||||
|
err_code |= ERR_WARN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2392,6 +2405,8 @@ int check_config_validity()
|
|||||||
else {
|
else {
|
||||||
cfgerr += acl_find_targets(defpx);
|
cfgerr += acl_find_targets(defpx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err_code |= proxy_check_http_errors(defpx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* starting to initialize the main proxies list */
|
/* starting to initialize the main proxies list */
|
||||||
|
|||||||
111
src/check.c
111
src/check.c
@ -235,19 +235,7 @@ static void check_trace(enum trace_level level, uint64_t mask,
|
|||||||
if (mask & CHK_EV_TCPCHK) {
|
if (mask & CHK_EV_TCPCHK) {
|
||||||
const char *type;
|
const char *type;
|
||||||
|
|
||||||
switch (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) {
|
type = tcpcheck_ruleset_type_to_str(check->tcpcheck->rs);
|
||||||
case TCPCHK_RULES_PGSQL_CHK: type = "PGSQL"; break;
|
|
||||||
case TCPCHK_RULES_REDIS_CHK: type = "REDIS"; break;
|
|
||||||
case TCPCHK_RULES_SMTP_CHK: type = "SMTP"; break;
|
|
||||||
case TCPCHK_RULES_HTTP_CHK: type = "HTTP"; break;
|
|
||||||
case TCPCHK_RULES_MYSQL_CHK: type = "MYSQL"; break;
|
|
||||||
case TCPCHK_RULES_LDAP_CHK: type = "LDAP"; break;
|
|
||||||
case TCPCHK_RULES_SSL3_CHK: type = "SSL3"; break;
|
|
||||||
case TCPCHK_RULES_AGENT_CHK: type = "AGENT"; break;
|
|
||||||
case TCPCHK_RULES_SPOP_CHK: type = "SPOP"; break;
|
|
||||||
case TCPCHK_RULES_TCP_CHK: type = "TCP"; break;
|
|
||||||
default: type = "???"; break;
|
|
||||||
}
|
|
||||||
if (check->current_step)
|
if (check->current_step)
|
||||||
chunk_appendf(&trace_buf, " - tcp-check=(%s,%d)", type, tcpcheck_get_step_id(check, NULL));
|
chunk_appendf(&trace_buf, " - tcp-check=(%s,%d)", type, tcpcheck_get_step_id(check, NULL));
|
||||||
else
|
else
|
||||||
@ -271,7 +259,7 @@ static void check_trace(enum trace_level level, uint64_t mask,
|
|||||||
buf = (b_is_null(&check->bo) ? NULL : &check->bo);
|
buf = (b_is_null(&check->bo) ? NULL : &check->bo);
|
||||||
|
|
||||||
if (buf) {
|
if (buf) {
|
||||||
if ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
|
if ((check->tcpcheck->rs->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
|
||||||
int full = (src->verbosity == CHK_VERB_COMPLETE);
|
int full = (src->verbosity == CHK_VERB_COMPLETE);
|
||||||
|
|
||||||
chunk_memcat(&trace_buf, "\n\t", 2);
|
chunk_memcat(&trace_buf, "\n\t", 2);
|
||||||
@ -832,7 +820,7 @@ void chk_report_conn_err(struct check *check, int errno_bck, int expired)
|
|||||||
chk = get_trash_chunk();
|
chk = get_trash_chunk();
|
||||||
|
|
||||||
if (check->type == PR_O2_TCPCHK_CHK &&
|
if (check->type == PR_O2_TCPCHK_CHK &&
|
||||||
(check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
|
(check->tcpcheck->rs->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
|
||||||
step = tcpcheck_get_step_id(check, NULL);
|
step = tcpcheck_get_step_id(check, NULL);
|
||||||
if (!step) {
|
if (!step) {
|
||||||
TRACE_DEVEL("initial connection failure", CHK_EV_HCHK_END|CHK_EV_HCHK_ERR, check);
|
TRACE_DEVEL("initial connection failure", CHK_EV_HCHK_END|CHK_EV_HCHK_ERR, check);
|
||||||
@ -1317,7 +1305,7 @@ struct task *process_chk_conn(struct task *t, void *context, unsigned int state)
|
|||||||
|
|
||||||
check->current_step = NULL;
|
check->current_step = NULL;
|
||||||
|
|
||||||
check->sc = sc_new_from_check(check, SC_FL_NONE);
|
check->sc = sc_new_from_check(check);
|
||||||
if (!check->sc) {
|
if (!check->sc) {
|
||||||
set_server_check_status(check, HCHK_STATUS_SOCKERR, NULL);
|
set_server_check_status(check, HCHK_STATUS_SOCKERR, NULL);
|
||||||
goto end;
|
goto end;
|
||||||
@ -1515,14 +1503,16 @@ int check_buf_available(void *target)
|
|||||||
/*
|
/*
|
||||||
* Allocate a buffer. If it fails, it adds the check in buffer wait queue.
|
* Allocate a buffer. If it fails, it adds the check in buffer wait queue.
|
||||||
*/
|
*/
|
||||||
struct buffer *check_get_buf(struct check *check, struct buffer *bptr)
|
struct buffer *check_get_buf(struct check *check, struct buffer *bptr, unsigned int small_buffer)
|
||||||
{
|
{
|
||||||
struct buffer *buf = NULL;
|
struct buffer *buf = NULL;
|
||||||
|
|
||||||
|
if (small_buffer == 0 || (buf = b_alloc_small(bptr)) == NULL) {
|
||||||
if (likely(!LIST_INLIST(&check->buf_wait.list)) &&
|
if (likely(!LIST_INLIST(&check->buf_wait.list)) &&
|
||||||
unlikely((buf = b_alloc(bptr, DB_CHANNEL)) == NULL)) {
|
unlikely((buf = b_alloc(bptr, DB_CHANNEL)) == NULL)) {
|
||||||
b_queue(DB_CHANNEL, &check->buf_wait, check, check_buf_available);
|
b_queue(DB_CHANNEL, &check->buf_wait, check, check_buf_available);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1533,7 +1523,10 @@ struct buffer *check_get_buf(struct check *check, struct buffer *bptr)
|
|||||||
void check_release_buf(struct check *check, struct buffer *bptr)
|
void check_release_buf(struct check *check, struct buffer *bptr)
|
||||||
{
|
{
|
||||||
if (bptr->size) {
|
if (bptr->size) {
|
||||||
|
int defbuf = b_is_default(bptr);
|
||||||
|
|
||||||
b_free(bptr);
|
b_free(bptr);
|
||||||
|
if (defbuf)
|
||||||
offer_buffers(check->buf_wait.target, 1);
|
offer_buffers(check->buf_wait.target, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1559,9 +1552,10 @@ void free_check(struct check *check)
|
|||||||
* done for health-check : the proxy is the owner of the rules / vars
|
* done for health-check : the proxy is the owner of the rules / vars
|
||||||
* in this case.
|
* in this case.
|
||||||
*/
|
*/
|
||||||
if (check->state & CHK_ST_AGENT) {
|
if (check->state & CHK_ST_AGENT || check->tcpcheck->healthcheck) {
|
||||||
free_tcpcheck_vars(&check->tcpcheck_rules->preset_vars);
|
free_tcpcheck_vars(&check->tcpcheck->preset_vars);
|
||||||
ha_free(&check->tcpcheck_rules);
|
ha_free(&check->tcpcheck->healthcheck);
|
||||||
|
ha_free(&check->tcpcheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
ha_free(&check->pool_conn_name);
|
ha_free(&check->pool_conn_name);
|
||||||
@ -1654,7 +1648,6 @@ int start_check_task(struct check *check, int mininter,
|
|||||||
*/
|
*/
|
||||||
static int start_checks()
|
static int start_checks()
|
||||||
{
|
{
|
||||||
|
|
||||||
struct proxy *px;
|
struct proxy *px;
|
||||||
struct server *s;
|
struct server *s;
|
||||||
char *errmsg = NULL;
|
char *errmsg = NULL;
|
||||||
@ -1681,6 +1674,10 @@ 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) &&
|
||||||
|
(s->check.tcpcheck->rs->flags & TCPCHK_RULES_MAY_USE_SBUF))
|
||||||
|
s->check.state |= CHK_ST_USE_SMALL_BUFF;
|
||||||
|
|
||||||
if (s->check.state & CHK_ST_CONFIGURED) {
|
if (s->check.state & CHK_ST_CONFIGURED) {
|
||||||
nbcheck++;
|
nbcheck++;
|
||||||
if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
|
if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
|
||||||
@ -1795,7 +1792,7 @@ 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;
|
||||||
|
|
||||||
check_type = srv->check.tcpcheck_rules->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)) {
|
||||||
/* If neither a port nor an addr was specified and no check
|
/* If neither a port nor an addr was specified and no check
|
||||||
@ -1805,7 +1802,15 @@ int init_srv_check(struct server *srv)
|
|||||||
* specified.
|
* specified.
|
||||||
*/
|
*/
|
||||||
if (!srv->check.port && !is_addr(&srv->check.addr)) {
|
if (!srv->check.port && !is_addr(&srv->check.addr)) {
|
||||||
if (!srv->check.use_ssl && srv->use_ssl != -1)
|
/*
|
||||||
|
* If any setting is set for the check, then we can't
|
||||||
|
* assume we'll use the same XPRT as the server, the
|
||||||
|
* server may be QUIC, but we want a TCP check.
|
||||||
|
*/
|
||||||
|
if (!srv->check.use_ssl && srv->use_ssl != -1 &&
|
||||||
|
!srv->check.via_socks4 && !srv->check.send_proxy &&
|
||||||
|
(!srv->check.alpn_len || (srv->check.alpn_len == srv->ssl_ctx.alpn_len && !strncmp(srv->check.alpn_str, srv->ssl_ctx.alpn_str, srv->check.alpn_len))) &&
|
||||||
|
(!srv->check.mux_proto || srv->check.mux_proto != srv->mux_proto))
|
||||||
srv->check.xprt = srv->xprt;
|
srv->check.xprt = srv->xprt;
|
||||||
else if (srv->check.use_ssl == 1)
|
else if (srv->check.use_ssl == 1)
|
||||||
srv->check.xprt = xprt_get(XPRT_SSL);
|
srv->check.xprt = xprt_get(XPRT_SSL);
|
||||||
@ -1881,7 +1886,7 @@ int init_srv_check(struct server *srv)
|
|||||||
(!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
|
(!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
|
||||||
goto init;
|
goto init;
|
||||||
|
|
||||||
if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
|
if (LIST_ISEMPTY(&srv->check.tcpcheck->rs->rules)) {
|
||||||
ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
|
ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
|
||||||
proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
|
proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
|
||||||
ret |= ERR_ALERT | ERR_ABORT;
|
ret |= ERR_ALERT | ERR_ABORT;
|
||||||
@ -1889,7 +1894,7 @@ int init_srv_check(struct server *srv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* search the first action (connect / send / expect) in the list */
|
/* search the first action (connect / send / expect) in the list */
|
||||||
r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
|
r = get_first_tcpcheck_rule(srv->check.tcpcheck->rs);
|
||||||
if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
|
if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
|
||||||
ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
|
ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
|
||||||
"nor tcp_check rule 'connect' with port information.\n",
|
"nor tcp_check rule 'connect' with port information.\n",
|
||||||
@ -1899,7 +1904,7 @@ int init_srv_check(struct server *srv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* scan the tcp-check ruleset to ensure a port has been configured */
|
/* scan the tcp-check ruleset to ensure a port has been configured */
|
||||||
list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
|
list_for_each_entry(r, &srv->check.tcpcheck->rs->rules, list) {
|
||||||
if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port && !get_host_port(&r->connect.addr))) {
|
if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port && !get_host_port(&r->connect.addr))) {
|
||||||
ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
|
ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
|
||||||
"and a tcp_check rule 'connect' with no port information.\n",
|
"and a tcp_check rule 'connect' with no port information.\n",
|
||||||
@ -1946,7 +1951,7 @@ int init_srv_agent_check(struct server *srv)
|
|||||||
/* If there is no connect rule preceding all send / expect rules, an
|
/* If there is no connect rule preceding all send / expect rules, an
|
||||||
* implicit one is inserted before all others.
|
* implicit one is inserted before all others.
|
||||||
*/
|
*/
|
||||||
chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
|
chk = get_first_tcpcheck_rule(srv->agent.tcpcheck->rs);
|
||||||
if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
|
if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
|
||||||
chk = calloc(1, sizeof(*chk));
|
chk = calloc(1, sizeof(*chk));
|
||||||
if (!chk) {
|
if (!chk) {
|
||||||
@ -1958,14 +1963,14 @@ int init_srv_agent_check(struct server *srv)
|
|||||||
}
|
}
|
||||||
chk->action = TCPCHK_ACT_CONNECT;
|
chk->action = TCPCHK_ACT_CONNECT;
|
||||||
chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
|
chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
|
||||||
LIST_INSERT(srv->agent.tcpcheck_rules->list, &chk->list);
|
LIST_INSERT(&srv->agent.tcpcheck->rs->rules, &chk->list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* <chk> is always defined here and it is a CONNECT action. If there is
|
/* <chk> is always defined here and it is a CONNECT action. If there is
|
||||||
* a preset variable, it means there is an agent string defined and data
|
* a preset variable, it means there is an agent string defined and data
|
||||||
* will be sent after the connect.
|
* will be sent after the connect.
|
||||||
*/
|
*/
|
||||||
if (!LIST_ISEMPTY(&srv->agent.tcpcheck_rules->preset_vars))
|
if (!LIST_ISEMPTY(&srv->agent.tcpcheck->preset_vars))
|
||||||
chk->connect.options |= TCPCHK_OPT_HAS_DATA;
|
chk->connect.options |= TCPCHK_OPT_HAS_DATA;
|
||||||
|
|
||||||
|
|
||||||
@ -2056,6 +2061,7 @@ static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct
|
|||||||
char **errmsg)
|
char **errmsg)
|
||||||
{
|
{
|
||||||
struct sockaddr_storage *sk;
|
struct sockaddr_storage *sk;
|
||||||
|
struct protocol *proto;
|
||||||
int port1, port2, err_code = 0;
|
int port1, port2, err_code = 0;
|
||||||
|
|
||||||
|
|
||||||
@ -2064,7 +2070,7 @@ static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, NULL, NULL, errmsg, NULL, NULL, NULL,
|
sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, &proto, NULL, errmsg, NULL, NULL, NULL,
|
||||||
PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
|
PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
|
||||||
if (!sk) {
|
if (!sk) {
|
||||||
memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
|
memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
|
||||||
@ -2072,6 +2078,7 @@ static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
srv->check.addr = *sk;
|
srv->check.addr = *sk;
|
||||||
|
srv->check.proto = proto;
|
||||||
/* if agentaddr was never set, we can use addr */
|
/* if agentaddr was never set, we can use addr */
|
||||||
if (!(srv->flags & SRV_F_AGENTADDR))
|
if (!(srv->flags & SRV_F_AGENTADDR))
|
||||||
srv->agent.addr = *sk;
|
srv->agent.addr = *sk;
|
||||||
@ -2101,7 +2108,11 @@ static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
set_srv_agent_addr(srv, &sk);
|
set_srv_agent_addr(srv, &sk);
|
||||||
|
/* Agent currently only uses TCP */
|
||||||
|
if (sk.ss_family == AF_INET)
|
||||||
|
srv->agent.proto = &proto_tcpv4;
|
||||||
|
else
|
||||||
|
srv->agent.proto = &proto_tcpv6;
|
||||||
out:
|
out:
|
||||||
return err_code;
|
return err_code;
|
||||||
|
|
||||||
@ -2115,7 +2126,7 @@ static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx,
|
|||||||
char **errmsg)
|
char **errmsg)
|
||||||
{
|
{
|
||||||
struct tcpcheck_ruleset *rs = NULL;
|
struct tcpcheck_ruleset *rs = NULL;
|
||||||
struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
|
struct tcpcheck *tc = srv->agent.tcpcheck;
|
||||||
struct tcpcheck_rule *chk;
|
struct tcpcheck_rule *chk;
|
||||||
int err_code = 0;
|
int err_code = 0;
|
||||||
|
|
||||||
@ -2128,17 +2139,15 @@ static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx,
|
|||||||
return ERR_WARN;
|
return ERR_WARN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rules) {
|
if (!tc) {
|
||||||
rules = calloc(1, sizeof(*rules));
|
tc = calloc(1, sizeof(*tc));
|
||||||
if (!rules) {
|
if (!tc) {
|
||||||
memprintf(errmsg, "out of memory.");
|
memprintf(errmsg, "out of memory.");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
LIST_INIT(&rules->preset_vars);
|
LIST_INIT(&tc->preset_vars);
|
||||||
srv->agent.tcpcheck_rules = rules;
|
srv->agent.tcpcheck = tc;
|
||||||
}
|
}
|
||||||
rules->list = NULL;
|
|
||||||
rules->flags = 0;
|
|
||||||
|
|
||||||
rs = find_tcpcheck_ruleset("*agent-check");
|
rs = find_tcpcheck_ruleset("*agent-check");
|
||||||
if (rs)
|
if (rs)
|
||||||
@ -2171,9 +2180,9 @@ static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx,
|
|||||||
LIST_APPEND(&rs->rules, &chk->list);
|
LIST_APPEND(&rs->rules, &chk->list);
|
||||||
|
|
||||||
ruleset_found:
|
ruleset_found:
|
||||||
rules->list = &rs->rules;
|
tc->rs = rs;
|
||||||
rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
|
tc->flags &= ~TCPCHK_FL_UNUSED_RS;
|
||||||
rules->flags |= TCPCHK_RULES_AGENT_CHK;
|
rs->flags |= TCPCHK_RULES_AGENT_CHK;
|
||||||
srv->do_agent = 1;
|
srv->do_agent = 1;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
@ -2266,7 +2275,7 @@ static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx,
|
|||||||
|
|
||||||
int set_srv_agent_send(struct server *srv, const char *send)
|
int set_srv_agent_send(struct server *srv, const char *send)
|
||||||
{
|
{
|
||||||
struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
|
struct tcpcheck *tc = srv->agent.tcpcheck;
|
||||||
struct tcpcheck_var *var = NULL;
|
struct tcpcheck_var *var = NULL;
|
||||||
char *str;
|
char *str;
|
||||||
|
|
||||||
@ -2275,13 +2284,13 @@ int set_srv_agent_send(struct server *srv, const char *send)
|
|||||||
if (str == NULL || var == NULL)
|
if (str == NULL || var == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
free_tcpcheck_vars(&rules->preset_vars);
|
free_tcpcheck_vars(&tc->preset_vars);
|
||||||
|
|
||||||
var->data.type = SMP_T_STR;
|
var->data.type = SMP_T_STR;
|
||||||
var->data.u.str.area = str;
|
var->data.u.str.area = str;
|
||||||
var->data.u.str.data = strlen(str);
|
var->data.u.str.data = strlen(str);
|
||||||
LIST_INIT(&var->list);
|
LIST_INIT(&var->list);
|
||||||
LIST_APPEND(&rules->preset_vars, &var->list);
|
LIST_APPEND(&tc->preset_vars, &var->list);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
@ -2295,7 +2304,7 @@ int set_srv_agent_send(struct server *srv, const char *send)
|
|||||||
static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
|
static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
|
||||||
char **errmsg)
|
char **errmsg)
|
||||||
{
|
{
|
||||||
struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
|
struct tcpcheck *tc = srv->agent.tcpcheck;
|
||||||
int err_code = 0;
|
int err_code = 0;
|
||||||
|
|
||||||
if (!*(args[*cur_arg+1])) {
|
if (!*(args[*cur_arg+1])) {
|
||||||
@ -2303,14 +2312,14 @@ static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rules) {
|
if (!tc) {
|
||||||
rules = calloc(1, sizeof(*rules));
|
tc = calloc(1, sizeof(*tc));
|
||||||
if (!rules) {
|
if (!tc) {
|
||||||
memprintf(errmsg, "out of memory.");
|
memprintf(errmsg, "out of memory.");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
LIST_INIT(&rules->preset_vars);
|
LIST_INIT(&tc->preset_vars);
|
||||||
srv->agent.tcpcheck_rules = rules;
|
srv->agent.tcpcheck = tc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
|
if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
|
||||||
|
|||||||
99
src/chunk.c
99
src/chunk.c
@ -53,6 +53,22 @@ struct pool_head *pool_head_large_trash __read_mostly = NULL;
|
|||||||
/* this is used to drain data, and as a temporary large buffer */
|
/* this is used to drain data, and as a temporary large buffer */
|
||||||
THREAD_LOCAL struct buffer trash_large = { };
|
THREAD_LOCAL struct buffer trash_large = { };
|
||||||
|
|
||||||
|
/* small trash chunks used for various conversions */
|
||||||
|
static THREAD_LOCAL struct buffer *small_trash_chunk;
|
||||||
|
static THREAD_LOCAL struct buffer small_trash_chunk1;
|
||||||
|
static THREAD_LOCAL struct buffer small_trash_chunk2;
|
||||||
|
|
||||||
|
/* small trash buffers used for various conversions */
|
||||||
|
static int small_trash_size __read_mostly = 0;
|
||||||
|
static THREAD_LOCAL char *small_trash_buf1 = NULL;
|
||||||
|
static THREAD_LOCAL char *small_trash_buf2 = NULL;
|
||||||
|
|
||||||
|
/* the trash pool for reentrant allocations */
|
||||||
|
struct pool_head *pool_head_small_trash __read_mostly = NULL;
|
||||||
|
|
||||||
|
/* this is used to drain data, and as a temporary small buffer */
|
||||||
|
THREAD_LOCAL struct buffer trash_small = { };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns a pre-allocated and initialized trash chunk that can be used for any
|
* Returns a pre-allocated and initialized trash chunk that can be used for any
|
||||||
* type of conversion. Two chunks and their respective buffers are alternatively
|
* type of conversion. Two chunks and their respective buffers are alternatively
|
||||||
@ -80,7 +96,7 @@ struct buffer *get_trash_chunk(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Similar to get_trash_chunk() but return a pre-allocated large chunk
|
/* Similar to get_trash_chunk() but return a pre-allocated large chunk
|
||||||
* instead. Becasuse large buffers are not enabled by default, this function may
|
* instead. Because large buffers are not enabled by default, this function may
|
||||||
* return NULL.
|
* return NULL.
|
||||||
*/
|
*/
|
||||||
struct buffer *get_large_trash_chunk(void)
|
struct buffer *get_large_trash_chunk(void)
|
||||||
@ -103,14 +119,40 @@ struct buffer *get_large_trash_chunk(void)
|
|||||||
return large_trash_chunk;
|
return large_trash_chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Similar to get_trash_chunk() but return a pre-allocated small chunk
|
||||||
|
* instead. Because small buffers are not enabled by default, this function may
|
||||||
|
* return NULL.
|
||||||
|
*/
|
||||||
|
struct buffer *get_small_trash_chunk(void)
|
||||||
|
{
|
||||||
|
char *small_trash_buf;
|
||||||
|
|
||||||
|
if (!small_trash_size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (small_trash_chunk == &small_trash_chunk1) {
|
||||||
|
small_trash_chunk = &small_trash_chunk2;
|
||||||
|
small_trash_buf = small_trash_buf2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
small_trash_chunk = &small_trash_chunk1;
|
||||||
|
small_trash_buf = small_trash_buf1;
|
||||||
|
}
|
||||||
|
*small_trash_buf = 0;
|
||||||
|
chunk_init(small_trash_chunk, small_trash_buf, small_trash_size);
|
||||||
|
return small_trash_chunk;
|
||||||
|
}
|
||||||
|
|
||||||
/* 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 chubks are not
|
* fail if the requested size is too big or if the large chubks are not
|
||||||
* configured.
|
* configured.
|
||||||
*/
|
*/
|
||||||
struct buffer *get_trash_chunk_sz(size_t size)
|
struct buffer *get_trash_chunk_sz(size_t size)
|
||||||
{
|
{
|
||||||
if (likely(size <= trash_size))
|
if (likely(size > small_trash_size && size <= trash_size))
|
||||||
return get_trash_chunk();
|
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
|
||||||
@ -122,17 +164,20 @@ struct buffer *get_trash_chunk_sz(size_t size)
|
|||||||
*/
|
*/
|
||||||
struct buffer *get_larger_trash_chunk(struct buffer *chk)
|
struct buffer *get_larger_trash_chunk(struct buffer *chk)
|
||||||
{
|
{
|
||||||
struct buffer *chunk;
|
struct buffer *chunk = NULL;
|
||||||
|
|
||||||
if (!chk)
|
|
||||||
return get_trash_chunk();
|
|
||||||
|
|
||||||
/* No large buffers or current chunk is alread a large trash chunk */
|
|
||||||
if (!large_trash_size || chk->size == large_trash_size)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
|
if (!chk || chk->size == small_trash_size) {
|
||||||
|
/* no chunk or a small one, use a regular buffer */
|
||||||
|
chunk = get_trash_chunk();
|
||||||
|
}
|
||||||
|
else if (large_trash_size && chk->size <= large_trash_size) {
|
||||||
|
/* a regular byffer, use a large buffer if possible */
|
||||||
chunk = get_large_trash_chunk();
|
chunk = get_large_trash_chunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chk && chunk)
|
||||||
b_xfer(chunk, chk, b_data(chk));
|
b_xfer(chunk, chk, b_data(chk));
|
||||||
|
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,9 +211,29 @@ static int alloc_large_trash_buffers(int bufsize)
|
|||||||
return trash_large.area && large_trash_buf1 && large_trash_buf2;
|
return trash_large.area && large_trash_buf1 && large_trash_buf2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* allocates the trash small buffers if necessary. Returns 0 in case of
|
||||||
|
* failure. Unlike alloc_trash_buffers(), It is unexpected to call this function
|
||||||
|
* multiple times. Small buffers are not used during configuration parsing.
|
||||||
|
*/
|
||||||
|
static int alloc_small_trash_buffers(int bufsize)
|
||||||
|
{
|
||||||
|
small_trash_size = bufsize;
|
||||||
|
if (!small_trash_size)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
BUG_ON(trash_small.area && small_trash_buf1 && small_trash_buf2);
|
||||||
|
|
||||||
|
chunk_init(&trash_small, my_realloc2(trash_small.area, bufsize), bufsize);
|
||||||
|
small_trash_buf1 = (char *)my_realloc2(small_trash_buf1, bufsize);
|
||||||
|
small_trash_buf2 = (char *)my_realloc2(small_trash_buf2, bufsize);
|
||||||
|
return trash_small.area && small_trash_buf1 && small_trash_buf2;
|
||||||
|
}
|
||||||
|
|
||||||
static int alloc_trash_buffers_per_thread()
|
static int alloc_trash_buffers_per_thread()
|
||||||
{
|
{
|
||||||
return alloc_trash_buffers(global.tune.bufsize) && alloc_large_trash_buffers(global.tune.bufsize_large);
|
return (alloc_trash_buffers(global.tune.bufsize) &&
|
||||||
|
alloc_large_trash_buffers(global.tune.bufsize_large) &&
|
||||||
|
alloc_small_trash_buffers(global.tune.bufsize_large));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_trash_buffers_per_thread()
|
static void free_trash_buffers_per_thread()
|
||||||
@ -180,6 +245,10 @@ static void free_trash_buffers_per_thread()
|
|||||||
chunk_destroy(&trash_large);
|
chunk_destroy(&trash_large);
|
||||||
ha_free(&large_trash_buf2);
|
ha_free(&large_trash_buf2);
|
||||||
ha_free(&large_trash_buf1);
|
ha_free(&large_trash_buf1);
|
||||||
|
|
||||||
|
chunk_destroy(&trash_small);
|
||||||
|
ha_free(&small_trash_buf2);
|
||||||
|
ha_free(&small_trash_buf1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the trash buffers. It returns 0 if an error occurred. */
|
/* Initialize the trash buffers. It returns 0 if an error occurred. */
|
||||||
@ -207,6 +276,14 @@ int init_trash_buffers(int first)
|
|||||||
if (!pool_head_large_trash)
|
if (!pool_head_large_trash)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!first && global.tune.bufsize_small) {
|
||||||
|
pool_head_small_trash = create_pool("small_trash",
|
||||||
|
sizeof(struct buffer) + global.tune.bufsize_small,
|
||||||
|
MEM_F_EXACT);
|
||||||
|
if (!pool_head_small_trash)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2670,7 +2670,7 @@ static int _send_status(char **args, char *payload, struct appctx *appctx, void
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* the sockpair between the master and the worker is
|
/* the sockpair between the master and the worker is
|
||||||
* used temporarly as a listener to receive
|
* used temporarily as a listener to receive
|
||||||
* _send_status. Once it is received we don't want to
|
* _send_status. Once it is received we don't want to
|
||||||
* use this FD as a listener anymore, but only as a
|
* use this FD as a listener anymore, but only as a
|
||||||
* server, to allow only connections from the master to
|
* server, to allow only connections from the master to
|
||||||
|
|||||||
@ -182,7 +182,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)) {
|
if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_QSTRM_RECV|CO_FL_QSTRM_SEND))) {
|
||||||
ret = conn_create_mux(conn, NULL);
|
ret = conn_create_mux(conn, NULL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto done;
|
goto done;
|
||||||
@ -412,7 +412,7 @@ int conn_install_mux_chk(struct connection *conn, void *ctx, struct session *ses
|
|||||||
struct ist mux_proto;
|
struct ist mux_proto;
|
||||||
const char *alpn_str = NULL;
|
const char *alpn_str = NULL;
|
||||||
int alpn_len = 0;
|
int alpn_len = 0;
|
||||||
int mode = tcpchk_rules_type_to_proto_mode(check->tcpcheck_rules->flags);
|
int mode = tcpchk_rules_type_to_proto_mode(check->tcpcheck->flags);
|
||||||
|
|
||||||
conn_get_alpn(conn, &alpn_str, &alpn_len);
|
conn_get_alpn(conn, &alpn_str, &alpn_len);
|
||||||
mux_proto = ist2(alpn_str, alpn_len);
|
mux_proto = ist2(alpn_str, alpn_len);
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
struct pool_head *pool_head_buffer __read_mostly;
|
struct pool_head *pool_head_buffer __read_mostly;
|
||||||
struct pool_head *pool_head_large_buffer __read_mostly = NULL;
|
struct pool_head *pool_head_large_buffer __read_mostly = NULL;
|
||||||
|
struct pool_head *pool_head_small_buffer __read_mostly;
|
||||||
|
|
||||||
/* perform minimal initializations, report 0 in case of error, 1 if OK. */
|
/* perform minimal initializations, report 0 in case of error, 1 if OK. */
|
||||||
int init_buffer()
|
int init_buffer()
|
||||||
@ -43,6 +44,12 @@ int init_buffer()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (global.tune.bufsize_small) {
|
||||||
|
pool_head_small_buffer = create_aligned_pool("small_buffer", global.tune.bufsize_small, 64, MEM_F_SHARED|MEM_F_EXACT);
|
||||||
|
if (!pool_head_small_buffer)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* make sure any change to the queues assignment isn't overlooked */
|
/* make sure any change to the queues assignment isn't overlooked */
|
||||||
BUG_ON(DB_PERMANENT - DB_UNLIKELY - 1 != DYNBUF_NBQ);
|
BUG_ON(DB_PERMANENT - DB_UNLIKELY - 1 != DYNBUF_NBQ);
|
||||||
BUG_ON(DB_MUX_RX_Q < DB_SE_RX_Q || DB_MUX_RX_Q >= DYNBUF_NBQ);
|
BUG_ON(DB_MUX_RX_Q < DB_SE_RX_Q || DB_MUX_RX_Q >= DYNBUF_NBQ);
|
||||||
|
|||||||
13
src/ech.c
13
src/ech.c
@ -136,6 +136,10 @@ static int cli_parse_show_ech(char **args, char *payload,
|
|||||||
{
|
{
|
||||||
struct show_ech_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_ech_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
|
||||||
/* no parameter, shows only file list */
|
/* no parameter, shows only file list */
|
||||||
if (*args[3]) {
|
if (*args[3]) {
|
||||||
SSL_CTX *sctx = NULL;
|
SSL_CTX *sctx = NULL;
|
||||||
@ -297,6 +301,9 @@ static int cli_parse_add_ech(char **args, char *payload, struct appctx *appctx,
|
|||||||
OSSL_ECHSTORE *es = NULL;
|
OSSL_ECHSTORE *es = NULL;
|
||||||
BIO *es_in = NULL;
|
BIO *es_in = NULL;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (!*args[3] || !payload)
|
if (!*args[3] || !payload)
|
||||||
return cli_err(appctx, "syntax: add ssl ech <name> <PEM file content>");
|
return cli_err(appctx, "syntax: add ssl ech <name> <PEM file content>");
|
||||||
if (cli_find_ech_specific_ctx(args[3], &sctx) != 1)
|
if (cli_find_ech_specific_ctx(args[3], &sctx) != 1)
|
||||||
@ -324,6 +331,9 @@ static int cli_parse_set_ech(char **args, char *payload, struct appctx *appctx,
|
|||||||
OSSL_ECHSTORE *es = NULL;
|
OSSL_ECHSTORE *es = NULL;
|
||||||
BIO *es_in = NULL;
|
BIO *es_in = NULL;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (!*args[3] || !payload)
|
if (!*args[3] || !payload)
|
||||||
return cli_err(appctx, "syntax: set ssl ech <name> <PEM file content>");
|
return cli_err(appctx, "syntax: set ssl ech <name> <PEM file content>");
|
||||||
if (cli_find_ech_specific_ctx(args[3], &sctx) != 1)
|
if (cli_find_ech_specific_ctx(args[3], &sctx) != 1)
|
||||||
@ -351,6 +361,9 @@ static int cli_parse_del_ech(char **args, char *payload, struct appctx *appctx,
|
|||||||
char success_message[ECH_SUCCESS_MSG_MAX];
|
char success_message[ECH_SUCCESS_MSG_MAX];
|
||||||
OSSL_ECHSTORE *es = NULL;
|
OSSL_ECHSTORE *es = NULL;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (!*args[3])
|
if (!*args[3])
|
||||||
return cli_err(appctx, "syntax: del ssl ech <name>");
|
return cli_err(appctx, "syntax: del ssl ech <name>");
|
||||||
if (*args[4])
|
if (*args[4])
|
||||||
|
|||||||
@ -1114,7 +1114,7 @@ static int spoe_process_event(struct stream *s, struct spoe_context *ctx,
|
|||||||
}
|
}
|
||||||
else if (ret == 0) {
|
else if (ret == 0) {
|
||||||
if ((s->scf->flags & SC_FL_ERROR) ||
|
if ((s->scf->flags & SC_FL_ERROR) ||
|
||||||
((s->scf->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)) && proxy_abrt_close_def(s->be, 1))) {
|
((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(s->be, 1))) {
|
||||||
ctx->status_code = SPOE_CTX_ERR_INTERRUPT;
|
ctx->status_code = SPOE_CTX_ERR_INTERRUPT;
|
||||||
spoe_stop_processing(agent, ctx);
|
spoe_stop_processing(agent, ctx);
|
||||||
spoe_handle_processing_error(s, agent, ctx, dir);
|
spoe_handle_processing_error(s, agent, ctx, dir);
|
||||||
|
|||||||
@ -1121,6 +1121,8 @@ static int read_cfg()
|
|||||||
setenv("HAPROXY_HTTPS_LOG_FMT", default_https_log_format, 1);
|
setenv("HAPROXY_HTTPS_LOG_FMT", default_https_log_format, 1);
|
||||||
setenv("HAPROXY_TCP_LOG_FMT", default_tcp_log_format, 1);
|
setenv("HAPROXY_TCP_LOG_FMT", default_tcp_log_format, 1);
|
||||||
setenv("HAPROXY_TCP_CLF_LOG_FMT", clf_tcp_log_format, 1);
|
setenv("HAPROXY_TCP_CLF_LOG_FMT", clf_tcp_log_format, 1);
|
||||||
|
setenv("HAPROXY_KEYLOG_FC_LOG_FMT", keylog_format_fc, 1);
|
||||||
|
setenv("HAPROXY_KEYLOG_BC_LOG_FMT", keylog_format_bc, 1);
|
||||||
setenv("HAPROXY_BRANCH", PRODUCT_BRANCH, 1);
|
setenv("HAPROXY_BRANCH", PRODUCT_BRANCH, 1);
|
||||||
list_for_each_entry(cfg, &cfg_cfgfiles, list) {
|
list_for_each_entry(cfg, &cfg_cfgfiles, list) {
|
||||||
int ret;
|
int ret;
|
||||||
@ -2822,7 +2824,7 @@ void deinit(void)
|
|||||||
* they are respectively cleaned up in sink_deinit() and deinit_log_forward()
|
* they are respectively cleaned up in sink_deinit() and deinit_log_forward()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* If named defaults were preserved, ensure <def_ref> count is resetted. */
|
/* If named defaults were preserved, ensure <def_ref> count is reset. */
|
||||||
if (!(global.tune.options & GTUNE_PURGE_DEFAULTS))
|
if (!(global.tune.options & GTUNE_PURGE_DEFAULTS))
|
||||||
defaults_px_unref_all();
|
defaults_px_unref_all();
|
||||||
|
|
||||||
|
|||||||
12
src/haterm.c
12
src/haterm.c
@ -47,7 +47,7 @@ const char *HTTP_HELP =
|
|||||||
" - /?R=<enable> Enable sending random data if >0.\n"
|
" - /?R=<enable> Enable sending random data if >0.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Note that those arguments may be cumulated on one line separated by a set of\n"
|
"Note that those arguments may be cumulated on one line separated by a set of\n"
|
||||||
"delimitors among [&?,;/] :\n"
|
"delimiters among [&?,;/] :\n"
|
||||||
" - GET /?s=20k&c=1&t=700&K=30r HTTP/1.0\n"
|
" - GET /?s=20k&c=1&t=700&K=30r HTTP/1.0\n"
|
||||||
" - GET /?r=500?s=0?c=0?t=1000 HTTP/1.0\n"
|
" - GET /?r=500?s=0?c=0?t=1000 HTTP/1.0\n"
|
||||||
"\n";
|
"\n";
|
||||||
@ -323,7 +323,7 @@ static int hstream_htx_buf_snd(struct connection *conn, struct hstream *hs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* The HTX data are not fully sent if the last HTX data
|
/* The HTX data are not fully sent if the last HTX data
|
||||||
* were not fully transfered or if there are remaining data
|
* were not fully transferred or if there are remaining data
|
||||||
* to send (->to_write > 0).
|
* to send (->to_write > 0).
|
||||||
*/
|
*/
|
||||||
if (!htx_is_empty(htxbuf(&hs->res))) {
|
if (!htx_is_empty(htxbuf(&hs->res))) {
|
||||||
@ -787,6 +787,10 @@ static struct task *process_hstream(struct task *t, void *context, unsigned int
|
|||||||
struct htx_sl *sl = http_get_stline(htx);
|
struct htx_sl *sl = http_get_stline(htx);
|
||||||
struct http_hdr_ctx expect, clength;
|
struct http_hdr_ctx expect, clength;
|
||||||
|
|
||||||
|
/* we're starting to work with this endpoint, let's flag it */
|
||||||
|
if (unlikely(!sc_ep_test(hs->sc, SE_FL_APP_STARTED)))
|
||||||
|
sc_ep_set(hs->sc, SE_FL_APP_STARTED);
|
||||||
|
|
||||||
if (sl->flags & HTX_SL_F_VER_11)
|
if (sl->flags & HTX_SL_F_VER_11)
|
||||||
hs->ka = 5;
|
hs->ka = 5;
|
||||||
|
|
||||||
@ -1025,7 +1029,7 @@ static int hstream_build_responses(void)
|
|||||||
|
|
||||||
/* original haterm chunk mode responses are made of 1-byte chunks
|
/* original haterm chunk mode responses are made of 1-byte chunks
|
||||||
* but the haproxy muxes do not support this. At this time
|
* but the haproxy muxes do not support this. At this time
|
||||||
* these reponses are handled the same way as for common
|
* these responses are handled the same way as for common
|
||||||
* responses with a pre-built buffer.
|
* responses with a pre-built buffer.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < sizeof(common_chunk_resp); i++)
|
for (i = 0; i < sizeof(common_chunk_resp); i++)
|
||||||
@ -1033,7 +1037,7 @@ static int hstream_build_responses(void)
|
|||||||
|
|
||||||
random_resp = malloc(random_resp_len);
|
random_resp = malloc(random_resp_len);
|
||||||
if (!random_resp) {
|
if (!random_resp) {
|
||||||
ha_alert("not enough memore...\n");
|
ha_alert("not enough memory...\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -106,7 +106,7 @@ static inline size_t hbuf_is_null(const struct hbuf *h)
|
|||||||
|
|
||||||
/* Simple function, to append <line> to <b> without without
|
/* Simple function, to append <line> to <b> without without
|
||||||
* trailing '\0' character.
|
* trailing '\0' character.
|
||||||
* Take into an account the '\t' and '\n' escaped sequeces.
|
* Take into an account the '\t' and '\n' escaped sequences.
|
||||||
*/
|
*/
|
||||||
static void hstream_str_buf_append(struct hbuf *h, const char *line)
|
static void hstream_str_buf_append(struct hbuf *h, const char *line)
|
||||||
{
|
{
|
||||||
@ -215,7 +215,7 @@ void haproxy_init_args(int argc, char **argv)
|
|||||||
argc--; argv++;
|
argc--; argv++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restore the argumenst */
|
/* Restore the arguments */
|
||||||
argc = sargc; argv = sargv;
|
argc = sargc; argv = sargv;
|
||||||
while (argc > 0) {
|
while (argc > 0) {
|
||||||
char *opt;
|
char *opt;
|
||||||
|
|||||||
309
src/http_act.c
309
src/http_act.c
@ -20,6 +20,7 @@
|
|||||||
#include <haproxy/action.h>
|
#include <haproxy/action.h>
|
||||||
#include <haproxy/api.h>
|
#include <haproxy/api.h>
|
||||||
#include <haproxy/arg.h>
|
#include <haproxy/arg.h>
|
||||||
|
#include <haproxy/base64.h>
|
||||||
#include <haproxy/capture-t.h>
|
#include <haproxy/capture-t.h>
|
||||||
#include <haproxy/cfgparse.h>
|
#include <haproxy/cfgparse.h>
|
||||||
#include <haproxy/chunk.h>
|
#include <haproxy/chunk.h>
|
||||||
@ -50,6 +51,7 @@ static void release_http_action(struct act_rule *rule)
|
|||||||
if (rule->arg.http.re)
|
if (rule->arg.http.re)
|
||||||
regex_free(rule->arg.http.re);
|
regex_free(rule->arg.http.re);
|
||||||
lf_expr_deinit(&rule->arg.http.fmt);
|
lf_expr_deinit(&rule->arg.http.fmt);
|
||||||
|
release_sample_expr(rule->arg.http.expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Release memory allocated by HTTP actions relying on an http reply. Concretely,
|
/* Release memory allocated by HTTP actions relying on an http reply. Concretely,
|
||||||
@ -1479,6 +1481,86 @@ static enum act_return http_action_set_header(struct act_rule *rule, struct prox
|
|||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function executes a set-headers-bin or add-headers-bin actions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static enum act_return http_action_set_headers_bin(struct act_rule *rule, struct proxy *px,
|
||||||
|
struct session *sess, struct stream *s, int flags)
|
||||||
|
{
|
||||||
|
struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
|
||||||
|
struct htx *htx = htxbuf(&msg->chn->buf);
|
||||||
|
struct sample *hdrs_bin;
|
||||||
|
char *p, *end;
|
||||||
|
enum act_return ret = ACT_RET_CONT;
|
||||||
|
struct http_hdr_ctx ctx;
|
||||||
|
struct ist n, v;
|
||||||
|
uint64_t sz = 0;
|
||||||
|
|
||||||
|
hdrs_bin = sample_fetch_as_type(px, sess, s, SMP_OPT_FINAL, rule->arg.http.expr, SMP_T_BIN);
|
||||||
|
if (!hdrs_bin)
|
||||||
|
return ACT_RET_CONT;
|
||||||
|
|
||||||
|
p = b_orig(&hdrs_bin->data.u.str);
|
||||||
|
end = b_tail(&hdrs_bin->data.u.str);
|
||||||
|
while (p < end) {
|
||||||
|
if (decode_varint(&p, end, &sz) == -1)
|
||||||
|
goto fail_rewrite;
|
||||||
|
if (!sz) {
|
||||||
|
if (decode_varint(&p, end, &sz) == -1 || sz > 0)
|
||||||
|
goto fail_rewrite;
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = ist2(p, sz);
|
||||||
|
p += sz;
|
||||||
|
|
||||||
|
if (decode_varint(&p, end, &sz) == -1)
|
||||||
|
goto fail_rewrite;
|
||||||
|
|
||||||
|
v = ist2(p, sz);
|
||||||
|
p += sz;
|
||||||
|
|
||||||
|
if (istlen(rule->arg.http.str) && !istmatch(n, rule->arg.http.str))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (is_immutable_header(n))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (rule->action == 0) { // set-header
|
||||||
|
/* remove all occurrences of the header */
|
||||||
|
ctx.blk = NULL;
|
||||||
|
while (http_find_header(htx, n, &ctx, 1))
|
||||||
|
http_remove_header(htx, &ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now add header */
|
||||||
|
if (!http_add_header(htx, n, v))
|
||||||
|
goto fail_rewrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* invalid encoding */
|
||||||
|
ret = ACT_RET_ERR;
|
||||||
|
|
||||||
|
leave:
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
fail_rewrite:
|
||||||
|
if (sess->fe_tgcounters)
|
||||||
|
_HA_ATOMIC_INC(&sess->fe_tgcounters->failed_rewrites);
|
||||||
|
if ((s->flags & SF_BE_ASSIGNED) && s->be_tgcounters)
|
||||||
|
_HA_ATOMIC_INC(&s->be_tgcounters->failed_rewrites);
|
||||||
|
if (sess->li_tgcounters)
|
||||||
|
_HA_ATOMIC_INC(&sess->li_tgcounters->failed_rewrites);
|
||||||
|
if (s->sv_tgcounters)
|
||||||
|
_HA_ATOMIC_INC(&s->sv_tgcounters->failed_rewrites);
|
||||||
|
|
||||||
|
if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
|
||||||
|
ret = ACT_RET_ERR;
|
||||||
|
if (!(s->flags & SF_ERR_MASK))
|
||||||
|
s->flags |= SF_ERR_PRXCOND;
|
||||||
|
}
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
|
/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
|
||||||
* header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
|
* header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
|
||||||
* on success, ACT_RET_PRS_ERR on error.
|
* on success, ACT_RET_PRS_ERR on error.
|
||||||
@ -1557,6 +1639,64 @@ static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg
|
|||||||
return ACT_RET_PRS_OK;
|
return ACT_RET_PRS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parse set-headers-bin */
|
||||||
|
static enum act_parse_ret parse_http_set_headers_bin(const char **args, int *orig_arg, struct proxy *px,
|
||||||
|
struct act_rule *rule, char **err)
|
||||||
|
{
|
||||||
|
struct sample_expr *expr;
|
||||||
|
unsigned int where;
|
||||||
|
int cur_arg;
|
||||||
|
|
||||||
|
if (args[*orig_arg-1][0] == 's')
|
||||||
|
rule->action = 0; // set-header
|
||||||
|
else
|
||||||
|
rule->action = 1; // add-header
|
||||||
|
rule->action_ptr = http_action_set_headers_bin;
|
||||||
|
rule->release_ptr = release_http_action;
|
||||||
|
lf_expr_init(&rule->arg.http.fmt);
|
||||||
|
|
||||||
|
cur_arg = *orig_arg;
|
||||||
|
if (!*args[cur_arg]) {
|
||||||
|
memprintf(err, "expects exactly one argument or three arguments <headers> prefix <pfx>");
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
|
||||||
|
err, &px->conf.args, NULL);
|
||||||
|
if (!expr)
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
|
||||||
|
where = 0;
|
||||||
|
if (px->cap & PR_CAP_FE)
|
||||||
|
where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
|
||||||
|
if (px->cap & PR_CAP_BE)
|
||||||
|
where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
|
||||||
|
|
||||||
|
if (!(expr->fetch->val & where)) {
|
||||||
|
memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
|
||||||
|
args[cur_arg-1], sample_src_names(expr->fetch->use));
|
||||||
|
release_sample_expr(expr);
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if an argument is available */
|
||||||
|
if (strcmp(args[cur_arg], "prefix") == 0 ) {
|
||||||
|
cur_arg++;
|
||||||
|
if(!*args[cur_arg]) {
|
||||||
|
memprintf(err, "expects 1 argument: <headers>; or 3 arguments: <headers> prefix <pfx>");
|
||||||
|
release_sample_expr(expr);
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
}
|
||||||
|
rule->arg.http.str = ist(strdup(args[cur_arg]));
|
||||||
|
cur_arg++;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule->arg.http.expr = expr;
|
||||||
|
|
||||||
|
*orig_arg = cur_arg;
|
||||||
|
return ACT_RET_PRS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/* This function executes a replace-header or replace-value actions. It
|
/* This function executes a replace-header or replace-value actions. It
|
||||||
* builds a string in the trash from the specified format string. It finds
|
* builds a string in the trash from the specified format string. It finds
|
||||||
* the action to be performed in <.action>, previously filled by function
|
* the action to be performed in <.action>, previously filled by function
|
||||||
@ -1767,6 +1907,166 @@ static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg
|
|||||||
return ACT_RET_PRS_OK;
|
return ACT_RET_PRS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function executes a del-headers-bin action with selected matching mode for
|
||||||
|
* header name. It finds the matching method to be performed in <.action>, previously
|
||||||
|
* filled by function parse_http_del_headers_bin(). On success, it returns ACT_RET_CONT.
|
||||||
|
* Otherwise ACT_RET_ERR is returned.
|
||||||
|
*/
|
||||||
|
static enum act_return http_action_del_headers_bin(struct act_rule *rule, struct proxy *px,
|
||||||
|
struct session *sess, struct stream *s, int flags)
|
||||||
|
{
|
||||||
|
struct http_hdr_ctx ctx;
|
||||||
|
struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
|
||||||
|
struct htx *htx = htxbuf(&msg->chn->buf);
|
||||||
|
struct sample *hdrs_bin;
|
||||||
|
char *p, *end;
|
||||||
|
enum act_return ret = ACT_RET_CONT;
|
||||||
|
struct ist n;
|
||||||
|
uint64_t sz = 0;
|
||||||
|
|
||||||
|
hdrs_bin = sample_fetch_as_type(px, sess, s, SMP_OPT_FINAL, rule->arg.http.expr, SMP_T_BIN);
|
||||||
|
if (!hdrs_bin)
|
||||||
|
return ACT_RET_CONT;
|
||||||
|
|
||||||
|
p = b_orig(&hdrs_bin->data.u.str);
|
||||||
|
end = b_tail(&hdrs_bin->data.u.str);
|
||||||
|
while (p < end) {
|
||||||
|
if (decode_varint(&p, end, &sz) == -1)
|
||||||
|
goto fail_rewrite;
|
||||||
|
if (!sz)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
n = ist2(p, sz);
|
||||||
|
p += sz;
|
||||||
|
|
||||||
|
if (is_immutable_header(n))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* remove all occurrences of the header */
|
||||||
|
ctx.blk = NULL;
|
||||||
|
switch (rule->action) {
|
||||||
|
case PAT_MATCH_STR:
|
||||||
|
while (http_find_header(htx, n, &ctx, 1))
|
||||||
|
http_remove_header(htx, &ctx);
|
||||||
|
break;
|
||||||
|
case PAT_MATCH_BEG:
|
||||||
|
while (http_find_pfx_header(htx, n, &ctx, 1))
|
||||||
|
http_remove_header(htx, &ctx);
|
||||||
|
break;
|
||||||
|
case PAT_MATCH_END:
|
||||||
|
while (http_find_sfx_header(htx, n, &ctx, 1))
|
||||||
|
http_remove_header(htx, &ctx);
|
||||||
|
break;
|
||||||
|
case PAT_MATCH_SUB:
|
||||||
|
while (http_find_sub_header(htx, n, &ctx, 1))
|
||||||
|
http_remove_header(htx, &ctx);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto fail_rewrite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* invalid encoding */
|
||||||
|
ret = ACT_RET_ERR;
|
||||||
|
|
||||||
|
leave:
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
fail_rewrite:
|
||||||
|
if (sess->fe_tgcounters)
|
||||||
|
_HA_ATOMIC_INC(&sess->fe_tgcounters->failed_rewrites);
|
||||||
|
if ((s->flags & SF_BE_ASSIGNED) && s->be_tgcounters)
|
||||||
|
_HA_ATOMIC_INC(&s->be_tgcounters->failed_rewrites);
|
||||||
|
if (sess->li_tgcounters)
|
||||||
|
_HA_ATOMIC_INC(&sess->li_tgcounters->failed_rewrites);
|
||||||
|
if (s->sv_tgcounters)
|
||||||
|
_HA_ATOMIC_INC(&s->sv_tgcounters->failed_rewrites);
|
||||||
|
|
||||||
|
if (!(msg->flags & HTTP_MSGF_SOFT_RW)) {
|
||||||
|
ret = ACT_RET_ERR;
|
||||||
|
if (!(s->flags & SF_ERR_MASK))
|
||||||
|
s->flags |= SF_ERR_PRXCOND;
|
||||||
|
}
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a "del-headers-bin" action. It takes string as a required argument,
|
||||||
|
* optional flag (currently only -m) and optional matching method of input string
|
||||||
|
* with header name to be deleted. Default matching method is exact match (-m str).
|
||||||
|
* It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
|
||||||
|
*/
|
||||||
|
static enum act_parse_ret parse_http_del_headers_bin(const char **args, int *orig_arg, struct proxy *px,
|
||||||
|
struct act_rule *rule, char **err)
|
||||||
|
{
|
||||||
|
struct sample_expr *expr;
|
||||||
|
unsigned int where;
|
||||||
|
int cur_arg;
|
||||||
|
int pat_idx;
|
||||||
|
|
||||||
|
/* set exact matching (-m str) as default */
|
||||||
|
rule->action = PAT_MATCH_STR;
|
||||||
|
rule->action_ptr = http_action_del_headers_bin;
|
||||||
|
rule->release_ptr = release_http_action;
|
||||||
|
lf_expr_init(&rule->arg.http.fmt);
|
||||||
|
|
||||||
|
cur_arg = *orig_arg;
|
||||||
|
if (!*args[cur_arg]) {
|
||||||
|
memprintf(err, "expects at least 1 argument");
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
|
||||||
|
err, &px->conf.args, NULL);
|
||||||
|
if (!expr)
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
|
||||||
|
where = 0;
|
||||||
|
if (px->cap & PR_CAP_FE)
|
||||||
|
where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
|
||||||
|
if (px->cap & PR_CAP_BE)
|
||||||
|
where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
|
||||||
|
|
||||||
|
if (!(expr->fetch->val & where)) {
|
||||||
|
memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
|
||||||
|
args[cur_arg-1], sample_src_names(expr->fetch->use));
|
||||||
|
release_sample_expr(expr);
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(args[cur_arg], "-m") == 0) {
|
||||||
|
cur_arg++;
|
||||||
|
if (!*args[cur_arg]) {
|
||||||
|
memprintf(err, "-m flag expects exactly 1 argument");
|
||||||
|
release_sample_expr(expr);
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
pat_idx = pat_find_match_name(args[cur_arg]);
|
||||||
|
switch (pat_idx) {
|
||||||
|
case PAT_MATCH_REG:
|
||||||
|
memprintf(err, "-m reg with is unsupported with del-header-bin due to performance reasons");
|
||||||
|
release_sample_expr(expr);
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
case PAT_MATCH_STR:
|
||||||
|
case PAT_MATCH_BEG:
|
||||||
|
case PAT_MATCH_END:
|
||||||
|
case PAT_MATCH_SUB:
|
||||||
|
rule->action = pat_idx;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
memprintf(err, "-m with unsupported matching method '%s'", args[cur_arg]);
|
||||||
|
release_sample_expr(expr);
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
}
|
||||||
|
cur_arg++;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule->arg.http.expr = expr;
|
||||||
|
|
||||||
|
*orig_arg = cur_arg;
|
||||||
|
return ACT_RET_PRS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/* This function executes a pause action.
|
/* This function executes a pause action.
|
||||||
*/
|
*/
|
||||||
static enum act_return http_action_pause(struct act_rule *rule, struct proxy *px,
|
static enum act_return http_action_pause(struct act_rule *rule, struct proxy *px,
|
||||||
@ -2530,11 +2830,13 @@ static struct action_kw_list http_req_actions = {
|
|||||||
.kw = {
|
.kw = {
|
||||||
{ "add-acl", parse_http_set_map, KWF_MATCH_PREFIX },
|
{ "add-acl", parse_http_set_map, KWF_MATCH_PREFIX },
|
||||||
{ "add-header", parse_http_set_header, 0 },
|
{ "add-header", parse_http_set_header, 0 },
|
||||||
|
{ "add-headers-bin", parse_http_set_headers_bin, 0 },
|
||||||
{ "allow", parse_http_allow, 0 },
|
{ "allow", parse_http_allow, 0 },
|
||||||
{ "auth", parse_http_auth, 0 },
|
{ "auth", parse_http_auth, 0 },
|
||||||
{ "capture", parse_http_req_capture, 0 },
|
{ "capture", parse_http_req_capture, 0 },
|
||||||
{ "del-acl", parse_http_set_map, KWF_MATCH_PREFIX },
|
{ "del-acl", parse_http_set_map, KWF_MATCH_PREFIX },
|
||||||
{ "del-header", parse_http_del_header, 0 },
|
{ "del-header", parse_http_del_header, 0 },
|
||||||
|
{ "del-headers-bin", parse_http_del_headers_bin, 0 },
|
||||||
{ "del-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
{ "del-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
||||||
{ "deny", parse_http_deny, 0 },
|
{ "deny", parse_http_deny, 0 },
|
||||||
{ "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
|
{ "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
|
||||||
@ -2551,6 +2853,7 @@ static struct action_kw_list http_req_actions = {
|
|||||||
{ "replace-value", parse_http_replace_header, 0 },
|
{ "replace-value", parse_http_replace_header, 0 },
|
||||||
{ "return", parse_http_return, 0 },
|
{ "return", parse_http_return, 0 },
|
||||||
{ "set-header", parse_http_set_header, 0 },
|
{ "set-header", parse_http_set_header, 0 },
|
||||||
|
{ "set-headers-bin", parse_http_set_headers_bin, 0 },
|
||||||
{ "set-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
{ "set-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
||||||
{ "set-method", parse_set_req_line, 0 },
|
{ "set-method", parse_set_req_line, 0 },
|
||||||
{ "set-path", parse_set_req_line, 0 },
|
{ "set-path", parse_set_req_line, 0 },
|
||||||
@ -2572,10 +2875,12 @@ static struct action_kw_list http_res_actions = {
|
|||||||
.kw = {
|
.kw = {
|
||||||
{ "add-acl", parse_http_set_map, KWF_MATCH_PREFIX },
|
{ "add-acl", parse_http_set_map, KWF_MATCH_PREFIX },
|
||||||
{ "add-header", parse_http_set_header, 0 },
|
{ "add-header", parse_http_set_header, 0 },
|
||||||
|
{ "add-headers-bin", parse_http_set_headers_bin,0 },
|
||||||
{ "allow", parse_http_allow, 0 },
|
{ "allow", parse_http_allow, 0 },
|
||||||
{ "capture", parse_http_res_capture, 0 },
|
{ "capture", parse_http_res_capture, 0 },
|
||||||
{ "del-acl", parse_http_set_map, KWF_MATCH_PREFIX },
|
{ "del-acl", parse_http_set_map, KWF_MATCH_PREFIX },
|
||||||
{ "del-header", parse_http_del_header, 0 },
|
{ "del-header", parse_http_del_header, 0 },
|
||||||
|
{ "del-headers-bin", parse_http_del_headers_bin,0 },
|
||||||
{ "del-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
{ "del-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
||||||
{ "deny", parse_http_deny, 0 },
|
{ "deny", parse_http_deny, 0 },
|
||||||
{ "do-log", parse_http_res_do_log, 0 },
|
{ "do-log", parse_http_res_do_log, 0 },
|
||||||
@ -2585,6 +2890,7 @@ static struct action_kw_list http_res_actions = {
|
|||||||
{ "replace-value", parse_http_replace_header, 0 },
|
{ "replace-value", parse_http_replace_header, 0 },
|
||||||
{ "return", parse_http_return, 0 },
|
{ "return", parse_http_return, 0 },
|
||||||
{ "set-header", parse_http_set_header, 0 },
|
{ "set-header", parse_http_set_header, 0 },
|
||||||
|
{ "set-headers-bin", parse_http_set_headers_bin,0 },
|
||||||
{ "set-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
{ "set-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
||||||
{ "set-status", parse_http_set_status, 0 },
|
{ "set-status", parse_http_set_status, 0 },
|
||||||
{ "strict-mode", parse_http_strict_mode, 0 },
|
{ "strict-mode", parse_http_strict_mode, 0 },
|
||||||
@ -2600,15 +2906,18 @@ INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
|
|||||||
static struct action_kw_list http_after_res_actions = {
|
static struct action_kw_list http_after_res_actions = {
|
||||||
.kw = {
|
.kw = {
|
||||||
{ "add-header", parse_http_set_header, 0 },
|
{ "add-header", parse_http_set_header, 0 },
|
||||||
|
{ "add-headers-bin", parse_http_set_headers_bin,0 },
|
||||||
{ "allow", parse_http_allow, 0 },
|
{ "allow", parse_http_allow, 0 },
|
||||||
{ "capture", parse_http_res_capture, 0 },
|
{ "capture", parse_http_res_capture, 0 },
|
||||||
{ "del-acl", parse_http_set_map, KWF_MATCH_PREFIX },
|
{ "del-acl", parse_http_set_map, KWF_MATCH_PREFIX },
|
||||||
{ "del-header", parse_http_del_header, 0 },
|
{ "del-header", parse_http_del_header, 0 },
|
||||||
|
{ "del-headers-bin", parse_http_del_headers_bin,0 },
|
||||||
{ "del-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
{ "del-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
||||||
{ "do-log", parse_http_after_res_do_log, 0 },
|
{ "do-log", parse_http_after_res_do_log, 0 },
|
||||||
{ "replace-header", parse_http_replace_header, 0 },
|
{ "replace-header", parse_http_replace_header, 0 },
|
||||||
{ "replace-value", parse_http_replace_header, 0 },
|
{ "replace-value", parse_http_replace_header, 0 },
|
||||||
{ "set-header", parse_http_set_header, 0 },
|
{ "set-header", parse_http_set_header, 0 },
|
||||||
|
{ "set-headers-bin", parse_http_set_headers_bin,0 },
|
||||||
{ "set-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
{ "set-map", parse_http_set_map, KWF_MATCH_PREFIX },
|
||||||
{ "set-status", parse_http_set_status, 0 },
|
{ "set-status", parse_http_set_status, 0 },
|
||||||
{ "strict-mode", parse_http_strict_mode, 0 },
|
{ "strict-mode", parse_http_strict_mode, 0 },
|
||||||
|
|||||||
@ -2828,8 +2828,7 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis
|
|||||||
int act_opts = 0;
|
int act_opts = 0;
|
||||||
|
|
||||||
if ((s->scf->flags & SC_FL_ERROR) ||
|
if ((s->scf->flags & SC_FL_ERROR) ||
|
||||||
((s->scf->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)) &&
|
((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(px, 1)))
|
||||||
proxy_abrt_close_def(px, 1)))
|
|
||||||
act_opts |= ACT_OPT_FINAL | ACT_OPT_FINAL_EARLY;
|
act_opts |= ACT_OPT_FINAL | ACT_OPT_FINAL_EARLY;
|
||||||
|
|
||||||
/* If "the current_rule_list" match the executed rule list, we are in
|
/* If "the current_rule_list" match the executed rule list, we are in
|
||||||
@ -3020,8 +3019,7 @@ static enum rule_result http_res_get_intercept_rule(struct proxy *px, struct lis
|
|||||||
if (final)
|
if (final)
|
||||||
act_opts |= ACT_OPT_FINAL;
|
act_opts |= ACT_OPT_FINAL;
|
||||||
if ((s->scf->flags & SC_FL_ERROR) ||
|
if ((s->scf->flags & SC_FL_ERROR) ||
|
||||||
((s->scf->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)) &&
|
((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(px, 1)))
|
||||||
proxy_abrt_close_def(px, 1)))
|
|
||||||
act_opts |= ACT_OPT_FINAL | ACT_OPT_FINAL_EARLY;
|
act_opts |= ACT_OPT_FINAL | ACT_OPT_FINAL_EARLY;
|
||||||
|
|
||||||
/* If "the current_rule_list" match the executed rule list, we are in
|
/* If "the current_rule_list" match the executed rule list, we are in
|
||||||
@ -4337,20 +4335,10 @@ enum rule_result http_wait_for_msg_body(struct stream *s, struct channel *chn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (channel_htx_full(chn, htx, global.tune.maxrewrite) || sc_waiting_room(chn_prod(chn))) {
|
if (channel_htx_full(chn, htx, global.tune.maxrewrite) || sc_waiting_room(chn_prod(chn))) {
|
||||||
struct buffer lbuf;
|
struct buffer lbuf = BUF_NULL;
|
||||||
char *area;
|
|
||||||
|
|
||||||
if (large_buffer == 0 || b_is_large(&chn->buf))
|
if (large_buffer == 0 || b_is_large(&chn->buf) || !htx_move_to_large_buffer(&lbuf, &chn->buf))
|
||||||
goto end; /* don't use large buffer or large buffer is full */
|
goto end; /* don't use large buffer or already a large buffer */
|
||||||
|
|
||||||
/* normal buffer is full, allocate a large one
|
|
||||||
*/
|
|
||||||
area = pool_alloc(pool_head_large_buffer);
|
|
||||||
if (!area)
|
|
||||||
goto end; /* Allocation failure: TODO must be improved to use buffer_wait */
|
|
||||||
lbuf = b_make(area, global.tune.bufsize_large, 0, 0);
|
|
||||||
htx_xfer_blks(htx_from_buf(&lbuf), htx, htx_used_space(htx), HTX_BLK_UNUSED);
|
|
||||||
htx_to_buf(htx, &chn->buf);
|
|
||||||
b_free(&chn->buf);
|
b_free(&chn->buf);
|
||||||
offer_buffers(s, 1);
|
offer_buffers(s, 1);
|
||||||
chn->buf = lbuf;
|
chn->buf = lbuf;
|
||||||
@ -4366,8 +4354,7 @@ enum rule_result http_wait_for_msg_body(struct stream *s, struct channel *chn,
|
|||||||
/* we get here if we need to wait for more data */
|
/* we get here if we need to wait for more data */
|
||||||
|
|
||||||
if ((s->scf->flags & SC_FL_ERROR) ||
|
if ((s->scf->flags & SC_FL_ERROR) ||
|
||||||
((s->scf->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)) &&
|
((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(s->be, 1)))
|
||||||
proxy_abrt_close_def(s->be, 1)))
|
|
||||||
ret = HTTP_RULE_RES_CONT;
|
ret = HTTP_RULE_RES_CONT;
|
||||||
else if (!(chn_prod(chn)->flags & (SC_FL_ERROR|SC_FL_EOS|SC_FL_ABRT_DONE))) {
|
else if (!(chn_prod(chn)->flags & (SC_FL_ERROR|SC_FL_EOS|SC_FL_ABRT_DONE))) {
|
||||||
if (!tick_isset(chn->analyse_exp))
|
if (!tick_isset(chn->analyse_exp))
|
||||||
@ -4747,7 +4734,7 @@ int http_forward_proxy_resp(struct stream *s, int final)
|
|||||||
if (s->txn->meth == HTTP_METH_HEAD)
|
if (s->txn->meth == HTTP_METH_HEAD)
|
||||||
htx_skip_msg_payload(htx);
|
htx_skip_msg_payload(htx);
|
||||||
|
|
||||||
/* Respnse from haproxy, override HTTP response verison using the request one */
|
/* Response from haproxy, override HTTP response version using the request one */
|
||||||
s->txn->rsp.vsn = s->txn->req.vsn;
|
s->txn->rsp.vsn = s->txn->req.vsn;
|
||||||
|
|
||||||
channel_auto_read(req);
|
channel_auto_read(req);
|
||||||
|
|||||||
@ -604,10 +604,7 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
|||||||
htx_to_buf(htx, outbuf);
|
htx_to_buf(htx, outbuf);
|
||||||
b_xfer(outbuf, &hc->req.buf, b_data(&hc->req.buf));
|
b_xfer(outbuf, &hc->req.buf, b_data(&hc->req.buf));
|
||||||
} else {
|
} else {
|
||||||
struct htx_ret ret;
|
if (!htx_xfer(htx, hc_htx, htx_used_space(hc_htx), HTX_XFER_DEFAULT)) {
|
||||||
|
|
||||||
ret = htx_xfer_blks(htx, hc_htx, htx_used_space(hc_htx), HTX_BLK_UNUSED);
|
|
||||||
if (!ret.ret) {
|
|
||||||
applet_have_more_data(appctx);
|
applet_have_more_data(appctx);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -711,7 +708,6 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
|||||||
if (hc->options & HTTPCLIENT_O_RES_HTX) {
|
if (hc->options & HTTPCLIENT_O_RES_HTX) {
|
||||||
/* HTX mode transfers the header to the hc buffer */
|
/* HTX mode transfers the header to the hc buffer */
|
||||||
struct htx *hc_htx;
|
struct htx *hc_htx;
|
||||||
struct htx_ret ret;
|
|
||||||
|
|
||||||
if (!b_alloc(&hc->res.buf, DB_MUX_TX)) {
|
if (!b_alloc(&hc->res.buf, DB_MUX_TX)) {
|
||||||
applet_wont_consume(appctx);
|
applet_wont_consume(appctx);
|
||||||
@ -720,8 +716,7 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
|||||||
hc_htx = htxbuf(&hc->res.buf);
|
hc_htx = htxbuf(&hc->res.buf);
|
||||||
|
|
||||||
/* xfer the headers */
|
/* xfer the headers */
|
||||||
ret = htx_xfer_blks(hc_htx, htx, htx_used_space(htx), HTX_BLK_EOH);
|
if (!htx_xfer(hc_htx, htx, htx_used_space(htx), HTX_XFER_HDRS_ONLY)) {
|
||||||
if (!ret.ret) {
|
|
||||||
applet_need_more_data(appctx);
|
applet_need_more_data(appctx);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -811,12 +806,10 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
|||||||
if (hc->options & HTTPCLIENT_O_RES_HTX) {
|
if (hc->options & HTTPCLIENT_O_RES_HTX) {
|
||||||
/* HTX mode transfers the header to the hc buffer */
|
/* HTX mode transfers the header to the hc buffer */
|
||||||
struct htx *hc_htx;
|
struct htx *hc_htx;
|
||||||
struct htx_ret ret;
|
|
||||||
|
|
||||||
hc_htx = htxbuf(&hc->res.buf);
|
hc_htx = htxbuf(&hc->res.buf);
|
||||||
|
|
||||||
ret = htx_xfer_blks(hc_htx, htx, htx_used_space(htx), HTX_BLK_UNUSED);
|
if (!htx_xfer(hc_htx, htx, htx_used_space(htx), HTX_XFER_DEFAULT))
|
||||||
if (!ret.ret)
|
|
||||||
applet_wont_consume(appctx);
|
applet_wont_consume(appctx);
|
||||||
else
|
else
|
||||||
applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
|
applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
|
||||||
|
|||||||
@ -44,7 +44,7 @@
|
|||||||
/* this struct is used between calls to smp_fetch_hdr() or smp_fetch_cookie() */
|
/* this struct is used between calls to smp_fetch_hdr() or smp_fetch_cookie() */
|
||||||
static THREAD_LOCAL struct http_hdr_ctx static_http_hdr_ctx;
|
static THREAD_LOCAL struct http_hdr_ctx static_http_hdr_ctx;
|
||||||
/* this is used to convert raw connection buffers to htx */
|
/* this is used to convert raw connection buffers to htx */
|
||||||
/* NOTE: For now, raw bufers cannot exceeds the standard size */
|
/* NOTE: For now, raw buffers cannot exceeds the standard size */
|
||||||
static THREAD_LOCAL struct buffer static_raw_htx_chunk;
|
static THREAD_LOCAL struct buffer static_raw_htx_chunk;
|
||||||
static THREAD_LOCAL char *static_raw_htx_buf;
|
static THREAD_LOCAL char *static_raw_htx_buf;
|
||||||
|
|
||||||
|
|||||||
171
src/http_htx.c
171
src/http_htx.c
@ -41,17 +41,18 @@ struct list http_replies_list = LIST_HEAD_INIT(http_replies_list);
|
|||||||
/* The declaration of an errorfiles/errorfile directives. Used during config
|
/* The declaration of an errorfiles/errorfile directives. Used during config
|
||||||
* parsing only. */
|
* parsing only. */
|
||||||
struct conf_errors {
|
struct conf_errors {
|
||||||
char type; /* directive type (0: errorfiles, 1: errorfile) */
|
enum http_err_directive directive; /* directive type: inline (errorfile <code> <file>) / section (errorfiles <section>) */
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
int status; /* the status code associated to this error */
|
int status; /* the status code associated to this error */
|
||||||
struct http_reply *reply; /* the http reply for the errorfile */
|
struct http_reply *reply; /* the http reply for the errorfile */
|
||||||
} errorfile; /* describe an "errorfile" directive */
|
} inl; /* for HTTP_ERR_DIRECTIVE_INLINE only */
|
||||||
struct {
|
struct {
|
||||||
char *name; /* the http-errors section name */
|
char *name; /* the http-errors section name */
|
||||||
char status[HTTP_ERR_SIZE]; /* list of status to import (0: ignore, 1: implicit import, 2: explicit import) */
|
struct http_errors *resolved; /* resolved section pointer set via proxy_check_http_errors() */
|
||||||
} errorfiles; /* describe an "errorfiles" directive */
|
enum http_err_import status[HTTP_ERR_SIZE]; /* list of status to import */
|
||||||
} info;
|
} section; /* for HTTP_ERR_DIRECTIVE_SECTION only */
|
||||||
|
} type;
|
||||||
|
|
||||||
char *file; /* file where the directive appears */
|
char *file; /* file where the directive appears */
|
||||||
int line; /* line where the directive appears */
|
int line; /* line where the directive appears */
|
||||||
@ -2034,9 +2035,9 @@ static int proxy_parse_errorloc(char **args, int section, struct proxy *curpx,
|
|||||||
ret = -1;
|
ret = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
conf_err->type = 1;
|
conf_err->directive = HTTP_ERR_DIRECTIVE_INLINE;
|
||||||
conf_err->info.errorfile.status = status;
|
conf_err->type.inl.status = status;
|
||||||
conf_err->info.errorfile.reply = reply;
|
conf_err->type.inl.reply = reply;
|
||||||
|
|
||||||
conf_err->file = strdup(file);
|
conf_err->file = strdup(file);
|
||||||
conf_err->line = line;
|
conf_err->line = line;
|
||||||
@ -2105,9 +2106,9 @@ static int proxy_parse_errorfile(char **args, int section, struct proxy *curpx,
|
|||||||
ret = -1;
|
ret = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
conf_err->type = 1;
|
conf_err->directive = HTTP_ERR_DIRECTIVE_INLINE;
|
||||||
conf_err->info.errorfile.status = status;
|
conf_err->type.inl.status = status;
|
||||||
conf_err->info.errorfile.reply = reply;
|
conf_err->type.inl.reply = reply;
|
||||||
conf_err->file = strdup(file);
|
conf_err->file = strdup(file);
|
||||||
conf_err->line = line;
|
conf_err->line = line;
|
||||||
LIST_APPEND(&curpx->conf.errors, &conf_err->list);
|
LIST_APPEND(&curpx->conf.errors, &conf_err->list);
|
||||||
@ -2146,12 +2147,12 @@ static int proxy_parse_errorfiles(char **args, int section, struct proxy *curpx,
|
|||||||
memprintf(err, "%s : out of memory.", args[0]);
|
memprintf(err, "%s : out of memory.", args[0]);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
conf_err->type = 0;
|
|
||||||
|
|
||||||
conf_err->info.errorfiles.name = name;
|
conf_err->directive = HTTP_ERR_DIRECTIVE_SECTION;
|
||||||
|
conf_err->type.section.name = name;
|
||||||
if (!*(args[2])) {
|
if (!*(args[2])) {
|
||||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++)
|
for (rc = 0; rc < HTTP_ERR_SIZE; rc++)
|
||||||
conf_err->info.errorfiles.status[rc] = 1;
|
conf_err->type.section.status[rc] = HTTP_ERR_IMPORT_IMPLICIT;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int cur_arg, status;
|
int cur_arg, status;
|
||||||
@ -2160,7 +2161,7 @@ static int proxy_parse_errorfiles(char **args, int section, struct proxy *curpx,
|
|||||||
|
|
||||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||||
if (http_err_codes[rc] == status) {
|
if (http_err_codes[rc] == status) {
|
||||||
conf_err->info.errorfiles.status[rc] = 2;
|
conf_err->type.section.status[rc] = HTTP_ERR_IMPORT_EXPLICIT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2231,16 +2232,16 @@ static int proxy_parse_http_error(char **args, int section, struct proxy *curpx,
|
|||||||
if (reply->type == HTTP_REPLY_ERRFILES) {
|
if (reply->type == HTTP_REPLY_ERRFILES) {
|
||||||
int rc = http_get_status_idx(reply->status);
|
int rc = http_get_status_idx(reply->status);
|
||||||
|
|
||||||
conf_err->type = 2;
|
conf_err->directive = HTTP_ERR_DIRECTIVE_SECTION;
|
||||||
conf_err->info.errorfiles.name = reply->body.http_errors;
|
conf_err->type.section.name = reply->body.http_errors;
|
||||||
conf_err->info.errorfiles.status[rc] = 2;
|
conf_err->type.section.status[rc] = HTTP_ERR_IMPORT_EXPLICIT;
|
||||||
reply->body.http_errors = NULL;
|
reply->body.http_errors = NULL;
|
||||||
release_http_reply(reply);
|
release_http_reply(reply);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
conf_err->type = 1;
|
conf_err->directive = HTTP_ERR_DIRECTIVE_INLINE;
|
||||||
conf_err->info.errorfile.status = reply->status;
|
conf_err->type.inl.status = reply->status;
|
||||||
conf_err->info.errorfile.reply = reply;
|
conf_err->type.inl.reply = reply;
|
||||||
LIST_APPEND(&http_replies_list, &reply->list);
|
LIST_APPEND(&http_replies_list, &reply->list);
|
||||||
}
|
}
|
||||||
conf_err->file = strdup(file);
|
conf_err->file = strdup(file);
|
||||||
@ -2260,60 +2261,46 @@ static int proxy_parse_http_error(char **args, int section, struct proxy *curpx,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check "errorfiles" proxy keyword */
|
/* Converts <conf_errors> initialized during config parsing for <px> proxy.
|
||||||
static int proxy_check_errors(struct proxy *px)
|
* Each one of them is transformed in a http_reply type, stored in proxy
|
||||||
|
* replies array member. The original <conf_errors> becomes unneeded and is
|
||||||
|
* thus removed and freed.
|
||||||
|
*/
|
||||||
|
static int proxy_finalize_http_errors(struct proxy *px)
|
||||||
{
|
{
|
||||||
struct conf_errors *conf_err, *conf_err_back;
|
struct conf_errors *conf_err, *conf_err_back;
|
||||||
struct http_errors *http_errs;
|
struct http_errors *http_errs;
|
||||||
int rc, err = ERR_NONE;
|
int rc;
|
||||||
|
|
||||||
list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
|
list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
|
||||||
if (conf_err->type == 1) {
|
switch (conf_err->directive) {
|
||||||
/* errorfile */
|
case HTTP_ERR_DIRECTIVE_INLINE:
|
||||||
rc = http_get_status_idx(conf_err->info.errorfile.status);
|
rc = http_get_status_idx(conf_err->type.inl.status);
|
||||||
px->replies[rc] = conf_err->info.errorfile.reply;
|
px->replies[rc] = conf_err->type.inl.reply;
|
||||||
|
|
||||||
/* For proxy, to rely on default replies, just don't reference a reply */
|
/* For proxy, to rely on default replies, just don't reference a reply */
|
||||||
if (px->replies[rc]->type == HTTP_REPLY_ERRMSG && !px->replies[rc]->body.errmsg)
|
if (px->replies[rc]->type == HTTP_REPLY_ERRMSG && !px->replies[rc]->body.errmsg)
|
||||||
px->replies[rc] = NULL;
|
px->replies[rc] = NULL;
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* errorfiles */
|
|
||||||
list_for_each_entry(http_errs, &http_errors_list, list) {
|
|
||||||
if (strcmp(http_errs->id, conf_err->info.errorfiles.name) == 0)
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
/* unknown http-errors section */
|
case HTTP_ERR_DIRECTIVE_SECTION:
|
||||||
if (&http_errs->list == &http_errors_list) {
|
http_errs = conf_err->type.section.resolved;
|
||||||
ha_alert("proxy '%s': unknown http-errors section '%s' (at %s:%d).\n",
|
if (http_errs) {
|
||||||
px->id, conf_err->info.errorfiles.name, conf_err->file, conf_err->line);
|
|
||||||
err |= ERR_ALERT | ERR_FATAL;
|
|
||||||
free(conf_err->info.errorfiles.name);
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(conf_err->info.errorfiles.name);
|
|
||||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||||
if (conf_err->info.errorfiles.status[rc] > 0) {
|
if (conf_err->type.section.status[rc] == HTTP_ERR_IMPORT_NO)
|
||||||
|
continue;
|
||||||
if (http_errs->replies[rc])
|
if (http_errs->replies[rc])
|
||||||
px->replies[rc] = http_errs->replies[rc];
|
px->replies[rc] = http_errs->replies[rc];
|
||||||
else if (conf_err->info.errorfiles.status[rc] == 2)
|
|
||||||
ha_warning("config: proxy '%s' : status '%d' not declared in"
|
|
||||||
" http-errors section '%s' (at %s:%d).\n",
|
|
||||||
px->id, http_err_codes[rc], http_errs->id,
|
|
||||||
conf_err->file, conf_err->line);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next:
|
|
||||||
LIST_DELETE(&conf_err->list);
|
LIST_DELETE(&conf_err->list);
|
||||||
free(conf_err->file);
|
free(conf_err->file);
|
||||||
free(conf_err);
|
free(conf_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
return ERR_NONE;
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int post_check_errors()
|
static int post_check_errors()
|
||||||
@ -2343,6 +2330,55 @@ static int post_check_errors()
|
|||||||
return err_code;
|
return err_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Checks the validity of conf_errors stored in <px> proxy after the
|
||||||
|
* configuration is completely parsed.
|
||||||
|
*
|
||||||
|
* Returns ERR_NONE on success and a combination of ERR_CODE on failure.
|
||||||
|
*/
|
||||||
|
int proxy_check_http_errors(struct proxy *px)
|
||||||
|
{
|
||||||
|
struct http_errors *http_errs;
|
||||||
|
struct conf_errors *conf_err;
|
||||||
|
int section_found;
|
||||||
|
int rc, err = ERR_NONE;
|
||||||
|
|
||||||
|
list_for_each_entry(conf_err, &px->conf.errors, list) {
|
||||||
|
if (conf_err->directive == HTTP_ERR_DIRECTIVE_SECTION) {
|
||||||
|
section_found = 0;
|
||||||
|
list_for_each_entry(http_errs, &http_errors_list, list) {
|
||||||
|
if (strcmp(http_errs->id, conf_err->type.section.name) == 0) {
|
||||||
|
section_found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!section_found) {
|
||||||
|
ha_alert("proxy '%s': unknown http-errors section '%s' (at %s:%d).\n",
|
||||||
|
px->id, conf_err->type.section.name, conf_err->file, conf_err->line);
|
||||||
|
ha_free(&conf_err->type.section.name);
|
||||||
|
err |= ERR_ALERT | ERR_FATAL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf_err->type.section.resolved = http_errs;
|
||||||
|
ha_free(&conf_err->type.section.name);
|
||||||
|
|
||||||
|
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||||
|
if (conf_err->type.section.status[rc] == HTTP_ERR_IMPORT_EXPLICIT &&
|
||||||
|
!http_errs->replies[rc]) {
|
||||||
|
ha_warning("config: proxy '%s' : status '%d' not declared in"
|
||||||
|
" http-errors section '%s' (at %s:%d).\n",
|
||||||
|
px->id, http_err_codes[rc], http_errs->id,
|
||||||
|
conf_err->file, conf_err->line);
|
||||||
|
err |= ERR_WARN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx, char **errmsg)
|
int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx, char **errmsg)
|
||||||
{
|
{
|
||||||
struct conf_errors *conf_err, *new_conf_err = NULL;
|
struct conf_errors *conf_err, *new_conf_err = NULL;
|
||||||
@ -2354,19 +2390,22 @@ int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx
|
|||||||
memprintf(errmsg, "unable to duplicate default errors (out of memory).");
|
memprintf(errmsg, "unable to duplicate default errors (out of memory).");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
new_conf_err->type = conf_err->type;
|
new_conf_err->directive = conf_err->directive;
|
||||||
if (conf_err->type == 1) {
|
switch (conf_err->directive) {
|
||||||
new_conf_err->info.errorfile.status = conf_err->info.errorfile.status;
|
case HTTP_ERR_DIRECTIVE_INLINE:
|
||||||
new_conf_err->info.errorfile.reply = conf_err->info.errorfile.reply;
|
new_conf_err->type.inl.status = conf_err->type.inl.status;
|
||||||
}
|
new_conf_err->type.inl.reply = conf_err->type.inl.reply;
|
||||||
else {
|
break;
|
||||||
new_conf_err->info.errorfiles.name = strdup(conf_err->info.errorfiles.name);
|
|
||||||
if (!new_conf_err->info.errorfiles.name) {
|
case HTTP_ERR_DIRECTIVE_SECTION:
|
||||||
|
new_conf_err->type.section.name = strdup(conf_err->type.section.name);
|
||||||
|
if (!new_conf_err->type.section.name) {
|
||||||
memprintf(errmsg, "unable to duplicate default errors (out of memory).");
|
memprintf(errmsg, "unable to duplicate default errors (out of memory).");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
memcpy(&new_conf_err->info.errorfiles.status, &conf_err->info.errorfiles.status,
|
memcpy(&new_conf_err->type.section.status, &conf_err->type.section.status,
|
||||||
sizeof(conf_err->info.errorfiles.status));
|
sizeof(conf_err->type.section.status));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
new_conf_err->file = strdup(conf_err->file);
|
new_conf_err->file = strdup(conf_err->file);
|
||||||
new_conf_err->line = conf_err->line;
|
new_conf_err->line = conf_err->line;
|
||||||
@ -2385,8 +2424,8 @@ void proxy_release_conf_errors(struct proxy *px)
|
|||||||
struct conf_errors *conf_err, *conf_err_back;
|
struct conf_errors *conf_err, *conf_err_back;
|
||||||
|
|
||||||
list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
|
list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
|
||||||
if (conf_err->type == 0)
|
if (conf_err->directive == HTTP_ERR_DIRECTIVE_SECTION)
|
||||||
free(conf_err->info.errorfiles.name);
|
free(conf_err->type.section.name);
|
||||||
LIST_DELETE(&conf_err->list);
|
LIST_DELETE(&conf_err->list);
|
||||||
free(conf_err->file);
|
free(conf_err->file);
|
||||||
free(conf_err);
|
free(conf_err);
|
||||||
@ -2505,7 +2544,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
|
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
|
||||||
REGISTER_POST_PROXY_CHECK(proxy_check_errors);
|
REGISTER_POST_PROXY_CHECK(proxy_finalize_http_errors);
|
||||||
REGISTER_POST_CHECK(post_check_errors);
|
REGISTER_POST_CHECK(post_check_errors);
|
||||||
|
|
||||||
REGISTER_CONFIG_SECTION("http-errors", cfg_parse_http_errors, NULL);
|
REGISTER_CONFIG_SECTION("http-errors", cfg_parse_http_errors, NULL);
|
||||||
|
|||||||
219
src/htx.c
219
src/htx.c
@ -11,6 +11,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <haproxy/chunk.h>
|
#include <haproxy/chunk.h>
|
||||||
|
#include <haproxy/dynbuf.h>
|
||||||
#include <haproxy/global.h>
|
#include <haproxy/global.h>
|
||||||
#include <haproxy/htx.h>
|
#include <haproxy/htx.h>
|
||||||
#include <haproxy/net_helper.h>
|
#include <haproxy/net_helper.h>
|
||||||
@ -719,10 +720,163 @@ struct htx_blk *htx_replace_blk_value(struct htx *htx, struct htx_blk *blk,
|
|||||||
return blk;
|
return blk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Transfer HTX blocks from <src> to <dst>, stopping if <count> bytes were
|
||||||
|
* transferred (including payload and meta-data). It returns the number of bytes
|
||||||
|
* copied. By default, copied blocks are removed from <src> and only full
|
||||||
|
* headers and trailers part can be moved. <flags> can be set to change the
|
||||||
|
* default behavior:
|
||||||
|
* - HTX_XFER_KEEP_SRC_BLKS: source blocks are not removed
|
||||||
|
* - HTX_XFER_PARTIAL_HDRS_COPY: partial headers and trailers part can be xferred
|
||||||
|
* - HTX_XFER_HDRS_ONLY: Only the headers part is xferred
|
||||||
|
*/
|
||||||
|
size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct htx_blk *blk, *last_dstblk;
|
||||||
|
size_t ret = 0;
|
||||||
|
uint32_t max, last_dstblk_sz;
|
||||||
|
int dst_full = 0;
|
||||||
|
|
||||||
|
last_dstblk = NULL;
|
||||||
|
last_dstblk_sz = 0;
|
||||||
|
for (blk = htx_get_head_blk(src); blk && count; blk = htx_get_next_blk(src, blk)) {
|
||||||
|
struct ist v;
|
||||||
|
enum htx_blk_type type;
|
||||||
|
uint32_t sz;
|
||||||
|
|
||||||
|
/* Ignore unused block */
|
||||||
|
type = htx_get_blk_type(blk);
|
||||||
|
if (type == HTX_BLK_UNUSED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((flags & HTX_XFER_HDRS_ONLY) &&
|
||||||
|
type != HTX_BLK_REQ_SL && type != HTX_BLK_RES_SL &&
|
||||||
|
type != HTX_BLK_HDR && type != HTX_BLK_EOH)
|
||||||
|
break;
|
||||||
|
|
||||||
|
max = htx_get_max_blksz(dst, count);
|
||||||
|
if (!max)
|
||||||
|
break;
|
||||||
|
|
||||||
|
sz = htx_get_blksz(blk);
|
||||||
|
switch (type) {
|
||||||
|
case HTX_BLK_DATA:
|
||||||
|
v = htx_get_blk_value(src, blk);
|
||||||
|
if (v.len > max)
|
||||||
|
v.len = max;
|
||||||
|
v.len = htx_add_data(dst, v);
|
||||||
|
if (!v.len) {
|
||||||
|
dst_full = 1;
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
last_dstblk = htx_get_tail_blk(dst);
|
||||||
|
last_dstblk_sz = v.len;
|
||||||
|
count -= sizeof(*blk) + v.len;
|
||||||
|
ret += sizeof(*blk) + v.len;
|
||||||
|
if (v.len != sz) {
|
||||||
|
dst_full = 1;
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (sz > max) {
|
||||||
|
dst_full = 1;
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_dstblk = htx_add_blk(dst, type, sz);
|
||||||
|
if (!last_dstblk) {
|
||||||
|
dst_full = 1;
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
last_dstblk->info = blk->info;
|
||||||
|
htx_memcpy(htx_get_blk_ptr(dst, last_dstblk), htx_get_blk_ptr(src, blk), sz);
|
||||||
|
last_dstblk_sz = sz;
|
||||||
|
count -= sizeof(*blk) + sz;
|
||||||
|
ret += sizeof(*blk) + sz;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_dstblk = NULL; /* Reset last_dstblk because it was fully copied */
|
||||||
|
last_dstblk_sz = 0;
|
||||||
|
}
|
||||||
|
stop:
|
||||||
|
/* Here, if not NULL, <blk> point on the first not fully copied block in
|
||||||
|
* <src>. And <last_dstblk>, if defined, is the last not fully copied
|
||||||
|
* block in <dst>. So have:
|
||||||
|
* - <blk> == NULL: everything was copied. <last_dstblk> must be NULL
|
||||||
|
* - <blk> != NULL && <last_dstblk> == NULL: partial copy but the last block was fully copied
|
||||||
|
* - <blk> != NULL && <last_dstblk> != NULL: partial copy and the last block was partially copied (DATA block only)
|
||||||
|
*/
|
||||||
|
if (!(flags & HTX_XFER_PARTIAL_HDRS_COPY)) {
|
||||||
|
/* Partial headers/trailers copy is not supported */
|
||||||
|
struct htx_blk *dstblk;
|
||||||
|
enum htx_blk_type type = HTX_BLK_UNUSED;
|
||||||
|
|
||||||
|
dstblk = htx_get_tail_blk(dst);
|
||||||
|
if (dstblk)
|
||||||
|
type = htx_get_blk_type(dstblk);
|
||||||
|
|
||||||
|
/* the last copied block is a start-line, a header or a trailer */
|
||||||
|
if (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_HDR || type == HTX_BLK_TLR) {
|
||||||
|
/* <src > cannot have partial headers or trailers part */
|
||||||
|
BUG_ON(blk == NULL);
|
||||||
|
|
||||||
|
/* Remove partial headers/trailers from <dst> and rollback on <str> to not remove them later */
|
||||||
|
while (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_HDR || type == HTX_BLK_TLR) {
|
||||||
|
BUG_ON(type != htx_get_blk_type(blk));
|
||||||
|
ret -= sizeof(*blk) + htx_get_blksz(blk);
|
||||||
|
htx_remove_blk(dst, dstblk);
|
||||||
|
dstblk = htx_get_tail_blk(dst);
|
||||||
|
blk = htx_get_prev_blk(src, blk);
|
||||||
|
if (!dstblk)
|
||||||
|
break;
|
||||||
|
type = htx_get_blk_type(dstblk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Report if the xfer was interrupted because <dst> was
|
||||||
|
* full but is was originally empty
|
||||||
|
*/
|
||||||
|
if (dst_full && htx_is_empty(dst))
|
||||||
|
src->flags |= HTX_FL_PARSING_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(flags & HTX_XFER_KEEP_SRC_BLKS)) {
|
||||||
|
/* True xfer performed, remove copied block from <src> */
|
||||||
|
struct htx_blk *blk2;
|
||||||
|
|
||||||
|
/* Remove all fully copied blocks */
|
||||||
|
if (!blk)
|
||||||
|
htx_drain(src, src->data);
|
||||||
|
else {
|
||||||
|
for (blk2 = htx_get_head_blk(src); blk2 && blk2 != blk; blk2 = htx_remove_blk(src, blk2));
|
||||||
|
|
||||||
|
/* If copy was stopped on a DATA block and the last destination
|
||||||
|
* block is not NULL, it means a partial copy was performed. So
|
||||||
|
* cut the source block accordingly
|
||||||
|
*/
|
||||||
|
if (last_dstblk && blk2 && htx_get_blk_type(blk2) == HTX_BLK_DATA) {
|
||||||
|
htx_cut_data_blk(src, blk2, last_dstblk_sz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Everything was copied, transfer terminal HTX flags too */
|
||||||
|
if (!blk) {
|
||||||
|
dst->flags |= (src->flags & (HTX_FL_EOM|HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR));
|
||||||
|
src->flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Transfer HTX blocks from <src> to <dst>, stopping once the first block of the
|
/* Transfer HTX blocks from <src> to <dst>, stopping once the first block of the
|
||||||
* type <mark> is transferred (typically EOH or EOT) or when <count> bytes were
|
* type <mark> is transferred (typically EOH or EOT) or when <count> bytes were
|
||||||
* moved (including payload and meta-data). It returns the number of bytes moved
|
* moved (including payload and meta-data). It returns the number of bytes moved
|
||||||
* and the last HTX block inserted in <dst>.
|
* and the last HTX block inserted in <dst>.
|
||||||
|
*
|
||||||
|
* DEPRECATED
|
||||||
*/
|
*/
|
||||||
struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
|
struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
|
||||||
enum htx_blk_type mark)
|
enum htx_blk_type mark)
|
||||||
@ -1181,3 +1335,68 @@ int htx_append_msg(struct htx *dst, const struct htx *src)
|
|||||||
htx_truncate(dst, offset);
|
htx_truncate(dst, offset);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* If possible, transfer HTX blocks from <src> to a small buffer. This function
|
||||||
|
* allocate the small buffer and makes <dst> point on it. If <dst> is not empty
|
||||||
|
* or if <src> contains to many data, NULL is returned. If the allocation
|
||||||
|
* failed, NULL is returned. Otherwise <dst> is returned. <flags> instructs how
|
||||||
|
* the transfer must be performed.
|
||||||
|
*/
|
||||||
|
struct buffer *__htx_xfer_to_small_buffer(struct buffer *dst, struct buffer *src, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct htx *dst_htx;
|
||||||
|
struct htx *src_htx = htxbuf(src);
|
||||||
|
size_t sz = (sizeof(struct htx) + htx_used_space(src_htx));
|
||||||
|
|
||||||
|
if (dst->size || sz > global.tune.bufsize_small || !b_alloc_small(dst))
|
||||||
|
return NULL;
|
||||||
|
dst_htx = htx_from_buf(dst);
|
||||||
|
htx_xfer(dst_htx, src_htx, src_htx->size, flags);
|
||||||
|
htx_to_buf(dst_htx, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If possible, transfer HTX blocks from <src> to a large buffer. This function
|
||||||
|
* allocate the small buffer and makes <dst> point on it. If <dst> is not empty
|
||||||
|
* or if <src> contains to many data, NULL is returned. If the allocation
|
||||||
|
* failed, NULL is returned. Otherwise <dst> is returned. <flags> instructs how
|
||||||
|
* the transfer must be performed.
|
||||||
|
*/
|
||||||
|
struct buffer *__htx_xfer_to_large_buffer(struct buffer *dst, struct buffer *src, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct htx *dst_htx;
|
||||||
|
struct htx *src_htx = htxbuf(src);
|
||||||
|
size_t sz = (sizeof(struct htx) + htx_used_space(src_htx));
|
||||||
|
|
||||||
|
if (dst->size || sz > global.tune.bufsize_large || !b_alloc_large(dst))
|
||||||
|
return NULL;
|
||||||
|
dst_htx = htx_from_buf(dst);
|
||||||
|
htx_xfer(dst_htx, src_htx, src_htx->size, flags);
|
||||||
|
htx_to_buf(dst_htx, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_small_buffer() */
|
||||||
|
struct buffer *htx_move_to_small_buffer(struct buffer *dst, struct buffer *src)
|
||||||
|
{
|
||||||
|
return __htx_xfer_to_small_buffer(dst, src, HTX_XFER_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_large_buffer() */
|
||||||
|
struct buffer *htx_move_to_large_buffer(struct buffer *dst, struct buffer *src)
|
||||||
|
{
|
||||||
|
return __htx_xfer_to_large_buffer(dst, src, HTX_XFER_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_small_buffer() */
|
||||||
|
struct buffer *htx_copy_to_small_buffer(struct buffer *dst, struct buffer *src)
|
||||||
|
{
|
||||||
|
return __htx_xfer_to_small_buffer(dst, src, HTX_XFER_KEEP_SRC_BLKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_large_buffer() */
|
||||||
|
struct buffer *htx_copy_to_large_buffer(struct buffer *dst, struct buffer *src)
|
||||||
|
{
|
||||||
|
return __htx_xfer_to_large_buffer(dst, src, HTX_XFER_KEEP_SRC_BLKS);
|
||||||
|
}
|
||||||
|
|||||||
@ -424,7 +424,7 @@ end:
|
|||||||
* the one found in the JWE token.
|
* the one found in the JWE token.
|
||||||
* The tag is built out of a HMAC of some concatenated data taken from the JWE
|
* The tag is built out of a HMAC of some concatenated data taken from the JWE
|
||||||
* token (see https://datatracker.ietf.org/doc/html/rfc7518#section-5.2). The
|
* token (see https://datatracker.ietf.org/doc/html/rfc7518#section-5.2). The
|
||||||
* firest half of the previously decrypted cek is used as HMAC key.
|
* first half of the previously decrypted cek is used as HMAC key.
|
||||||
* Returns 0 in case of success, 1 otherwise.
|
* Returns 0 in case of success, 1 otherwise.
|
||||||
*/
|
*/
|
||||||
static int build_and_check_tag(jwe_enc enc, struct jwt_item items[JWE_ELT_MAX],
|
static int build_and_check_tag(jwe_enc enc, struct jwt_item items[JWE_ELT_MAX],
|
||||||
@ -602,7 +602,7 @@ static inline void clear_decoded_items(struct buffer *decoded_items[JWE_ELT_MAX]
|
|||||||
/*
|
/*
|
||||||
* Decrypt the contents of a JWE token thanks to the user-provided base64
|
* Decrypt the contents of a JWE token thanks to the user-provided base64
|
||||||
* encoded secret. This converter can only be used for tokens that have a
|
* encoded secret. This converter can only be used for tokens that have a
|
||||||
* symetric algorithm (AESKW, AESGCMKW or "dir" special case).
|
* symmetric algorithm (AESKW, AESGCMKW or "dir" special case).
|
||||||
* Returns the decrypted contents, or nothing if any error happened.
|
* Returns the decrypted contents, or nothing if any error happened.
|
||||||
*/
|
*/
|
||||||
static int sample_conv_jwt_decrypt_secret(const struct arg *args, struct sample *smp, void *private)
|
static int sample_conv_jwt_decrypt_secret(const struct arg *args, struct sample *smp, void *private)
|
||||||
@ -1096,7 +1096,7 @@ end:
|
|||||||
/*
|
/*
|
||||||
* Decrypt the contents of a JWE token thanks to the user-provided certificate
|
* Decrypt the contents of a JWE token thanks to the user-provided certificate
|
||||||
* and private key. This converter can only be used for tokens that have an
|
* and private key. This converter can only be used for tokens that have an
|
||||||
* asymetric algorithm (RSA only for now).
|
* asymmetric algorithm (RSA only for now).
|
||||||
* Returns the decrypted contents, or nothing if any error happened.
|
* Returns the decrypted contents, or nothing if any error happened.
|
||||||
*/
|
*/
|
||||||
static int sample_conv_jwt_decrypt_cert(const struct arg *args, struct sample *smp, void *private)
|
static int sample_conv_jwt_decrypt_cert(const struct arg *args, struct sample *smp, void *private)
|
||||||
@ -1173,7 +1173,7 @@ static int sample_conv_jwt_decrypt_cert(const struct arg *args, struct sample *s
|
|||||||
/* With ECDH-ES no CEK will be provided. */
|
/* With ECDH-ES no CEK will be provided. */
|
||||||
if (!ec || alg != JWE_ALG_ECDH_ES) {
|
if (!ec || alg != JWE_ALG_ECDH_ES) {
|
||||||
|
|
||||||
/* With asymetric crypto algorithms we should always have a CEK */
|
/* With asymmetric crypto algorithms we should always have a CEK */
|
||||||
if (!items[JWE_ELT_CEK].length)
|
if (!items[JWE_ELT_CEK].length)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
|
|||||||
@ -2631,6 +2631,16 @@ static int bind_parse_proto(char **args, int cur_arg, struct proxy *px, struct b
|
|||||||
memprintf(err, "'%s' : unknown MUX protocol '%s'", args[cur_arg], args[cur_arg+1]);
|
memprintf(err, "'%s' : unknown MUX protocol '%s'", args[cur_arg], args[cur_arg+1]);
|
||||||
return ERR_ALERT | ERR_FATAL;
|
return ERR_ALERT | ERR_FATAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conf->mux_proto->mux->flags & MX_FL_EXPERIMENTAL) {
|
||||||
|
if (!experimental_directives_allowed) {
|
||||||
|
memprintf(err, "'%s' : '%s' protocol is experimental, must be allowed via a global 'expose-experimental-directives'.",
|
||||||
|
args[cur_arg], args[cur_arg + 1]);
|
||||||
|
return ERR_ALERT | ERR_FATAL;
|
||||||
|
}
|
||||||
|
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
137
src/log.c
137
src/log.c
@ -334,6 +334,23 @@ char default_tcp_log_format[] = "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%
|
|||||||
char clf_tcp_log_format[] = "%{+Q}o %{-Q}ci - - [%T] \"TCP \" 000 %B \"\" \"\" %cp %ms %ft %b %s %Th %Tw %Tc %Tt %U %ts-- %ac %fc %bc %sc %rc %sq %bq \"\" \"\" ";
|
char clf_tcp_log_format[] = "%{+Q}o %{-Q}ci - - [%T] \"TCP \" 000 %B \"\" \"\" %cp %ms %ft %b %s %Th %Tw %Tc %Tt %U %ts-- %ac %fc %bc %sc %rc %sq %bq \"\" \"\" ";
|
||||||
char *log_format = NULL;
|
char *log_format = NULL;
|
||||||
|
|
||||||
|
char keylog_format_bc[] = "CLIENT_EARLY_TRAFFIC_SECRET %[ssl_bc_client_random,hex] %[ssl_bc_client_early_traffic_secret]\n"
|
||||||
|
"CLIENT_HANDSHAKE_TRAFFIC_SECRET %[ssl_bc_client_random,hex] %[ssl_bc_client_handshake_traffic_secret]\n"
|
||||||
|
"SERVER_HANDSHAKE_TRAFFIC_SECRET %[ssl_bc_client_random,hex] %[ssl_bc_server_handshake_traffic_secret]\n"
|
||||||
|
"CLIENT_TRAFFIC_SECRET_0 %[ssl_bc_client_random,hex] %[ssl_bc_client_traffic_secret_0]\n"
|
||||||
|
"SERVER_TRAFFIC_SECRET_0 %[ssl_bc_client_random,hex] %[ssl_bc_server_traffic_secret_0]\n"
|
||||||
|
"EXPORTER_SECRET %[ssl_bc_client_random,hex] %[ssl_bc_exporter_secret]\n"
|
||||||
|
"EARLY_EXPORTER_SECRET %[ssl_bc_client_random,hex] %[ssl_bc_early_exporter_secret]";
|
||||||
|
|
||||||
|
char keylog_format_fc[] = "CLIENT_EARLY_TRAFFIC_SECRET %[ssl_fc_client_random,hex] %[ssl_fc_client_early_traffic_secret]\n"
|
||||||
|
"CLIENT_HANDSHAKE_TRAFFIC_SECRET %[ssl_fc_client_random,hex] %[ssl_fc_client_handshake_traffic_secret]\n"
|
||||||
|
"SERVER_HANDSHAKE_TRAFFIC_SECRET %[ssl_fc_client_random,hex] %[ssl_fc_server_handshake_traffic_secret]\n"
|
||||||
|
"CLIENT_TRAFFIC_SECRET_0 %[ssl_fc_client_random,hex] %[ssl_fc_client_traffic_secret_0]\n"
|
||||||
|
"SERVER_TRAFFIC_SECRET_0 %[ssl_fc_client_random,hex] %[ssl_fc_server_traffic_secret_0]\n"
|
||||||
|
"EXPORTER_SECRET %[ssl_fc_client_random,hex] %[ssl_fc_exporter_secret]\n"
|
||||||
|
"EARLY_EXPORTER_SECRET %[ssl_fc_client_random,hex] %[ssl_fc_early_exporter_secret]";
|
||||||
|
|
||||||
|
|
||||||
/* Default string used for structured-data part in RFC5424 formatted
|
/* Default string used for structured-data part in RFC5424 formatted
|
||||||
* syslog messages.
|
* syslog messages.
|
||||||
*/
|
*/
|
||||||
@ -351,7 +368,9 @@ static inline int logformat_str_isdefault(const char *str)
|
|||||||
str == clf_http_log_format ||
|
str == clf_http_log_format ||
|
||||||
str == default_tcp_log_format ||
|
str == default_tcp_log_format ||
|
||||||
str == clf_tcp_log_format ||
|
str == clf_tcp_log_format ||
|
||||||
str == default_rfc5424_sd_log_format;
|
str == default_rfc5424_sd_log_format ||
|
||||||
|
str == keylog_format_bc ||
|
||||||
|
str == keylog_format_fc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* free logformat str if it is not a default (static) one */
|
/* free logformat str if it is not a default (static) one */
|
||||||
@ -2913,6 +2932,7 @@ static inline void __send_log_set_metadata_sd(struct ist *metadata, char *sd, si
|
|||||||
struct process_send_log_ctx {
|
struct process_send_log_ctx {
|
||||||
struct session *sess;
|
struct session *sess;
|
||||||
struct stream *stream;
|
struct stream *stream;
|
||||||
|
struct log_profile *profile;
|
||||||
struct log_orig origin;
|
struct log_orig origin;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2942,6 +2962,10 @@ static inline void _process_send_log_override(struct process_send_log_ctx *ctx,
|
|||||||
enum log_orig_id orig = (ctx) ? ctx->origin.id : LOG_ORIG_UNSPEC;
|
enum log_orig_id orig = (ctx) ? ctx->origin.id : LOG_ORIG_UNSPEC;
|
||||||
uint16_t orig_fl = (ctx) ? ctx->origin.flags : LOG_ORIG_FL_NONE;
|
uint16_t orig_fl = (ctx) ? ctx->origin.flags : LOG_ORIG_FL_NONE;
|
||||||
|
|
||||||
|
/* ctx->profile gets priority over logger profile */
|
||||||
|
if (ctx && ctx->profile)
|
||||||
|
prof = ctx->profile;
|
||||||
|
|
||||||
BUG_ON(!prof);
|
BUG_ON(!prof);
|
||||||
|
|
||||||
if (!b_is_null(&prof->log_tag))
|
if (!b_is_null(&prof->log_tag))
|
||||||
@ -3095,8 +3119,8 @@ static void process_send_log(struct process_send_log_ctx *ctx,
|
|||||||
|
|
||||||
nblogger += 1;
|
nblogger += 1;
|
||||||
|
|
||||||
/* logger may use a profile to override a few things */
|
/* caller or default logger may use a profile to override a few things */
|
||||||
if (unlikely(logger->prof))
|
if (unlikely(logger->prof || (ctx && ctx->profile)))
|
||||||
_process_send_log_override(ctx, logger, hdr, message, size, nblogger);
|
_process_send_log_override(ctx, logger, hdr, message, size, nblogger);
|
||||||
else
|
else
|
||||||
_process_send_log_final(logger, hdr, message, size, nblogger);
|
_process_send_log_final(logger, hdr, message, size, nblogger);
|
||||||
@ -5200,17 +5224,11 @@ out:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void do_log_ctx(struct process_send_log_ctx *ctx)
|
||||||
* opportunistic log when at least the session is known to exist
|
|
||||||
* <s> may be NULL
|
|
||||||
*
|
|
||||||
* Will not log if the frontend has no log defined. By default it will
|
|
||||||
* try to emit the log as INFO, unless the stream already exists and
|
|
||||||
* set-log-level was used.
|
|
||||||
*/
|
|
||||||
void do_log(struct session *sess, struct stream *s, struct log_orig origin)
|
|
||||||
{
|
{
|
||||||
struct process_send_log_ctx ctx;
|
struct stream *s = ctx->stream;
|
||||||
|
struct session *sess = ctx->sess;
|
||||||
|
struct log_orig origin = ctx->origin;
|
||||||
int size;
|
int size;
|
||||||
int sd_size = 0;
|
int sd_size = 0;
|
||||||
int level = -1;
|
int level = -1;
|
||||||
@ -5242,11 +5260,27 @@ void do_log(struct session *sess, struct stream *s, struct log_orig origin)
|
|||||||
|
|
||||||
size = sess_build_logline_orig(sess, s, logline, global.max_syslog_len, &sess->fe->logformat, origin);
|
size = sess_build_logline_orig(sess, s, logline, global.max_syslog_len, &sess->fe->logformat, origin);
|
||||||
|
|
||||||
|
__send_log(ctx, &sess->fe->loggers, &sess->fe->log_tag, level,
|
||||||
|
logline, size, logline_rfc5424, sd_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* opportunistic log when at least the session is known to exist
|
||||||
|
* <s> may be NULL
|
||||||
|
*
|
||||||
|
* Will not log if the frontend has no log defined. By default it will
|
||||||
|
* try to emit the log as INFO, unless the stream already exists and
|
||||||
|
* set-log-level was used.
|
||||||
|
*/
|
||||||
|
void do_log(struct session *sess, struct stream *s, struct log_orig origin)
|
||||||
|
{
|
||||||
|
struct process_send_log_ctx ctx;
|
||||||
|
|
||||||
ctx.origin = origin;
|
ctx.origin = origin;
|
||||||
ctx.sess = sess;
|
ctx.sess = sess;
|
||||||
ctx.stream = s;
|
ctx.stream = s;
|
||||||
__send_log(&ctx, &sess->fe->loggers, &sess->fe->log_tag, level,
|
ctx.profile = NULL;
|
||||||
logline, size, logline_rfc5424, sd_size);
|
do_log_ctx(&ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -5297,6 +5331,7 @@ void strm_log(struct stream *s, struct log_orig origin)
|
|||||||
ctx.origin = origin;
|
ctx.origin = origin;
|
||||||
ctx.sess = sess;
|
ctx.sess = sess;
|
||||||
ctx.stream = s;
|
ctx.stream = s;
|
||||||
|
ctx.profile = NULL;
|
||||||
__send_log(&ctx, &sess->fe->loggers, &sess->fe->log_tag, level,
|
__send_log(&ctx, &sess->fe->loggers, &sess->fe->log_tag, level,
|
||||||
logline, size, logline_rfc5424, sd_size);
|
logline, size, logline_rfc5424, sd_size);
|
||||||
s->logs.logwait = 0;
|
s->logs.logwait = 0;
|
||||||
@ -5364,6 +5399,7 @@ void _sess_log(struct session *sess, int embryonic)
|
|||||||
ctx.origin = orig;
|
ctx.origin = orig;
|
||||||
ctx.sess = sess;
|
ctx.sess = sess;
|
||||||
ctx.stream = NULL;
|
ctx.stream = NULL;
|
||||||
|
ctx.profile = NULL;
|
||||||
__send_log(&ctx, &sess->fe->loggers,
|
__send_log(&ctx, &sess->fe->loggers,
|
||||||
&sess->fe->log_tag, level,
|
&sess->fe->log_tag, level,
|
||||||
logline, size, logline_rfc5424, sd_size);
|
logline, size, logline_rfc5424, sd_size);
|
||||||
@ -6910,24 +6946,87 @@ static int px_parse_log_steps(char **args, int section_type, struct proxy *curpx
|
|||||||
static enum act_return do_log_action(struct act_rule *rule, struct proxy *px,
|
static enum act_return do_log_action(struct act_rule *rule, struct proxy *px,
|
||||||
struct session *sess, struct stream *s, int flags)
|
struct session *sess, struct stream *s, int flags)
|
||||||
{
|
{
|
||||||
|
struct process_send_log_ctx ctx;
|
||||||
|
|
||||||
/* do_log() expects valid session pointer */
|
/* do_log() expects valid session pointer */
|
||||||
BUG_ON(sess == NULL);
|
BUG_ON(sess == NULL);
|
||||||
|
|
||||||
do_log(sess, s, log_orig(rule->arg.expr_int.value, LOG_ORIG_FL_NONE));
|
ctx.origin = log_orig(rule->arg.do_log.orig, LOG_ORIG_FL_NONE);
|
||||||
|
ctx.sess = sess;
|
||||||
|
ctx.stream = s;
|
||||||
|
ctx.profile = rule->arg.do_log.profile;
|
||||||
|
|
||||||
|
do_log_ctx(&ctx);
|
||||||
return ACT_RET_CONT;
|
return ACT_RET_CONT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse a "do_log" action. It doesn't take any argument
|
static int do_log_action_check(struct act_rule *rule, struct proxy *px, char **err)
|
||||||
|
{
|
||||||
|
if (rule->arg.do_log.profile_name) {
|
||||||
|
struct log_profile *prof;
|
||||||
|
|
||||||
|
prof = log_profile_find_by_name(rule->arg.do_log.profile_name);
|
||||||
|
if (!prof) {
|
||||||
|
memprintf(err, "do-log action: profile '%s' is invalid", rule->arg.do_log.profile_name);
|
||||||
|
ha_free(&rule->arg.do_log.profile_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha_free(&rule->arg.do_log.profile_name);
|
||||||
|
|
||||||
|
if (!log_profile_postcheck(px, prof, err)) {
|
||||||
|
memprintf(err, "do-log action on %s %s uses incompatible log-profile '%s': %s", proxy_type_str(px), px->id, prof->id, *err);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rule->arg.do_log.profile = prof;
|
||||||
|
}
|
||||||
|
return 1; // success
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_log_action_release(struct act_rule *rule)
|
||||||
|
{
|
||||||
|
ha_free(&rule->arg.do_log.profile_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Parse a "do_log" action. It takes optional "log-profile" argument to
|
||||||
|
* specifically use a given log-profile when generating the log message
|
||||||
|
*
|
||||||
* May be used from places where per-context actions are usually registered
|
* May be used from places where per-context actions are usually registered
|
||||||
*/
|
*/
|
||||||
enum act_parse_ret do_log_parse_act(enum log_orig_id id,
|
enum act_parse_ret do_log_parse_act(enum log_orig_id id,
|
||||||
const char **args, int *orig_arg, struct proxy *px,
|
const char **args, int *orig_arg, struct proxy *px,
|
||||||
struct act_rule *rule, char **err)
|
struct act_rule *rule, char **err)
|
||||||
{
|
{
|
||||||
|
int cur_arg = *orig_arg;
|
||||||
|
|
||||||
rule->action_ptr = do_log_action;
|
rule->action_ptr = do_log_action;
|
||||||
rule->action = ACT_CUSTOM;
|
rule->action = ACT_CUSTOM;
|
||||||
rule->release_ptr = NULL;
|
rule->check_ptr = do_log_action_check;
|
||||||
rule->arg.expr_int.value = id;
|
rule->release_ptr = do_log_action_release;
|
||||||
|
rule->arg.do_log.orig = id;
|
||||||
|
|
||||||
|
while (*args[*orig_arg]) {
|
||||||
|
if (!strcmp(args[*orig_arg], "profile")) {
|
||||||
|
if (!*args[*orig_arg + 1]) {
|
||||||
|
memprintf(err,
|
||||||
|
"action '%s': 'profile' expects argument.",
|
||||||
|
args[cur_arg-1]);
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
}
|
||||||
|
rule->arg.do_log.profile_name = strdup(args[*orig_arg + 1]);
|
||||||
|
if (!rule->arg.do_log.profile_name) {
|
||||||
|
memprintf(err,
|
||||||
|
"action '%s': memory error when setting 'profile'",
|
||||||
|
args[cur_arg-1]);
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
}
|
||||||
|
*orig_arg += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return ACT_RET_PRS_OK;
|
return ACT_RET_PRS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
24
src/map.c
24
src/map.c
@ -621,6 +621,9 @@ static int cli_parse_get_map(char **args, char *payload, struct appctx *appctx,
|
|||||||
{
|
{
|
||||||
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
|
if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
|
||||||
/* Set flags. */
|
/* Set flags. */
|
||||||
if (args[1][0] == 'm')
|
if (args[1][0] == 'm')
|
||||||
@ -664,6 +667,9 @@ static int cli_parse_prepare_map(char **args, char *payload, struct appctx *appc
|
|||||||
{
|
{
|
||||||
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (strcmp(args[1], "map") == 0 ||
|
if (strcmp(args[1], "map") == 0 ||
|
||||||
strcmp(args[1], "acl") == 0) {
|
strcmp(args[1], "acl") == 0) {
|
||||||
uint next_gen;
|
uint next_gen;
|
||||||
@ -706,6 +712,9 @@ static int cli_parse_show_map(char **args, char *payload, struct appctx *appctx,
|
|||||||
{
|
{
|
||||||
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (strcmp(args[1], "map") == 0 ||
|
if (strcmp(args[1], "map") == 0 ||
|
||||||
strcmp(args[1], "acl") == 0) {
|
strcmp(args[1], "acl") == 0) {
|
||||||
const char *gen = NULL;
|
const char *gen = NULL;
|
||||||
@ -760,6 +769,9 @@ static int cli_parse_set_map(char **args, char *payload, struct appctx *appctx,
|
|||||||
{
|
{
|
||||||
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (strcmp(args[1], "map") == 0) {
|
if (strcmp(args[1], "map") == 0) {
|
||||||
char *err;
|
char *err;
|
||||||
|
|
||||||
@ -832,6 +844,9 @@ static int cli_parse_add_map(char **args, char *payload, struct appctx *appctx,
|
|||||||
{
|
{
|
||||||
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (strcmp(args[1], "map") == 0 ||
|
if (strcmp(args[1], "map") == 0 ||
|
||||||
strcmp(args[1], "acl") == 0) {
|
strcmp(args[1], "acl") == 0) {
|
||||||
const char *gen = NULL;
|
const char *gen = NULL;
|
||||||
@ -962,6 +977,9 @@ static int cli_parse_del_map(char **args, char *payload, struct appctx *appctx,
|
|||||||
{
|
{
|
||||||
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (args[1][0] == 'm')
|
if (args[1][0] == 'm')
|
||||||
ctx->display_flags = PAT_REF_MAP;
|
ctx->display_flags = PAT_REF_MAP;
|
||||||
else
|
else
|
||||||
@ -1057,6 +1075,9 @@ static int cli_parse_clear_map(char **args, char *payload, struct appctx *appctx
|
|||||||
{
|
{
|
||||||
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
|
if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
|
||||||
const char *gen = NULL;
|
const char *gen = NULL;
|
||||||
|
|
||||||
@ -1113,6 +1134,9 @@ static int cli_parse_commit_map(char **args, char *payload, struct appctx *appct
|
|||||||
{
|
{
|
||||||
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_map_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
|
if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
|
||||||
const char *gen = NULL;
|
const char *gen = NULL;
|
||||||
uint genid;
|
uint genid;
|
||||||
|
|||||||
@ -4885,8 +4885,14 @@ static size_t h1_snd_buf(struct stconn *sc, struct buffer *buf, size_t count, in
|
|||||||
|
|
||||||
/* Inherit some flags from the upper layer */
|
/* Inherit some flags from the upper layer */
|
||||||
h1c->flags &= ~(H1C_F_CO_MSG_MORE|H1C_F_CO_STREAMER);
|
h1c->flags &= ~(H1C_F_CO_MSG_MORE|H1C_F_CO_STREAMER);
|
||||||
if (flags & CO_SFL_MSG_MORE)
|
if (flags & CO_SFL_MSG_MORE) {
|
||||||
|
/* Don't set H1C_F_CO_MSG_MORE when sending a bodyless response to client.
|
||||||
|
* We must do that if the response is not finished, regardless it a bodyless
|
||||||
|
* response, to be sure to send it ASAP.
|
||||||
|
*/
|
||||||
|
if ((h1c->flags & H1C_F_IS_BACK) || !(h1s->flags & H1S_F_BODYLESS_RESP))
|
||||||
h1c->flags |= H1C_F_CO_MSG_MORE;
|
h1c->flags |= H1C_F_CO_MSG_MORE;
|
||||||
|
}
|
||||||
if (flags & CO_SFL_STREAMER)
|
if (flags & CO_SFL_STREAMER)
|
||||||
h1c->flags |= H1C_F_CO_STREAMER;
|
h1c->flags |= H1C_F_CO_STREAMER;
|
||||||
|
|
||||||
|
|||||||
95
src/mux_h2.c
95
src/mux_h2.c
@ -489,6 +489,9 @@ static int h2_be_glitches_threshold = 0; /* backend's max glitches
|
|||||||
static int h2_fe_glitches_threshold = 0; /* frontend's max glitches: unlimited */
|
static int h2_fe_glitches_threshold = 0; /* frontend's max glitches: unlimited */
|
||||||
static uint h2_be_rxbuf = 0; /* backend's default total rxbuf (bytes) */
|
static uint h2_be_rxbuf = 0; /* backend's default total rxbuf (bytes) */
|
||||||
static uint h2_fe_rxbuf = 0; /* frontend's default total rxbuf (bytes) */
|
static uint h2_fe_rxbuf = 0; /* frontend's default total rxbuf (bytes) */
|
||||||
|
static unsigned int h2_be_max_frames_at_once = 0; /* backend value: 0=no limit */
|
||||||
|
static unsigned int h2_fe_max_frames_at_once = 0; /* frontend value: 0=no limit */
|
||||||
|
static unsigned int h2_fe_max_rst_at_once = 0; /* frontend value: 0=no limit */
|
||||||
static unsigned int h2_settings_max_concurrent_streams = 100; /* default value */
|
static unsigned int h2_settings_max_concurrent_streams = 100; /* default value */
|
||||||
static unsigned int h2_be_settings_max_concurrent_streams = 0; /* backend value */
|
static unsigned int h2_be_settings_max_concurrent_streams = 0; /* backend value */
|
||||||
static unsigned int h2_fe_settings_max_concurrent_streams = 0; /* frontend value */
|
static unsigned int h2_fe_settings_max_concurrent_streams = 0; /* frontend value */
|
||||||
@ -3450,6 +3453,7 @@ static int h2c_handle_priority(struct h2c *h2c)
|
|||||||
static int h2c_handle_rst_stream(struct h2c *h2c, struct h2s *h2s)
|
static int h2c_handle_rst_stream(struct h2c *h2c, struct h2s *h2s)
|
||||||
{
|
{
|
||||||
int rst_code;
|
int rst_code;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
TRACE_ENTER(H2_EV_RX_FRAME|H2_EV_RX_RST|H2_EV_RX_EOI, h2c->conn, h2s);
|
TRACE_ENTER(H2_EV_RX_FRAME|H2_EV_RX_RST|H2_EV_RX_EOI, h2c->conn, h2s);
|
||||||
|
|
||||||
@ -3478,6 +3482,16 @@ static int h2c_handle_rst_stream(struct h2c *h2c, struct h2s *h2s)
|
|||||||
|
|
||||||
if (h2s_sc(h2s)) {
|
if (h2s_sc(h2s)) {
|
||||||
se_fl_set_error(h2s->sd);
|
se_fl_set_error(h2s->sd);
|
||||||
|
|
||||||
|
if (unlikely(!se_fl_test(h2s->sd, SE_FL_APP_STARTED))) {
|
||||||
|
/* the application layer has not yet started to read! */
|
||||||
|
TRACE_STATE("received early RST_STREAM", H2_EV_RX_FRAME|H2_EV_RX_RST, h2c->conn);
|
||||||
|
if (h2c_report_glitch(h2c, 1, "received early RST_STREAM, attack suspected")) {
|
||||||
|
TRACE_DEVEL("too many glitches, leaving on error", H2_EV_RX_FRAME|H2_EV_RX_RST, h2c->conn, h2s);
|
||||||
|
ret = 0; // report the error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
se_report_term_evt(h2s->sd, se_tevt_type_rst_rcvd);
|
se_report_term_evt(h2s->sd, se_tevt_type_rst_rcvd);
|
||||||
if (!h2s->sd->abort_info.info) {
|
if (!h2s->sd->abort_info.info) {
|
||||||
h2s->sd->abort_info.info = (SE_ABRT_SRC_MUX_H2 << SE_ABRT_SRC_SHIFT);
|
h2s->sd->abort_info.info = (SE_ABRT_SRC_MUX_H2 << SE_ABRT_SRC_SHIFT);
|
||||||
@ -3488,7 +3502,7 @@ static int h2c_handle_rst_stream(struct h2c *h2c, struct h2s *h2s)
|
|||||||
|
|
||||||
h2s->flags |= H2_SF_RST_RCVD;
|
h2s->flags |= H2_SF_RST_RCVD;
|
||||||
TRACE_LEAVE(H2_EV_RX_FRAME|H2_EV_RX_RST|H2_EV_RX_EOI, h2c->conn, h2s);
|
TRACE_LEAVE(H2_EV_RX_FRAME|H2_EV_RX_RST|H2_EV_RX_EOI, h2c->conn, h2s);
|
||||||
return 1;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* processes a HEADERS frame. Returns h2s on success or NULL on missing data.
|
/* processes a HEADERS frame. Returns h2s on success or NULL on missing data.
|
||||||
@ -4239,6 +4253,8 @@ static void h2_process_demux(struct h2c *h2c)
|
|||||||
struct h2_fh hdr;
|
struct h2_fh hdr;
|
||||||
unsigned int padlen = 0;
|
unsigned int padlen = 0;
|
||||||
int32_t old_iw = h2c->miw;
|
int32_t old_iw = h2c->miw;
|
||||||
|
uint frames_budget = 0;
|
||||||
|
uint rst_budget = 0;
|
||||||
|
|
||||||
TRACE_ENTER(H2_EV_H2C_WAKE, h2c->conn);
|
TRACE_ENTER(H2_EV_H2C_WAKE, h2c->conn);
|
||||||
|
|
||||||
@ -4327,6 +4343,14 @@ static void h2_process_demux(struct h2c *h2c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (h2c->flags & H2_CF_IS_BACK) {
|
||||||
|
frames_budget = h2_be_max_frames_at_once;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
frames_budget = h2_fe_max_frames_at_once;
|
||||||
|
rst_budget = h2_fe_max_rst_at_once;
|
||||||
|
}
|
||||||
|
|
||||||
/* process as many incoming frames as possible below */
|
/* process as many incoming frames as possible below */
|
||||||
while (1) {
|
while (1) {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -4629,6 +4653,29 @@ static void h2_process_demux(struct h2c *h2c)
|
|||||||
h2c->st0 = H2_CS_FRAME_H;
|
h2c->st0 = H2_CS_FRAME_H;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If more frames remain in the buffer, let's first check if we've
|
||||||
|
* depleted the frames processing budget. Consuming the RST budget
|
||||||
|
* makes the tasklet go to TL_BULK to make it less priority than
|
||||||
|
* other processing since it's often used by attacks, while other
|
||||||
|
* frame types just yield normally.
|
||||||
|
*/
|
||||||
|
if (b_data(&h2c->dbuf)) {
|
||||||
|
if (h2c->dft == H2_FT_RST_STREAM && (rst_budget && !--rst_budget)) {
|
||||||
|
/* we've consumed all RST frames permitted by
|
||||||
|
* the budget, we have to yield now.
|
||||||
|
*/
|
||||||
|
tasklet_wakeup(h2c->wait_event.tasklet, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ((frames_budget && !--frames_budget)) {
|
||||||
|
/* we've consumed all frames permitted by the
|
||||||
|
* budget, we have to yield now.
|
||||||
|
*/
|
||||||
|
tasklet_wakeup(h2c->wait_event.tasklet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (h2c_update_strm_rx_win(h2c) &&
|
if (h2c_update_strm_rx_win(h2c) &&
|
||||||
@ -7830,7 +7877,6 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
|
|||||||
struct htx *h2s_htx = NULL;
|
struct htx *h2s_htx = NULL;
|
||||||
struct htx *buf_htx = NULL;
|
struct htx *buf_htx = NULL;
|
||||||
struct buffer *rxbuf = NULL;
|
struct buffer *rxbuf = NULL;
|
||||||
struct htx_ret htxret;
|
|
||||||
size_t ret = 0;
|
size_t ret = 0;
|
||||||
uint prev_h2c_flags = h2c->flags;
|
uint prev_h2c_flags = h2c->flags;
|
||||||
unsigned long long prev_body_len = h2s->body_len;
|
unsigned long long prev_body_len = h2s->body_len;
|
||||||
@ -7865,17 +7911,7 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
htxret = htx_xfer_blks(buf_htx, h2s_htx, count, HTX_BLK_UNUSED);
|
count -= htx_xfer(buf_htx, h2s_htx, count, HTX_XFER_DEFAULT);
|
||||||
count -= htxret.ret;
|
|
||||||
|
|
||||||
if (h2s_htx->flags & HTX_FL_PARSING_ERROR) {
|
|
||||||
buf_htx->flags |= HTX_FL_PARSING_ERROR;
|
|
||||||
if (htx_is_empty(buf_htx))
|
|
||||||
se_fl_set(h2s->sd, SE_FL_EOI);
|
|
||||||
}
|
|
||||||
else if (htx_is_empty(h2s_htx)) {
|
|
||||||
buf_htx->flags |= (h2s_htx->flags & HTX_FL_EOM);
|
|
||||||
}
|
|
||||||
|
|
||||||
htx_to_buf(buf_htx, buf);
|
htx_to_buf(buf_htx, buf);
|
||||||
htx_to_buf(h2s_htx, rxbuf);
|
htx_to_buf(h2s_htx, rxbuf);
|
||||||
@ -7904,13 +7940,7 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
|
|||||||
|
|
||||||
/* tell the stream layer whether there are data left or not */
|
/* tell the stream layer whether there are data left or not */
|
||||||
if (h2s_rxbuf_cnt(h2s)) {
|
if (h2s_rxbuf_cnt(h2s)) {
|
||||||
/* Note that parsing errors can also arrive here, we may need
|
|
||||||
* to propagate errors upstream otherwise no new activity will
|
|
||||||
* unblock them.
|
|
||||||
*/
|
|
||||||
se_fl_set(h2s->sd, SE_FL_RCV_MORE | SE_FL_WANT_ROOM);
|
se_fl_set(h2s->sd, SE_FL_RCV_MORE | SE_FL_WANT_ROOM);
|
||||||
if (h2s_htx && h2s_htx->flags & HTX_FL_PARSING_ERROR)
|
|
||||||
h2s_propagate_term_flags(h2c, h2s);
|
|
||||||
BUG_ON_HOT(!buf->data);
|
BUG_ON_HOT(!buf->data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -8800,6 +8830,30 @@ static int h2_parse_max_total_streams(char **args, int section_type, struct prox
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* config parser for global "tune.h2.{be.,fe.,}max-{frames,rst}-at-once" */
|
||||||
|
static int h2_parse_max_frames_at_once(char **args, int section_type, struct proxy *curpx,
|
||||||
|
const struct proxy *defpx, const char *file, int line,
|
||||||
|
char **err)
|
||||||
|
{
|
||||||
|
uint *vptr;
|
||||||
|
|
||||||
|
/* backend/frontend/default */
|
||||||
|
if (strcmp(args[0], "tune.h2.be.max-frames-at-once") == 0)
|
||||||
|
vptr = &h2_be_max_frames_at_once;
|
||||||
|
else if (strcmp(args[0], "tune.h2.fe.max-frames-at-once") == 0)
|
||||||
|
vptr = &h2_fe_max_frames_at_once;
|
||||||
|
else if (strcmp(args[0], "tune.h2.fe.max-rst-at-once") == 0)
|
||||||
|
vptr = &h2_fe_max_rst_at_once;
|
||||||
|
else
|
||||||
|
BUG_ON(1, "unhandled keyword");
|
||||||
|
|
||||||
|
if (too_many_args(1, args, err, NULL))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*vptr = atoi(args[1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* config parser for global "tune.h2.max-frame-size" */
|
/* config parser for global "tune.h2.max-frame-size" */
|
||||||
static int h2_parse_max_frame_size(char **args, int section_type, struct proxy *curpx,
|
static int h2_parse_max_frame_size(char **args, int section_type, struct proxy *curpx,
|
||||||
const struct proxy *defpx, const char *file, int line,
|
const struct proxy *defpx, const char *file, int line,
|
||||||
@ -8898,10 +8952,13 @@ static struct cfg_kw_list cfg_kws = {ILH, {
|
|||||||
{ CFG_GLOBAL, "tune.h2.be.glitches-threshold", h2_parse_glitches_threshold },
|
{ CFG_GLOBAL, "tune.h2.be.glitches-threshold", h2_parse_glitches_threshold },
|
||||||
{ CFG_GLOBAL, "tune.h2.be.initial-window-size", h2_parse_initial_window_size },
|
{ CFG_GLOBAL, "tune.h2.be.initial-window-size", h2_parse_initial_window_size },
|
||||||
{ CFG_GLOBAL, "tune.h2.be.max-concurrent-streams", h2_parse_max_concurrent_streams },
|
{ CFG_GLOBAL, "tune.h2.be.max-concurrent-streams", h2_parse_max_concurrent_streams },
|
||||||
|
{ CFG_GLOBAL, "tune.h2.be.max-frames-at-once", h2_parse_max_frames_at_once },
|
||||||
{ CFG_GLOBAL, "tune.h2.be.rxbuf", h2_parse_rxbuf },
|
{ CFG_GLOBAL, "tune.h2.be.rxbuf", h2_parse_rxbuf },
|
||||||
{ CFG_GLOBAL, "tune.h2.fe.glitches-threshold", h2_parse_glitches_threshold },
|
{ CFG_GLOBAL, "tune.h2.fe.glitches-threshold", h2_parse_glitches_threshold },
|
||||||
{ CFG_GLOBAL, "tune.h2.fe.initial-window-size", h2_parse_initial_window_size },
|
{ CFG_GLOBAL, "tune.h2.fe.initial-window-size", h2_parse_initial_window_size },
|
||||||
{ CFG_GLOBAL, "tune.h2.fe.max-concurrent-streams", h2_parse_max_concurrent_streams },
|
{ CFG_GLOBAL, "tune.h2.fe.max-concurrent-streams", h2_parse_max_concurrent_streams },
|
||||||
|
{ CFG_GLOBAL, "tune.h2.fe.max-frames-at-once", h2_parse_max_frames_at_once },
|
||||||
|
{ CFG_GLOBAL, "tune.h2.fe.max-rst-at-once", h2_parse_max_frames_at_once },
|
||||||
{ CFG_GLOBAL, "tune.h2.fe.max-total-streams", h2_parse_max_total_streams },
|
{ CFG_GLOBAL, "tune.h2.fe.max-total-streams", h2_parse_max_total_streams },
|
||||||
{ CFG_GLOBAL, "tune.h2.fe.rxbuf", h2_parse_rxbuf },
|
{ CFG_GLOBAL, "tune.h2.fe.rxbuf", h2_parse_rxbuf },
|
||||||
{ CFG_GLOBAL, "tune.h2.header-table-size", h2_parse_header_table_size },
|
{ CFG_GLOBAL, "tune.h2.header-table-size", h2_parse_header_table_size },
|
||||||
|
|||||||
238
src/mux_quic.c
238
src/mux_quic.c
@ -1,4 +1,5 @@
|
|||||||
#include <haproxy/mux_quic.h>
|
#include <haproxy/mux_quic.h>
|
||||||
|
#include <haproxy/mux_quic_priv.h>
|
||||||
|
|
||||||
#include <import/eb64tree.h>
|
#include <import/eb64tree.h>
|
||||||
|
|
||||||
@ -10,6 +11,7 @@
|
|||||||
#include <haproxy/global-t.h>
|
#include <haproxy/global-t.h>
|
||||||
#include <haproxy/h3.h>
|
#include <haproxy/h3.h>
|
||||||
#include <haproxy/list.h>
|
#include <haproxy/list.h>
|
||||||
|
#include <haproxy/mux_quic_qstrm.h>
|
||||||
#include <haproxy/ncbuf.h>
|
#include <haproxy/ncbuf.h>
|
||||||
#include <haproxy/pool.h>
|
#include <haproxy/pool.h>
|
||||||
#include <haproxy/proxy.h>
|
#include <haproxy/proxy.h>
|
||||||
@ -32,6 +34,7 @@
|
|||||||
#include <haproxy/stconn.h>
|
#include <haproxy/stconn.h>
|
||||||
#include <haproxy/time.h>
|
#include <haproxy/time.h>
|
||||||
#include <haproxy/trace.h>
|
#include <haproxy/trace.h>
|
||||||
|
#include <haproxy/xprt_qstrm.h>
|
||||||
#include <haproxy/xref.h>
|
#include <haproxy/xref.h>
|
||||||
|
|
||||||
DECLARE_TYPED_POOL(pool_head_qcc, "qcc", struct qcc);
|
DECLARE_TYPED_POOL(pool_head_qcc, "qcc", struct qcc);
|
||||||
@ -44,7 +47,7 @@ static void qmux_ctrl_room(struct qc_stream_desc *, uint64_t room);
|
|||||||
/* Returns true if pacing should be used for <conn> connection. */
|
/* Returns true if pacing should be used for <conn> connection. */
|
||||||
static int qcc_is_pacing_active(const struct connection *conn)
|
static int qcc_is_pacing_active(const struct connection *conn)
|
||||||
{
|
{
|
||||||
return quic_tune_conn_test(QUIC_TUNE_FB_TX_PACING, conn);
|
return conn_is_quic(conn) && quic_tune_conn_test(QUIC_TUNE_FB_TX_PACING, conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free <rxbuf> instance and its inner data storage attached to <qcs> stream. */
|
/* Free <rxbuf> instance and its inner data storage attached to <qcs> stream. */
|
||||||
@ -100,9 +103,9 @@ static void qcs_free(struct qcs *qcs)
|
|||||||
qcc->app_ops->detach(qcs);
|
qcc->app_ops->detach(qcs);
|
||||||
|
|
||||||
/* Release qc_stream_desc buffer from quic-conn layer. */
|
/* Release qc_stream_desc buffer from quic-conn layer. */
|
||||||
if (qcs->stream) {
|
if (conn_is_quic(qcc->conn) && qcs->tx.stream) {
|
||||||
qc_stream_desc_sub_send(qcs->stream, NULL);
|
qc_stream_desc_sub_send(qcs->tx.stream, NULL);
|
||||||
qc_stream_desc_release(qcs->stream, qcs->tx.fc.off_real, qcc);
|
qc_stream_desc_release(qcs->tx.stream, qcs->tx.fc.off_real, qcc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free Rx buffer. */
|
/* Free Rx buffer. */
|
||||||
@ -133,7 +136,7 @@ static struct qcs *qcs_new(struct qcc *qcc, uint64_t id, enum qcs_type type)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
qcs->stream = NULL;
|
qcs->tx.stream = NULL;
|
||||||
qcs->qcc = qcc;
|
qcs->qcc = qcc;
|
||||||
qcs->sess = NULL;
|
qcs->sess = NULL;
|
||||||
qcs->sd = NULL;
|
qcs->sd = NULL;
|
||||||
@ -197,15 +200,25 @@ static struct qcs *qcs_new(struct qcc *qcc, uint64_t id, enum qcs_type type)
|
|||||||
|
|
||||||
/* Allocate transport layer stream descriptor. Only needed for TX. */
|
/* Allocate transport layer stream descriptor. Only needed for TX. */
|
||||||
if (!quic_stream_is_uni(id) || !quic_stream_is_remote(qcc, id)) {
|
if (!quic_stream_is_uni(id) || !quic_stream_is_remote(qcc, id)) {
|
||||||
|
if (conn_is_quic(qcc->conn)) {
|
||||||
struct quic_conn *qc = qcc->conn->handle.qc;
|
struct quic_conn *qc = qcc->conn->handle.qc;
|
||||||
qcs->stream = qc_stream_desc_new(id, type, qcs, qc);
|
qcs->tx.stream = qc_stream_desc_new(id, type, qcs, qc);
|
||||||
if (!qcs->stream) {
|
if (!qcs->tx.stream) {
|
||||||
TRACE_ERROR("qc_stream_desc alloc failure", QMUX_EV_QCS_NEW, qcc->conn, qcs);
|
TRACE_ERROR("qc_stream_desc alloc failure", QMUX_EV_QCS_NEW, qcc->conn, qcs);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
qc_stream_desc_sub_send(qcs->stream, qmux_ctrl_send);
|
qc_stream_desc_sub_send(qcs->tx.stream, qmux_ctrl_send);
|
||||||
qc_stream_desc_sub_room(qcs->stream, qmux_ctrl_room);
|
qc_stream_desc_sub_room(qcs->tx.stream, qmux_ctrl_room);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qcs->tx.qstrm_buf = BUF_NULL;
|
||||||
|
b_alloc(&qcs->tx.qstrm_buf, DB_MUX_TX);
|
||||||
|
if (!b_size(&qcs->tx.qstrm_buf)) {
|
||||||
|
TRACE_ERROR("tx buf alloc failure", QMUX_EV_QCS_NEW, qcc->conn, qcs);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qcc->app_ops->attach && qcc->app_ops->attach(qcs, qcc->ctx)) {
|
if (qcc->app_ops->attach && qcc->app_ops->attach(qcs, qcc->ctx)) {
|
||||||
@ -398,7 +411,7 @@ static void qcc_refresh_timeout(struct qcc *qcc)
|
|||||||
/* Mark a stream as open if it was idle. This can be used on every
|
/* Mark a stream as open if it was idle. This can be used on every
|
||||||
* successful emission/reception operation to update the stream state.
|
* successful emission/reception operation to update the stream state.
|
||||||
*/
|
*/
|
||||||
static void qcs_idle_open(struct qcs *qcs)
|
void qcs_idle_open(struct qcs *qcs)
|
||||||
{
|
{
|
||||||
/* This operation must not be used if the stream is already closed. */
|
/* This operation must not be used if the stream is already closed. */
|
||||||
BUG_ON_HOT(qcs->st == QC_SS_CLO);
|
BUG_ON_HOT(qcs->st == QC_SS_CLO);
|
||||||
@ -410,7 +423,7 @@ static void qcs_idle_open(struct qcs *qcs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Close the local channel of <qcs> instance. */
|
/* Close the local channel of <qcs> instance. */
|
||||||
static void qcs_close_local(struct qcs *qcs)
|
void qcs_close_local(struct qcs *qcs)
|
||||||
{
|
{
|
||||||
TRACE_STATE("closing stream locally", QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs);
|
TRACE_STATE("closing stream locally", QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs);
|
||||||
|
|
||||||
@ -434,7 +447,7 @@ static void qcs_close_local(struct qcs *qcs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Returns true if <qcs> can be purged. */
|
/* Returns true if <qcs> can be purged. */
|
||||||
static int qcs_is_completed(struct qcs *qcs)
|
int qcs_is_completed(struct qcs *qcs)
|
||||||
{
|
{
|
||||||
/* A stream is completed if fully closed and stconn released, or simply
|
/* A stream is completed if fully closed and stconn released, or simply
|
||||||
* detached and everything already sent.
|
* detached and everything already sent.
|
||||||
@ -570,19 +583,36 @@ void qcs_notify_send(struct qcs *qcs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns total number of bytes not already sent to quic-conn layer. */
|
const struct buffer *qcs_tx_buf_const(const struct qcs *qcs)
|
||||||
static uint64_t qcs_prep_bytes(const struct qcs *qcs)
|
|
||||||
{
|
{
|
||||||
struct buffer *out = qc_stream_buf_get(qcs->stream);
|
return conn_is_quic(qcs->qcc->conn) ?
|
||||||
|
qc_stream_buf_get(qcs->tx.stream) : &qcs->tx.qstrm_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct buffer *qcs_tx_buf(struct qcs *qcs)
|
||||||
|
{
|
||||||
|
return conn_is_quic(qcs->qcc->conn) ?
|
||||||
|
qc_stream_buf_get(qcs->tx.stream) : &qcs->tx.qstrm_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns total number of bytes not already sent to quic-conn layer. */
|
||||||
|
uint64_t qcs_prep_bytes(const struct qcs *qcs)
|
||||||
|
{
|
||||||
|
const struct buffer *out = qcs_tx_buf_const(qcs);
|
||||||
uint64_t diff, base_off;
|
uint64_t diff, base_off;
|
||||||
|
|
||||||
if (!out)
|
if (!out)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (conn_is_quic(qcs->qcc->conn)) {
|
||||||
/* if ack_offset < buf_offset, it points to an older buffer. */
|
/* if ack_offset < buf_offset, it points to an older buffer. */
|
||||||
base_off = MAX(qcs->stream->buf_offset, qcs->stream->ack_offset);
|
base_off = MAX(qcs->tx.stream->buf_offset, qcs->tx.stream->ack_offset);
|
||||||
diff = qcs->tx.fc.off_real - base_off;
|
diff = qcs->tx.fc.off_real - base_off;
|
||||||
return b_data(out) - diff;
|
return b_data(out) - diff;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return b_data(out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Used as a callback for qc_stream_desc layer to notify about emission of a
|
/* Used as a callback for qc_stream_desc layer to notify about emission of a
|
||||||
@ -636,8 +666,8 @@ static void qmux_ctrl_send(struct qc_stream_desc *stream, uint64_t data, uint64_
|
|||||||
}
|
}
|
||||||
/* Release buffer if everything sent and buf is full or stream is waiting for room. */
|
/* Release buffer if everything sent and buf is full or stream is waiting for room. */
|
||||||
if (!qcs_prep_bytes(qcs) &&
|
if (!qcs_prep_bytes(qcs) &&
|
||||||
(b_full(&qcs->stream->buf->buf) || qcs->flags & QC_SF_BLK_MROOM)) {
|
(b_full(&qcs->tx.stream->buf->buf) || qcs->flags & QC_SF_BLK_MROOM)) {
|
||||||
qc_stream_buf_release(qcs->stream);
|
qc_stream_buf_release(qcs->tx.stream);
|
||||||
qcs->flags &= ~QC_SF_BLK_MROOM;
|
qcs->flags &= ~QC_SF_BLK_MROOM;
|
||||||
qcs_notify_send(qcs);
|
qcs_notify_send(qcs);
|
||||||
}
|
}
|
||||||
@ -648,7 +678,7 @@ static void qmux_ctrl_send(struct qc_stream_desc *stream, uint64_t data, uint64_
|
|||||||
increment_send_rate(diff, 0);
|
increment_send_rate(diff, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!qc_stream_buf_get(qcs->stream) || !qcs_prep_bytes(qcs)) {
|
if (!qc_stream_buf_get(qcs->tx.stream) || !qcs_prep_bytes(qcs)) {
|
||||||
/* Remove stream from send_list if all was sent. */
|
/* Remove stream from send_list if all was sent. */
|
||||||
LIST_DEL_INIT(&qcs->el_send);
|
LIST_DEL_INIT(&qcs->el_send);
|
||||||
TRACE_STATE("stream sent done", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
TRACE_STATE("stream sent done", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
@ -658,13 +688,13 @@ static void qmux_ctrl_send(struct qc_stream_desc *stream, uint64_t data, uint64_
|
|||||||
qcs_close_local(qcs);
|
qcs_close_local(qcs);
|
||||||
|
|
||||||
if (qcs->flags & QC_SF_FIN_STREAM) {
|
if (qcs->flags & QC_SF_FIN_STREAM) {
|
||||||
qcs->stream->flags |= QC_SD_FL_WAIT_FOR_FIN;
|
qcs->tx.stream->flags |= QC_SD_FL_WAIT_FOR_FIN;
|
||||||
/* Reset flag to not emit multiple FIN STREAM frames. */
|
/* Reset flag to not emit multiple FIN STREAM frames. */
|
||||||
qcs->flags &= ~QC_SF_FIN_STREAM;
|
qcs->flags &= ~QC_SF_FIN_STREAM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unsubscribe from streamdesc when everything sent. */
|
/* Unsubscribe from streamdesc when everything sent. */
|
||||||
qc_stream_desc_sub_send(qcs->stream, NULL);
|
qc_stream_desc_sub_send(qcs->tx.stream, NULL);
|
||||||
|
|
||||||
if (qcs_is_completed(qcs)) {
|
if (qcs_is_completed(qcs)) {
|
||||||
TRACE_STATE("add stream in purg_list", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
TRACE_STATE("add stream in purg_list", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
@ -910,13 +940,16 @@ static struct qcs *qcc_init_stream_remote(struct qcc *qcc, uint64_t id)
|
|||||||
*/
|
*/
|
||||||
void qcs_send_metadata(struct qcs *qcs)
|
void qcs_send_metadata(struct qcs *qcs)
|
||||||
{
|
{
|
||||||
|
if (conn_is_quic(qcs->qcc->conn)) {
|
||||||
/* Reserved for stream with Tx capability. */
|
/* Reserved for stream with Tx capability. */
|
||||||
BUG_ON(!qcs->stream);
|
BUG_ON(!qcs->tx.stream);
|
||||||
/* Cannot use if some data already transferred for this stream. */
|
/* Cannot use if some data already transferred for this stream. */
|
||||||
BUG_ON(qcs->stream->ack_offset || !eb_is_empty(&qcs->stream->buf_tree));
|
BUG_ON(qcs->tx.stream->ack_offset || !eb_is_empty(&qcs->tx.stream->buf_tree));
|
||||||
|
|
||||||
|
qc_stream_desc_sub_room(qcs->tx.stream, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
qcs->flags |= QC_SF_TXBUB_OOB;
|
qcs->flags |= QC_SF_TXBUB_OOB;
|
||||||
qc_stream_desc_sub_room(qcs->stream, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Instantiate a streamdesc instance for <qcs> stream. This is necessary to
|
/* Instantiate a streamdesc instance for <qcs> stream. This is necessary to
|
||||||
@ -1438,7 +1471,7 @@ struct buffer *qcc_get_stream_rxbuf(struct qcs *qcs)
|
|||||||
struct buffer *qcc_get_stream_txbuf(struct qcs *qcs, int *err, int small)
|
struct buffer *qcc_get_stream_txbuf(struct qcs *qcs, int *err, int small)
|
||||||
{
|
{
|
||||||
struct qcc *qcc = qcs->qcc;
|
struct qcc *qcc = qcs->qcc;
|
||||||
struct buffer *out = qc_stream_buf_get(qcs->stream);
|
struct buffer *out = qcs_tx_buf(qcs);
|
||||||
|
|
||||||
/* Stream must not try to reallocate a buffer if currently waiting for one. */
|
/* Stream must not try to reallocate a buffer if currently waiting for one. */
|
||||||
BUG_ON(LIST_INLIST(&qcs->el_buf));
|
BUG_ON(LIST_INLIST(&qcs->el_buf));
|
||||||
@ -1462,7 +1495,7 @@ struct buffer *qcc_get_stream_txbuf(struct qcs *qcs, int *err, int small)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out = qc_stream_buf_alloc(qcs->stream, qcs->tx.fc.off_real, small);
|
out = qc_stream_buf_alloc(qcs->tx.stream, qcs->tx.fc.off_real, small);
|
||||||
if (!out) {
|
if (!out) {
|
||||||
TRACE_ERROR("stream desc alloc failure", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
TRACE_ERROR("stream desc alloc failure", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
*err = 1;
|
*err = 1;
|
||||||
@ -1487,7 +1520,7 @@ struct buffer *qcc_get_stream_txbuf(struct qcs *qcs, int *err, int small)
|
|||||||
struct buffer *qcc_realloc_stream_txbuf(struct qcs *qcs)
|
struct buffer *qcc_realloc_stream_txbuf(struct qcs *qcs)
|
||||||
{
|
{
|
||||||
struct qcc *qcc = qcs->qcc;
|
struct qcc *qcc = qcs->qcc;
|
||||||
struct buffer *out = qc_stream_buf_get(qcs->stream);
|
struct buffer *out = qc_stream_buf_get(qcs->tx.stream);
|
||||||
|
|
||||||
/* Stream must not try to reallocate a buffer if currently waiting for one. */
|
/* Stream must not try to reallocate a buffer if currently waiting for one. */
|
||||||
BUG_ON(LIST_INLIST(&qcs->el_buf));
|
BUG_ON(LIST_INLIST(&qcs->el_buf));
|
||||||
@ -1500,7 +1533,7 @@ struct buffer *qcc_realloc_stream_txbuf(struct qcs *qcs)
|
|||||||
qcc->tx.buf_in_flight -= b_size(out);
|
qcc->tx.buf_in_flight -= b_size(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
out = qc_stream_buf_realloc(qcs->stream);
|
out = qc_stream_buf_realloc(qcs->tx.stream);
|
||||||
if (!out) {
|
if (!out) {
|
||||||
TRACE_ERROR("buffer alloc failure", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
TRACE_ERROR("buffer alloc failure", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
goto out;
|
goto out;
|
||||||
@ -1547,7 +1580,7 @@ int qcc_release_stream_txbuf(struct qcs *qcs)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
qc_stream_buf_release(qcs->stream);
|
qc_stream_buf_release(qcs->tx.stream);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1578,7 +1611,7 @@ static void qcc_clear_frms(struct qcc *qcc)
|
|||||||
TRACE_STATE("resetting STREAM frames list", QMUX_EV_QCC_SEND, qcc->conn);
|
TRACE_STATE("resetting STREAM frames list", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
while (!LIST_ISEMPTY(&qcc->tx.frms)) {
|
while (!LIST_ISEMPTY(&qcc->tx.frms)) {
|
||||||
struct quic_frame *frm = LIST_ELEM(qcc->tx.frms.n, struct quic_frame *, list);
|
struct quic_frame *frm = LIST_ELEM(qcc->tx.frms.n, struct quic_frame *, list);
|
||||||
qc_frm_free(qcc->conn->handle.qc, &frm);
|
qc_frm_free(conn_is_quic(qcc->conn) ? qcc->conn->handle.qc : NULL, &frm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1666,7 +1699,8 @@ void qcc_send_stream(struct qcs *qcs, int urg, int count)
|
|||||||
if (count) {
|
if (count) {
|
||||||
qfctl_sinc(&qcc->tx.fc, count);
|
qfctl_sinc(&qcc->tx.fc, count);
|
||||||
qfctl_sinc(&qcs->tx.fc, count);
|
qfctl_sinc(&qcs->tx.fc, count);
|
||||||
bdata_ctr_add(&qcs->stream->data, count);
|
if (conn_is_quic(qcc->conn))
|
||||||
|
bdata_ctr_add(&qcs->tx.stream->data, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE_LEAVE(QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
TRACE_LEAVE(QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
@ -2468,7 +2502,8 @@ static int qcs_build_stream_frm(struct qcs *qcs, struct buffer *out, char fin,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
frm->stream.stream = qcs->stream;
|
frm->stream.stream =
|
||||||
|
conn_is_quic(qcc->conn) ? (void *)qcs->tx.stream : (void *)qcs;
|
||||||
frm->stream.id = qcs->id;
|
frm->stream.id = qcs->id;
|
||||||
frm->stream.offset = 0;
|
frm->stream.offset = 0;
|
||||||
frm->stream.dup = 0;
|
frm->stream.dup = 0;
|
||||||
@ -2534,24 +2569,13 @@ static int qcc_subscribe_send(struct qcc *qcc)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wrapper for send on transport layer. Send a list of frames <frms> for the
|
static int qcc_quic_send_frames(struct qcc *qcc, struct list *frms, int stream)
|
||||||
* connection <qcc>.
|
|
||||||
*
|
|
||||||
* Returns 0 if all data sent with success. On fatal error, a negative error
|
|
||||||
* code is returned. A positive 1 is used if emission should be paced.
|
|
||||||
*/
|
|
||||||
static int qcc_send_frames(struct qcc *qcc, struct list *frms, int stream)
|
|
||||||
{
|
{
|
||||||
enum quic_tx_err ret;
|
enum quic_tx_err ret;
|
||||||
struct quic_pacer *pacer = NULL;
|
struct quic_pacer *pacer = NULL;
|
||||||
|
|
||||||
TRACE_ENTER(QMUX_EV_QCC_SEND, qcc->conn);
|
TRACE_ENTER(QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
|
||||||
if (LIST_ISEMPTY(frms)) {
|
|
||||||
TRACE_DEVEL("leaving on no frame to send", QMUX_EV_QCC_SEND, qcc->conn);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream && qcc_is_pacing_active(qcc->conn))
|
if (stream && qcc_is_pacing_active(qcc->conn))
|
||||||
pacer = &qcc->tx.pacer;
|
pacer = &qcc->tx.pacer;
|
||||||
|
|
||||||
@ -2579,6 +2603,23 @@ static int qcc_send_frames(struct qcc *qcc, struct list *frms, int stream)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wrapper for send on transport layer. Send a list of frames <frms> for the
|
||||||
|
* connection <qcc>.
|
||||||
|
*
|
||||||
|
* Returns 0 if all data sent with success. On fatal error, a negative error
|
||||||
|
* code is returned. A positive 1 is used if emission should be paced.
|
||||||
|
*/
|
||||||
|
static int qcc_send_frames(struct qcc *qcc, struct list *frms, int stream)
|
||||||
|
{
|
||||||
|
if (LIST_ISEMPTY(frms)) {
|
||||||
|
TRACE_DEVEL("leaving on no frame to send", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn_is_quic(qcc->conn) ? qcc_quic_send_frames(qcc, frms, stream) :
|
||||||
|
qcc_qstrm_send_frames(qcc, frms);
|
||||||
|
}
|
||||||
|
|
||||||
/* Emit a RESET_STREAM on <qcs>.
|
/* Emit a RESET_STREAM on <qcs>.
|
||||||
*
|
*
|
||||||
* Returns 0 if the frame has been successfully sent else non-zero.
|
* Returns 0 if the frame has been successfully sent else non-zero.
|
||||||
@ -2677,7 +2718,7 @@ static int qcs_send_stop_sending(struct qcs *qcs)
|
|||||||
static int qcs_send(struct qcs *qcs, struct list *frms, uint64_t window_conn)
|
static int qcs_send(struct qcs *qcs, struct list *frms, uint64_t window_conn)
|
||||||
{
|
{
|
||||||
struct qcc *qcc = qcs->qcc;
|
struct qcc *qcc = qcs->qcc;
|
||||||
struct buffer *out = qc_stream_buf_get(qcs->stream);
|
struct buffer *out = qcs_tx_buf(qcs);
|
||||||
int flen = 0;
|
int flen = 0;
|
||||||
const char fin = qcs->flags & QC_SF_FIN_STREAM;
|
const char fin = qcs->flags & QC_SF_FIN_STREAM;
|
||||||
|
|
||||||
@ -2779,7 +2820,7 @@ static int qcc_emit_rs_ss(struct qcc *qcc)
|
|||||||
list_for_each_entry_safe(qcs, qcs_tmp, &qcc->send_list, el_send) {
|
list_for_each_entry_safe(qcs, qcs_tmp, &qcc->send_list, el_send) {
|
||||||
/* Stream must not be present in send_list if it has nothing to send. */
|
/* Stream must not be present in send_list if it has nothing to send. */
|
||||||
BUG_ON(!(qcs->flags & (QC_SF_FIN_STREAM|QC_SF_TO_STOP_SENDING|QC_SF_TO_RESET)) &&
|
BUG_ON(!(qcs->flags & (QC_SF_FIN_STREAM|QC_SF_TO_STOP_SENDING|QC_SF_TO_RESET)) &&
|
||||||
(!qcs->stream || !qcs_prep_bytes(qcs)));
|
((conn_is_quic(qcc->conn) && !qcs->tx.stream) || !qcs_prep_bytes(qcs)));
|
||||||
|
|
||||||
/* Interrupt looping for the first stream where no RS nor SS is
|
/* Interrupt looping for the first stream where no RS nor SS is
|
||||||
* necessary and is not use for "metadata" transfer. These
|
* necessary and is not use for "metadata" transfer. These
|
||||||
@ -2805,7 +2846,7 @@ static int qcc_emit_rs_ss(struct qcc *qcc)
|
|||||||
|
|
||||||
/* Remove stream from send_list if only SS was necessary. */
|
/* Remove stream from send_list if only SS was necessary. */
|
||||||
if (!(qcs->flags & (QC_SF_FIN_STREAM|QC_SF_TO_RESET)) &&
|
if (!(qcs->flags & (QC_SF_FIN_STREAM|QC_SF_TO_RESET)) &&
|
||||||
(!qcs->stream || !qcs_prep_bytes(qcs))) {
|
((conn_is_quic(qcc->conn) && !qcs->tx.stream) || !qcs_prep_bytes(qcs))) {
|
||||||
LIST_DEL_INIT(&qcs->el_send);
|
LIST_DEL_INIT(&qcs->el_send);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2874,7 +2915,7 @@ static int qcc_build_frms(struct qcc *qcc, struct list *qcs_failed)
|
|||||||
/* Streams with RS/SS must be handled via qcc_emit_rs_ss(). */
|
/* Streams with RS/SS must be handled via qcc_emit_rs_ss(). */
|
||||||
BUG_ON(qcs->flags & (QC_SF_TO_STOP_SENDING|QC_SF_TO_RESET));
|
BUG_ON(qcs->flags & (QC_SF_TO_STOP_SENDING|QC_SF_TO_RESET));
|
||||||
/* Stream must not be present in send_list if it has nothing to send. */
|
/* Stream must not be present in send_list if it has nothing to send. */
|
||||||
BUG_ON(!(qcs->flags & QC_SF_FIN_STREAM) && (!qcs->stream || !qcs_prep_bytes(qcs)));
|
BUG_ON(!(qcs->flags & QC_SF_FIN_STREAM) && ((conn_is_quic(qcc->conn) && !qcs->tx.stream) || !qcs_prep_bytes(qcs)));
|
||||||
|
|
||||||
/* Total sent bytes must not exceed connection window. */
|
/* Total sent bytes must not exceed connection window. */
|
||||||
BUG_ON(total > window_conn);
|
BUG_ON(total > window_conn);
|
||||||
@ -3039,6 +3080,12 @@ static int qcc_io_send(struct qcc *qcc)
|
|||||||
* flow-control limit reached.
|
* flow-control limit reached.
|
||||||
*/
|
*/
|
||||||
while ((ret = qcc_send_frames(qcc, frms, 1)) == 0 && !qfctl_rblocked(&qcc->tx.fc)) {
|
while ((ret = qcc_send_frames(qcc, frms, 1)) == 0 && !qfctl_rblocked(&qcc->tx.fc)) {
|
||||||
|
/* TODO should this check also be performed for QUIC ? */
|
||||||
|
if (!conn_is_quic(qcc->conn) && (qcc->conn->flags & CO_FL_ERROR)) {
|
||||||
|
TRACE_DEVEL("connection on error", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
window_conn = qfctl_rcap(&qcc->tx.fc);
|
window_conn = qfctl_rcap(&qcc->tx.fc);
|
||||||
resent = 0;
|
resent = 0;
|
||||||
|
|
||||||
@ -3050,7 +3097,8 @@ static int qcc_io_send(struct qcc *qcc)
|
|||||||
* new qc_stream_desc should be present in send_list as
|
* new qc_stream_desc should be present in send_list as
|
||||||
* long as transport layer can handle all data.
|
* long as transport layer can handle all data.
|
||||||
*/
|
*/
|
||||||
BUG_ON(qcs->stream->buf && !qfctl_rblocked(&qcs->tx.fc));
|
BUG_ON((!conn_is_quic(qcc->conn) || qcs->tx.stream->buf) &&
|
||||||
|
!qfctl_rblocked(&qcs->tx.fc));
|
||||||
|
|
||||||
/* Total sent bytes must not exceed connection window. */
|
/* Total sent bytes must not exceed connection window. */
|
||||||
BUG_ON(resent > window_conn);
|
BUG_ON(resent > window_conn);
|
||||||
@ -3150,6 +3198,11 @@ static int qcc_io_recv(struct qcc *qcc)
|
|||||||
if ((qcc->flags & QC_CF_WAIT_HS) && !(qcc->wait_event.events & SUB_RETRY_RECV))
|
if ((qcc->flags & QC_CF_WAIT_HS) && !(qcc->wait_event.events & SUB_RETRY_RECV))
|
||||||
qcc_wait_for_hs(qcc);
|
qcc_wait_for_hs(qcc);
|
||||||
|
|
||||||
|
if (!conn_is_quic(qcc->conn)) {
|
||||||
|
if (!(qcc->wait_event.events & SUB_RETRY_RECV))
|
||||||
|
qcc_qstrm_recv(qcc);
|
||||||
|
}
|
||||||
|
|
||||||
while (!LIST_ISEMPTY(&qcc->recv_list)) {
|
while (!LIST_ISEMPTY(&qcc->recv_list)) {
|
||||||
qcs = LIST_ELEM(qcc->recv_list.n, struct qcs *, el_recv);
|
qcs = LIST_ELEM(qcc->recv_list.n, struct qcs *, el_recv);
|
||||||
/* No need to add an uni local stream in recv_list. */
|
/* No need to add an uni local stream in recv_list. */
|
||||||
@ -3245,6 +3298,7 @@ static void qcc_shutdown(struct qcc *qcc)
|
|||||||
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conn_is_quic(qcc->conn)) {
|
||||||
/* Register "no error" code at transport layer. Do not use
|
/* Register "no error" code at transport layer. Do not use
|
||||||
* quic_set_connection_close() as retransmission may be performed to
|
* quic_set_connection_close() as retransmission may be performed to
|
||||||
* finalized transfers. Do not overwrite quic-conn existing code if
|
* finalized transfers. Do not overwrite quic-conn existing code if
|
||||||
@ -3254,6 +3308,7 @@ static void qcc_shutdown(struct qcc *qcc)
|
|||||||
*/
|
*/
|
||||||
if (!(qcc->conn->handle.qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))
|
if (!(qcc->conn->handle.qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))
|
||||||
qcc->conn->handle.qc->err = qcc->err;
|
qcc->conn->handle.qc->err = qcc->err;
|
||||||
|
}
|
||||||
|
|
||||||
/* A connection is not reusable if app layer is closed. */
|
/* A connection is not reusable if app layer is closed. */
|
||||||
if (qcc->flags & QC_CF_IS_BACK)
|
if (qcc->flags & QC_CF_IS_BACK)
|
||||||
@ -3311,7 +3366,7 @@ static int qcc_io_process(struct qcc *qcc)
|
|||||||
/* If using listener socket, soft-stop is not supported. The
|
/* If using listener socket, soft-stop is not supported. The
|
||||||
* connection must be closed immediately.
|
* connection must be closed immediately.
|
||||||
*/
|
*/
|
||||||
if (!qc_test_fd(qcc->conn->handle.qc)) {
|
if (conn_is_quic(qcc->conn) && !qc_test_fd(qcc->conn->handle.qc)) {
|
||||||
TRACE_DEVEL("proxy disabled with listener socket, closing connection", QMUX_EV_QCC_WAKE, qcc->conn);
|
TRACE_DEVEL("proxy disabled with listener socket, closing connection", QMUX_EV_QCC_WAKE, qcc->conn);
|
||||||
qcc->conn->flags |= (CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH);
|
qcc->conn->flags |= (CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH);
|
||||||
qcc_io_send(qcc);
|
qcc_io_send(qcc);
|
||||||
@ -3375,7 +3430,7 @@ static void qcc_release(struct qcc *qcc)
|
|||||||
qcs_free(qcs);
|
qcs_free(qcs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn) {
|
if (conn && conn_is_quic(conn)) {
|
||||||
qc = conn->handle.qc;
|
qc = conn->handle.qc;
|
||||||
|
|
||||||
/* unsubscribe from all remaining qc_stream_desc */
|
/* unsubscribe from all remaining qc_stream_desc */
|
||||||
@ -3408,7 +3463,7 @@ static void qcc_release(struct qcc *qcc)
|
|||||||
if (qcc->app_ops) {
|
if (qcc->app_ops) {
|
||||||
if (qcc->app_ops->release)
|
if (qcc->app_ops->release)
|
||||||
qcc->app_ops->release(qcc->ctx);
|
qcc->app_ops->release(qcc->ctx);
|
||||||
if (conn->handle.qc)
|
if (conn_is_quic(conn) && conn->handle.qc)
|
||||||
conn->handle.qc->strm_reject = qcc->app_ops->strm_reject;
|
conn->handle.qc->strm_reject = qcc->app_ops->strm_reject;
|
||||||
}
|
}
|
||||||
TRACE_PROTO("application layer released", QMUX_EV_QCC_END, conn);
|
TRACE_PROTO("application layer released", QMUX_EV_QCC_END, conn);
|
||||||
@ -3647,7 +3702,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
struct session *sess, struct buffer *input)
|
struct session *sess, struct buffer *input)
|
||||||
{
|
{
|
||||||
struct qcc *qcc;
|
struct qcc *qcc;
|
||||||
struct quic_transport_params *lparams, *rparams;
|
const struct quic_transport_params *lparams, *rparams;
|
||||||
void *conn_ctx = conn->ctx;
|
void *conn_ctx = conn->ctx;
|
||||||
|
|
||||||
TRACE_ENTER(QMUX_EV_QCC_NEW);
|
TRACE_ENTER(QMUX_EV_QCC_NEW);
|
||||||
@ -3666,6 +3721,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
qcc->glitches = 0;
|
qcc->glitches = 0;
|
||||||
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
||||||
|
|
||||||
|
if (conn_is_quic(conn)) {
|
||||||
/* Server parameters, params used for RX flow control. */
|
/* Server parameters, params used for RX flow control. */
|
||||||
lparams = &conn->handle.qc->rx.params;
|
lparams = &conn->handle.qc->rx.params;
|
||||||
|
|
||||||
@ -3686,6 +3742,28 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
qcc->rfctl.msd_bidi_l = rparams->initial_max_stream_data_bidi_local;
|
qcc->rfctl.msd_bidi_l = rparams->initial_max_stream_data_bidi_local;
|
||||||
qcc->rfctl.msd_bidi_r = rparams->initial_max_stream_data_bidi_remote;
|
qcc->rfctl.msd_bidi_r = rparams->initial_max_stream_data_bidi_remote;
|
||||||
qcc->rfctl.msd_uni_l = rparams->initial_max_stream_data_uni;
|
qcc->rfctl.msd_uni_l = rparams->initial_max_stream_data_uni;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rparams = xprt_qstrm_rparams(conn->xprt_ctx);
|
||||||
|
qfctl_init(&qcc->tx.fc, rparams->initial_max_data);
|
||||||
|
|
||||||
|
qcc->rfctl.ms_uni = rparams->initial_max_streams_uni;
|
||||||
|
qcc->rfctl.ms_bidi = rparams->initial_max_streams_bidi;
|
||||||
|
qcc->rfctl.msd_bidi_l = rparams->initial_max_stream_data_bidi_local;
|
||||||
|
qcc->rfctl.msd_bidi_r = rparams->initial_max_stream_data_bidi_remote;
|
||||||
|
qcc->rfctl.msd_uni_l = rparams->initial_max_stream_data_uni;
|
||||||
|
|
||||||
|
/* TODO */
|
||||||
|
qcc->lfctl.ms_bidi = qcc->lfctl.ms_bidi_init = 16384;
|
||||||
|
qcc->lfctl.ms_uni = 3;
|
||||||
|
qcc->lfctl.msd_bidi_l = 16384;
|
||||||
|
qcc->lfctl.msd_bidi_r = 16384;
|
||||||
|
qcc->lfctl.msd_uni_r = 16384;
|
||||||
|
qcc->lfctl.cl_bidi_r = 0;
|
||||||
|
|
||||||
|
qcc->lfctl.md = qcc->lfctl.md_init = 16384;
|
||||||
|
qcc->lfctl.offsets_recv = qcc->lfctl.offsets_consume = 0;
|
||||||
|
}
|
||||||
|
|
||||||
qcc->tx.buf_in_flight = 0;
|
qcc->tx.buf_in_flight = 0;
|
||||||
|
|
||||||
@ -3705,6 +3783,22 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
qcc->pacing_task->state |= TASK_F_WANTS_TIME;
|
qcc->pacing_task->state |= TASK_F_WANTS_TIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!conn_is_quic(conn)) {
|
||||||
|
qcc->tx.qstrm_buf = BUF_NULL;
|
||||||
|
b_alloc(&qcc->tx.qstrm_buf, DB_MUX_TX);
|
||||||
|
if (!b_size(&qcc->tx.qstrm_buf)) {
|
||||||
|
TRACE_ERROR("tx qstrm buf alloc failure", QMUX_EV_QCC_NEW);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
qcc->rx.qstrm_buf = BUF_NULL;
|
||||||
|
b_alloc(&qcc->rx.qstrm_buf, DB_MUX_RX);
|
||||||
|
if (!b_size(&qcc->rx.qstrm_buf)) {
|
||||||
|
TRACE_ERROR("rx qstrm buf alloc failure", QMUX_EV_QCC_NEW);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (conn_is_back(conn)) {
|
if (conn_is_back(conn)) {
|
||||||
qcc->next_bidi_l = 0x00;
|
qcc->next_bidi_l = 0x00;
|
||||||
qcc->largest_bidi_r = 0x01;
|
qcc->largest_bidi_r = 0x01;
|
||||||
@ -3763,6 +3857,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
qcc_reset_idle_start(qcc);
|
qcc_reset_idle_start(qcc);
|
||||||
LIST_INIT(&qcc->opening_list);
|
LIST_INIT(&qcc->opening_list);
|
||||||
|
|
||||||
|
if (conn_is_quic(conn))
|
||||||
HA_ATOMIC_STORE(&conn->handle.qc->qcc, qcc);
|
HA_ATOMIC_STORE(&conn->handle.qc->qcc, qcc);
|
||||||
|
|
||||||
/* Register conn as app_ops may use it. */
|
/* Register conn as app_ops may use it. */
|
||||||
@ -3783,6 +3878,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
/* init read cycle */
|
/* init read cycle */
|
||||||
tasklet_wakeup(qcc->wait_event.tasklet);
|
tasklet_wakeup(qcc->wait_event.tasklet);
|
||||||
|
|
||||||
|
if (conn_is_quic(conn)) {
|
||||||
/* MUX is initialized before QUIC handshake completion if early data
|
/* MUX is initialized before QUIC handshake completion if early data
|
||||||
* received. Flag connection to delay stream processing if
|
* received. Flag connection to delay stream processing if
|
||||||
* wait-for-handshake is active.
|
* wait-for-handshake is active.
|
||||||
@ -3798,6 +3894,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
/* Initiate backend side transfer by creating the first
|
/* Initiate backend side transfer by creating the first
|
||||||
* bidirectional stream. MUX will then be woken up on QUIC
|
* bidirectional stream. MUX will then be woken up on QUIC
|
||||||
@ -3838,12 +3935,14 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
if (conn_is_quic(conn)) {
|
||||||
/* Prepare CONNECTION_CLOSE, using INTERNAL_ERROR as fallback code if unset. */
|
/* Prepare CONNECTION_CLOSE, using INTERNAL_ERROR as fallback code if unset. */
|
||||||
if (!(conn->handle.qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE)) {
|
if (!(conn->handle.qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE)) {
|
||||||
struct quic_err err = qcc && qcc->err.code ?
|
struct quic_err err = qcc && qcc->err.code ?
|
||||||
qcc->err : quic_err_transport(QC_ERR_INTERNAL_ERROR);
|
qcc->err : quic_err_transport(QC_ERR_INTERNAL_ERROR);
|
||||||
quic_set_connection_close(conn->handle.qc, err);
|
quic_set_connection_close(conn->handle.qc, err);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (qcc) {
|
if (qcc) {
|
||||||
/* In case of MUX init failure, session will ensure connection is freed. */
|
/* In case of MUX init failure, session will ensure connection is freed. */
|
||||||
@ -4556,8 +4655,8 @@ void qcc_show_quic(struct qcc *qcc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!quic_stream_is_uni(qcs->id) || !quic_stream_is_remote(qcc, qcs->id)) {
|
if (!quic_stream_is_uni(qcs->id) || !quic_stream_is_remote(qcc, qcs->id)) {
|
||||||
if (qcs->stream)
|
if (qcs->tx.stream)
|
||||||
bdata_ctr_print(&trash, &qcs->stream->data, "txb=");
|
bdata_ctr_print(&trash, &qcs->tx.stream->data, "txb=");
|
||||||
chunk_appendf(&trash, " txoff=%llu(%llu) msd=%llu",
|
chunk_appendf(&trash, " txoff=%llu(%llu) msd=%llu",
|
||||||
(ullong)qcs->tx.fc.off_real,
|
(ullong)qcs->tx.fc.off_real,
|
||||||
(ullong)qcs->tx.fc.off_soft - (ullong)qcs->tx.fc.off_real,
|
(ullong)qcs->tx.fc.off_soft - (ullong)qcs->tx.fc.off_real,
|
||||||
@ -4572,3 +4671,32 @@ static struct mux_proto_list mux_proto_quic =
|
|||||||
{ .token = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &qmux_ops };
|
{ .token = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &qmux_ops };
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_quic);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_quic);
|
||||||
|
|
||||||
|
static const struct mux_ops qstrm_ops = {
|
||||||
|
.init = qmux_init,
|
||||||
|
.destroy = qmux_destroy,
|
||||||
|
.detach = qmux_strm_detach,
|
||||||
|
.rcv_buf = qmux_strm_rcv_buf,
|
||||||
|
.snd_buf = qmux_strm_snd_buf,
|
||||||
|
.nego_fastfwd = qmux_strm_nego_ff,
|
||||||
|
.done_fastfwd = qmux_strm_done_ff,
|
||||||
|
.resume_fastfwd = qmux_strm_resume_ff,
|
||||||
|
.subscribe = qmux_strm_subscribe,
|
||||||
|
.unsubscribe = qmux_strm_unsubscribe,
|
||||||
|
.wake = qmux_wake,
|
||||||
|
.avail_streams = qmux_avail_streams,
|
||||||
|
.used_streams = qmux_used_streams,
|
||||||
|
.takeover = NULL, /* QUIC takeover support not implemented yet */
|
||||||
|
.attach = qmux_strm_attach,
|
||||||
|
.shut = qmux_strm_shut,
|
||||||
|
.ctl = qmux_ctl,
|
||||||
|
.sctl = qmux_sctl,
|
||||||
|
.show_sd = qmux_strm_show_sd,
|
||||||
|
.flags = MX_FL_HTX|MX_FL_NO_UPG|MX_FL_EXPERIMENTAL,
|
||||||
|
.name = "QMUX",
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mux_proto_list mux_proto_qstrm =
|
||||||
|
{ .token = IST("qmux"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &qstrm_ops };
|
||||||
|
|
||||||
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_qstrm);
|
||||||
|
|||||||
317
src/mux_quic_qstrm.c
Normal file
317
src/mux_quic_qstrm.c
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
#include <haproxy/mux_quic_qstrm.h>
|
||||||
|
|
||||||
|
#include <haproxy/api.h>
|
||||||
|
#include <haproxy/buf.h>
|
||||||
|
#include <haproxy/chunk.h>
|
||||||
|
#include <haproxy/connection.h>
|
||||||
|
#include <haproxy/mux_quic.h>
|
||||||
|
#include <haproxy/mux_quic_priv.h>
|
||||||
|
#include <haproxy/proxy.h>
|
||||||
|
#include <haproxy/qmux_trace.h>
|
||||||
|
#include <haproxy/quic_fctl.h>
|
||||||
|
#include <haproxy/quic_frame.h>
|
||||||
|
#include <haproxy/trace.h>
|
||||||
|
|
||||||
|
/* Returns true if <frm> type can be used for QMux protocol. */
|
||||||
|
static int qstrm_is_frm_valid(const struct quic_frame *frm)
|
||||||
|
{
|
||||||
|
return frm->type == QUIC_FT_PADDING ||
|
||||||
|
frm->type == QUIC_FT_RESET_STREAM ||
|
||||||
|
frm->type == QUIC_FT_STOP_SENDING ||
|
||||||
|
(frm->type >= QUIC_FT_STREAM_8 && frm->type <= QUIC_FT_STREAM_F) ||
|
||||||
|
frm->type == QUIC_FT_MAX_DATA ||
|
||||||
|
frm->type == QUIC_FT_MAX_STREAM_DATA ||
|
||||||
|
frm->type == QUIC_FT_MAX_STREAMS_BIDI ||
|
||||||
|
frm->type == QUIC_FT_MAX_STREAMS_UNI ||
|
||||||
|
frm->type == QUIC_FT_DATA_BLOCKED ||
|
||||||
|
frm->type == QUIC_FT_STREAM_DATA_BLOCKED ||
|
||||||
|
frm->type == QUIC_FT_STREAMS_BLOCKED_BIDI ||
|
||||||
|
frm->type == QUIC_FT_STREAMS_BLOCKED_UNI ||
|
||||||
|
frm->type == QUIC_FT_CONNECTION_CLOSE ||
|
||||||
|
frm->type == QUIC_FT_CONNECTION_CLOSE_APP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse the next frame in <buf> and handle it by the MUX layer.
|
||||||
|
*
|
||||||
|
* Returns the frame length on success. If frame is truncated, 0 is returned.
|
||||||
|
* A negative error code is used for fatal failures.
|
||||||
|
*/
|
||||||
|
static int qstrm_parse_frm(struct qcc *qcc, struct buffer *buf)
|
||||||
|
{
|
||||||
|
struct quic_frame frm;
|
||||||
|
const unsigned char *pos, *old, *end;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
old = pos = (unsigned char *)b_head(buf);
|
||||||
|
end = (unsigned char *)b_head(buf) + b_data(buf);
|
||||||
|
ret = qc_parse_frm_type(&frm, &pos, end, NULL);
|
||||||
|
if (!ret)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!qstrm_is_frm_valid(&frm)) {
|
||||||
|
/* TODO close connection with FRAME_ENCODING_ERROR */
|
||||||
|
b_reset(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qc_parse_frm_payload(&frm, &pos, end, NULL);
|
||||||
|
if (!ret)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (frm.type >= QUIC_FT_STREAM_8 &&
|
||||||
|
frm.type <= QUIC_FT_STREAM_F) {
|
||||||
|
struct qf_stream *strm_frm = &frm.stream;
|
||||||
|
|
||||||
|
qcc_recv(qcc, strm_frm->id, strm_frm->len, strm_frm->offset,
|
||||||
|
(frm.type & QUIC_STREAM_FRAME_TYPE_FIN_BIT), (char *)strm_frm->data);
|
||||||
|
}
|
||||||
|
else if (frm.type == QUIC_FT_RESET_STREAM) {
|
||||||
|
struct qf_reset_stream *rst_frm = &frm.reset_stream;
|
||||||
|
qcc_recv_reset_stream(qcc, rst_frm->id, rst_frm->app_error_code, rst_frm->final_size);
|
||||||
|
}
|
||||||
|
else if (frm.type == QUIC_FT_MAX_DATA) {
|
||||||
|
struct qf_max_data *md_frm = &frm.max_data;
|
||||||
|
qcc_recv_max_data(qcc, md_frm->max_data);
|
||||||
|
}
|
||||||
|
else if (frm.type == QUIC_FT_MAX_STREAM_DATA) {
|
||||||
|
struct qf_max_stream_data *msd_frm = &frm.max_stream_data;
|
||||||
|
qcc_recv_max_stream_data(qcc, msd_frm->id, msd_frm->max_stream_data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ABORT_NOW();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos - old;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform data reception for <qcc> connection. Content is parsed as QMux
|
||||||
|
* frames. These operations are performed in loop until read returns no data.
|
||||||
|
*
|
||||||
|
* Returns the total amount of read data or -1 on error.
|
||||||
|
*/
|
||||||
|
int qcc_qstrm_recv(struct qcc *qcc)
|
||||||
|
{
|
||||||
|
struct connection *conn = qcc->conn;
|
||||||
|
struct buffer *buf = &qcc->rx.qstrm_buf;
|
||||||
|
int total = 0, frm_ret;
|
||||||
|
size_t ret;
|
||||||
|
|
||||||
|
TRACE_ENTER(QMUX_EV_QCC_RECV, qcc->conn);
|
||||||
|
|
||||||
|
do {
|
||||||
|
recv:
|
||||||
|
/* Wrapping is not supported for QMux reception. */
|
||||||
|
BUG_ON(b_data(buf) != b_contig_data(buf, 0));
|
||||||
|
|
||||||
|
/* Checks if there is no more room before wrapping position. */
|
||||||
|
if (b_head(buf) + b_contig_data(buf, 0) == b_wrap(buf)) {
|
||||||
|
if (!b_room(buf)) {
|
||||||
|
/* TODO frame bigger than buffer, connection must be closed */
|
||||||
|
ABORT_NOW();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Realign data in the buffer to have more room. */
|
||||||
|
memmove(b_orig(buf), b_head(buf), b_data(buf));
|
||||||
|
buf->head = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Ensure maximum room is always available. */
|
||||||
|
b_realign_if_empty(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = conn->xprt->rcv_buf(conn, conn->xprt_ctx, buf, b_contig_space(buf), NULL, 0, 0);
|
||||||
|
BUG_ON(conn->flags & CO_FL_ERROR);
|
||||||
|
|
||||||
|
total += ret;
|
||||||
|
while (b_data(buf)) {
|
||||||
|
frm_ret = qstrm_parse_frm(qcc, buf);
|
||||||
|
|
||||||
|
BUG_ON(frm_ret < 0); /* TODO handle fatal errors */
|
||||||
|
if (!frm_ret) {
|
||||||
|
/* Checks if wrapping position is reached, requires realign. */
|
||||||
|
if (b_head(buf) + b_contig_data(buf, 0) == b_wrap(buf))
|
||||||
|
goto recv;
|
||||||
|
/* Truncated frame read but room still left, subscribe to retry later. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_del(buf, frm_ret);
|
||||||
|
}
|
||||||
|
} while (ret > 0);
|
||||||
|
|
||||||
|
if (!conn_xprt_read0_pending(qcc->conn)) {
|
||||||
|
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
|
||||||
|
&qcc->wait_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);
|
||||||
|
return total;
|
||||||
|
|
||||||
|
err:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Updates a <qcs> stream after a successful emission of data of length <data>. */
|
||||||
|
static void qstrm_ctrl_send(struct qcs *qcs, uint64_t data)
|
||||||
|
{
|
||||||
|
struct qcc *qcc = qcs->qcc;
|
||||||
|
struct quic_fctl *fc_conn = &qcc->tx.fc;
|
||||||
|
struct quic_fctl *fc_strm = &qcs->tx.fc;
|
||||||
|
|
||||||
|
TRACE_ENTER(QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
|
|
||||||
|
qcs_idle_open(qcs);
|
||||||
|
|
||||||
|
/* Ensure real offset never exceeds soft value. */
|
||||||
|
BUG_ON(fc_conn->off_real + data > fc_conn->off_soft);
|
||||||
|
BUG_ON(fc_strm->off_real + data > fc_strm->off_soft);
|
||||||
|
|
||||||
|
/* increase offset on connection */
|
||||||
|
if (qfctl_rinc(fc_conn, data)) {
|
||||||
|
TRACE_STATE("connection flow-control reached",
|
||||||
|
QMUX_EV_QCS_SEND, qcc->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* increase offset on stream */
|
||||||
|
if (qfctl_rinc(fc_strm, data)) {
|
||||||
|
TRACE_STATE("stream flow-control reached",
|
||||||
|
QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
b_del(&qcs->tx.qstrm_buf, data);
|
||||||
|
/* Release buffer if everything sent and stream is waiting for room. */
|
||||||
|
if (!qcs_prep_bytes(qcs) && (qcs->flags & QC_SF_BLK_MROOM)) {
|
||||||
|
qcs->flags &= ~QC_SF_BLK_MROOM;
|
||||||
|
qcs_notify_send(qcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add measurement for send rate. This is done at the MUX layer
|
||||||
|
* to account only for STREAM frames without retransmission.
|
||||||
|
*/
|
||||||
|
increment_send_rate(data, 0);
|
||||||
|
|
||||||
|
if (!qcs_prep_bytes(qcs)) {
|
||||||
|
/* Remove stream from send_list if all was sent. */
|
||||||
|
LIST_DEL_INIT(&qcs->el_send);
|
||||||
|
TRACE_STATE("stream sent done", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
|
|
||||||
|
if (qcs->flags & (QC_SF_FIN_STREAM|QC_SF_DETACH)) {
|
||||||
|
/* Close stream locally. */
|
||||||
|
qcs_close_local(qcs);
|
||||||
|
|
||||||
|
if (qcs->flags & QC_SF_FIN_STREAM) {
|
||||||
|
/* Reset flag to not emit multiple FIN STREAM frames. */
|
||||||
|
qcs->flags &= ~QC_SF_FIN_STREAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qcs_is_completed(qcs)) {
|
||||||
|
TRACE_STATE("add stream in purg_list", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
|
LIST_APPEND(&qcc->purg_list, &qcs->el_send);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sends <frms> list of frames for <qcc> connection.
|
||||||
|
*
|
||||||
|
* Returns 0 if all data are emitted or a positive value if sending should be
|
||||||
|
* retry later. A negative error code is used for a fatal failure.
|
||||||
|
*/
|
||||||
|
int qcc_qstrm_send_frames(struct qcc *qcc, struct list *frms)
|
||||||
|
{
|
||||||
|
struct connection *conn = qcc->conn;
|
||||||
|
struct quic_frame *frm, *frm_old;
|
||||||
|
struct quic_frame *split_frm, *next_frm;
|
||||||
|
struct buffer *buf = &qcc->tx.qstrm_buf;
|
||||||
|
unsigned char *pos, *old, *end;
|
||||||
|
size_t ret;
|
||||||
|
|
||||||
|
TRACE_ENTER(QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
|
||||||
|
if (b_data(buf)) {
|
||||||
|
ret = conn->xprt->snd_buf(conn, conn->xprt_ctx, buf, b_data(buf), NULL, 0, 0);
|
||||||
|
if (!ret) {
|
||||||
|
TRACE_DEVEL("snd_buf interrupted", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != b_data(buf)) {
|
||||||
|
/* TODO */
|
||||||
|
ABORT_NOW();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b_reset(buf);
|
||||||
|
list_for_each_entry_safe(frm, frm_old, frms, list) {
|
||||||
|
loop:
|
||||||
|
split_frm = next_frm = NULL;
|
||||||
|
b_reset(buf);
|
||||||
|
old = pos = (unsigned char *)b_orig(buf);
|
||||||
|
end = (unsigned char *)b_wrap(buf);
|
||||||
|
|
||||||
|
BUG_ON(!frm);
|
||||||
|
TRACE_PRINTF(TRACE_LEVEL_DEVELOPER, QMUX_EV_QCC_SEND, qcc->conn, 0, 0, 0,
|
||||||
|
"frm type %02llx", (ullong)frm->type);
|
||||||
|
|
||||||
|
if (frm->type >= QUIC_FT_STREAM_8 && frm->type <= QUIC_FT_STREAM_F) {
|
||||||
|
size_t flen, split_size;
|
||||||
|
|
||||||
|
flen = quic_strm_frm_fillbuf(end - pos, frm, &split_size);
|
||||||
|
if (!flen)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (split_size) {
|
||||||
|
split_frm = quic_strm_frm_split(frm, split_size);
|
||||||
|
if (!split_frm) {
|
||||||
|
ABORT_NOW();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_frm = frm;
|
||||||
|
frm = split_frm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qc_build_frm(frm, &pos, end, NULL);
|
||||||
|
BUG_ON(pos - old > global.tune.bufsize);
|
||||||
|
BUG_ON(pos == old);
|
||||||
|
b_add(buf, pos - old);
|
||||||
|
|
||||||
|
ret = conn->xprt->snd_buf(conn, conn->xprt_ctx, buf, b_data(buf), NULL, 0, 0);
|
||||||
|
if (!ret) {
|
||||||
|
TRACE_DEVEL("snd_buf interrupted", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
if (split_frm)
|
||||||
|
LIST_INSERT(frms, &split_frm->list);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != b_data(buf)) {
|
||||||
|
/* TODO */
|
||||||
|
ABORT_NOW();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frm->type >= QUIC_FT_STREAM_8 && frm->type <= QUIC_FT_STREAM_F)
|
||||||
|
qstrm_ctrl_send(frm->stream.stream, frm->stream.len);
|
||||||
|
|
||||||
|
LIST_DEL_INIT(&frm->list);
|
||||||
|
if (split_frm) {
|
||||||
|
frm = next_frm;
|
||||||
|
goto loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (conn->flags & CO_FL_ERROR) {
|
||||||
|
/* TODO */
|
||||||
|
//ABORT_NOW();
|
||||||
|
}
|
||||||
|
else if (!LIST_ISEMPTY(frms) && !(qcc->wait_event.events & SUB_RETRY_SEND)) {
|
||||||
|
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_SEND, &qcc->wait_event);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@ -776,7 +776,7 @@ static int sample_conv_ip_fp(const struct arg *arg_p, struct sample *smp, void *
|
|||||||
/* kind1 = NOP and is a single byte, others have a length field */
|
/* kind1 = NOP and is a single byte, others have a length field */
|
||||||
if (smp->data.u.str.area[ofs] == 1)
|
if (smp->data.u.str.area[ofs] == 1)
|
||||||
next = ofs + 1;
|
next = ofs + 1;
|
||||||
else if (ofs + 1 <= tcplen)
|
else if (ofs + 1 < tcplen)
|
||||||
next = ofs + smp->data.u.str.area[ofs + 1];
|
next = ofs + smp->data.u.str.area[ofs + 1];
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
@ -790,10 +790,10 @@ static int sample_conv_ip_fp(const struct arg *arg_p, struct sample *smp, void *
|
|||||||
if (mode & 2) // mode & 2: append tcp.options_list
|
if (mode & 2) // mode & 2: append tcp.options_list
|
||||||
trash->area[trash->data++] = opt;
|
trash->area[trash->data++] = opt;
|
||||||
|
|
||||||
if (opt == 2 /* MSS */) {
|
if (opt == 2 && (ofs + 3 < tcplen) /* MSS value starts at ofs + 2 and is 2 Bytes long */) {
|
||||||
tcpmss = read_n16(smp->data.u.str.area + ofs + 2);
|
tcpmss = read_n16(smp->data.u.str.area + ofs + 2);
|
||||||
}
|
}
|
||||||
else if (opt == 3 /* WS */) {
|
else if (opt == 3 && (ofs + 2 < tcplen) /* WS value 1 Byte is at ofs + 2 */) {
|
||||||
tcpws = (uchar)smp->data.u.str.area[ofs + 2];
|
tcpws = (uchar)smp->data.u.str.area[ofs + 2];
|
||||||
/* output from 1 to 15, thus 0=not found */
|
/* output from 1 to 15, thus 0=not found */
|
||||||
tcpws = tcpws > 14 ? 15 : tcpws + 1;
|
tcpws = tcpws > 14 ? 15 : tcpws + 1;
|
||||||
@ -813,7 +813,7 @@ static int sample_conv_ip_fp(const struct arg *arg_p, struct sample *smp, void *
|
|||||||
write_n16(trash->area + 3, tcpwin);
|
write_n16(trash->area + 3, tcpwin);
|
||||||
write_n16(trash->area + 5, tcpmss);
|
write_n16(trash->area + 5, tcpmss);
|
||||||
|
|
||||||
/* the the bit mask of present options */
|
/* then the bit mask of present options */
|
||||||
trash->area[7] = opts;
|
trash->area[7] = opts;
|
||||||
|
|
||||||
/* mode 4: append source IP address */
|
/* mode 4: append source IP address */
|
||||||
|
|||||||
65
src/proxy.c
65
src/proxy.c
@ -1577,7 +1577,7 @@ void init_new_proxy(struct proxy *p)
|
|||||||
LIST_INIT(&p->conf.args.list);
|
LIST_INIT(&p->conf.args.list);
|
||||||
LIST_INIT(&p->conf.lf_checks);
|
LIST_INIT(&p->conf.lf_checks);
|
||||||
LIST_INIT(&p->filter_configs);
|
LIST_INIT(&p->filter_configs);
|
||||||
LIST_INIT(&p->tcpcheck_rules.preset_vars);
|
LIST_INIT(&p->tcpcheck.preset_vars);
|
||||||
|
|
||||||
MT_LIST_INIT(&p->lbprm.lb_free_list);
|
MT_LIST_INIT(&p->lbprm.lb_free_list);
|
||||||
|
|
||||||
@ -1676,11 +1676,17 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bind_conf->mux_proto) {
|
if (bind_conf->mux_proto) {
|
||||||
|
int is_quic;
|
||||||
|
|
||||||
|
if ((bind_conf->options & (BC_O_USE_SOCK_DGRAM | BC_O_USE_XPRT_STREAM)) == (BC_O_USE_SOCK_DGRAM | BC_O_USE_XPRT_STREAM))
|
||||||
|
is_quic = 1;
|
||||||
|
else
|
||||||
|
is_quic = 0;
|
||||||
/* it is possible that an incorrect mux was referenced
|
/* it is possible that an incorrect mux was referenced
|
||||||
* due to the proxy's mode not being taken into account
|
* due to the proxy's mode not being taken into account
|
||||||
* on first pass. Let's adjust it now.
|
* on first pass. Let's adjust it now.
|
||||||
*/
|
*/
|
||||||
mux_ent = conn_get_best_mux_entry(bind_conf->mux_proto->token, PROTO_SIDE_FE, mode);
|
mux_ent = conn_get_best_mux_entry(bind_conf->mux_proto->token, PROTO_SIDE_FE, is_quic, mode);
|
||||||
|
|
||||||
if (!mux_ent || !isteq(mux_ent->token, bind_conf->mux_proto->token)) {
|
if (!mux_ent || !isteq(mux_ent->token, bind_conf->mux_proto->token)) {
|
||||||
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for 'bind %s' at [%s:%d].\n",
|
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for 'bind %s' at [%s:%d].\n",
|
||||||
@ -1870,28 +1876,12 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
|||||||
else if (px->options & PR_O_TRANSP)
|
else if (px->options & PR_O_TRANSP)
|
||||||
px->options &= ~PR_O_DISPATCH;
|
px->options &= ~PR_O_DISPATCH;
|
||||||
|
|
||||||
if ((px->tcpcheck_rules.flags & TCPCHK_RULES_UNUSED_HTTP_RS)) {
|
if ((px->tcpcheck.flags & TCPCHK_FL_UNUSED_HTTP_RS)) {
|
||||||
ha_warning("%s '%s' uses http-check rules without 'option httpchk', so the rules are ignored.\n",
|
ha_warning("%s '%s' uses http-check rules without 'option httpchk', so the rules are ignored.\n",
|
||||||
proxy_type_str(px), px->id);
|
proxy_type_str(px), px->id);
|
||||||
*err_code |= ERR_WARN;
|
*err_code |= ERR_WARN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
|
|
||||||
(px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_HTTP_CHK) {
|
|
||||||
if (px->options & PR_O_DISABLE404) {
|
|
||||||
ha_warning("'%s' will be ignored for %s '%s' (requires 'option httpchk').\n",
|
|
||||||
"disable-on-404", proxy_type_str(px), px->id);
|
|
||||||
*err_code |= ERR_WARN;
|
|
||||||
px->options &= ~PR_O_DISABLE404;
|
|
||||||
}
|
|
||||||
if (px->options2 & PR_O2_CHK_SNDST) {
|
|
||||||
ha_warning("'%s' will be ignored for %s '%s' (requires 'option httpchk').\n",
|
|
||||||
"send-state", proxy_type_str(px), px->id);
|
|
||||||
*err_code |= ERR_WARN;
|
|
||||||
px->options2 &= ~PR_O2_CHK_SNDST;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
|
if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
|
||||||
if (!global.external_check) {
|
if (!global.external_check) {
|
||||||
ha_alert("Proxy '%s' : '%s' unable to find required 'global.external-check'.\n",
|
ha_alert("Proxy '%s' : '%s' unable to find required 'global.external-check'.\n",
|
||||||
@ -2437,7 +2427,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
|||||||
if ((px->cap & PR_CAP_BE) && !px->timeout.queue)
|
if ((px->cap & PR_CAP_BE) && !px->timeout.queue)
|
||||||
px->timeout.queue = px->timeout.connect;
|
px->timeout.queue = px->timeout.connect;
|
||||||
|
|
||||||
if ((px->tcpcheck_rules.flags & TCPCHK_RULES_UNUSED_TCP_RS)) {
|
if (px->tcpcheck.flags & TCPCHK_FL_UNUSED_TCP_RS) {
|
||||||
ha_warning("%s '%s' uses tcp-check rules without 'option tcp-check', so the rules are ignored.\n",
|
ha_warning("%s '%s' uses tcp-check rules without 'option tcp-check', so the rules are ignored.\n",
|
||||||
proxy_type_str(px), px->id);
|
proxy_type_str(px), px->id);
|
||||||
*err_code |= ERR_WARN;
|
*err_code |= ERR_WARN;
|
||||||
@ -2547,12 +2537,16 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
|||||||
|
|
||||||
srv_minmax_conn_apply(newsrv);
|
srv_minmax_conn_apply(newsrv);
|
||||||
|
|
||||||
|
*err_code |= check_server_tcpcheck(newsrv);
|
||||||
|
if (*err_code & (ERR_ABORT|ERR_FATAL))
|
||||||
|
goto out;
|
||||||
|
|
||||||
/* this will also properly set the transport layer for
|
/* this will also properly set the transport layer for
|
||||||
* prod and checks
|
* prod and checks
|
||||||
* if default-server have use_ssl, prerare ssl init
|
* if default-server have use_ssl, prerare ssl init
|
||||||
* without activating it */
|
* without activating it */
|
||||||
if (newsrv->use_ssl == 1 || newsrv->check.use_ssl == 1 ||
|
if (newsrv->use_ssl == 1 || newsrv->check.use_ssl == 1 ||
|
||||||
(newsrv->proxy->options & PR_O_TCPCHK_SSL) ||
|
(newsrv->check.tcpcheck->flags & TCPCHK_FL_USE_SSL) ||
|
||||||
((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) {
|
((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) {
|
||||||
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv)
|
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv)
|
||||||
cfgerr += xprt_get(XPRT_SSL)->prepare_srv(newsrv);
|
cfgerr += xprt_get(XPRT_SSL)->prepare_srv(newsrv);
|
||||||
@ -2672,6 +2666,8 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
|||||||
*err_code |= ERR_WARN;
|
*err_code |= ERR_WARN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*err_code |= proxy_check_http_errors(px);
|
||||||
|
|
||||||
if (px->mode != PR_MODE_HTTP && !(px->options & PR_O_HTTP_UPG)) {
|
if (px->mode != PR_MODE_HTTP && !(px->options & PR_O_HTTP_UPG)) {
|
||||||
int optnum;
|
int optnum;
|
||||||
|
|
||||||
@ -2877,7 +2873,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
|||||||
* due to the proxy's mode not being taken into account
|
* due to the proxy's mode not being taken into account
|
||||||
* on first pass. Let's adjust it now.
|
* on first pass. Let's adjust it now.
|
||||||
*/
|
*/
|
||||||
mux_ent = conn_get_best_mux_entry(newsrv->mux_proto->token, PROTO_SIDE_BE, mode);
|
mux_ent = conn_get_best_mux_entry(newsrv->mux_proto->token, PROTO_SIDE_BE, srv_is_quic(newsrv), mode);
|
||||||
|
|
||||||
if (!mux_ent || !isteq(mux_ent->token, newsrv->mux_proto->token)) {
|
if (!mux_ent || !isteq(mux_ent->token, newsrv->mux_proto->token)) {
|
||||||
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for server '%s' at [%s:%d].\n",
|
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for server '%s' at [%s:%d].\n",
|
||||||
@ -2916,7 +2912,6 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
|||||||
if (px->cap & PR_CAP_BE) {
|
if (px->cap & PR_CAP_BE) {
|
||||||
if (!(px->options2 & PR_O2_CHK_ANY)) {
|
if (!(px->options2 & PR_O2_CHK_ANY)) {
|
||||||
struct tcpcheck_ruleset *rs = NULL;
|
struct tcpcheck_ruleset *rs = NULL;
|
||||||
struct tcpcheck_rules *rules = &px->tcpcheck_rules;
|
|
||||||
|
|
||||||
px->options2 |= PR_O2_TCPCHK_CHK;
|
px->options2 |= PR_O2_TCPCHK_CHK;
|
||||||
|
|
||||||
@ -2929,10 +2924,8 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
|||||||
cfgerr++;
|
cfgerr++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
px->tcpcheck.rs = rs;
|
||||||
free_tcpcheck_vars(&rules->preset_vars);
|
free_tcpcheck_vars(&px->tcpcheck.preset_vars);
|
||||||
rules->list = &rs->rules;
|
|
||||||
rules->flags = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3165,7 +3158,7 @@ int proxy_ref_defaults(struct proxy *px, struct proxy *defpx, char **errmsg)
|
|||||||
defaults_px_ref(defpx, px);
|
defaults_px_ref(defpx, px);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((defpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) &&
|
if (defpx->tcpcheck.rs && (defpx->tcpcheck.rs->flags & TCPCHK_RULES_PROTO_CHK) &&
|
||||||
(px->cap & PR_CAP_LISTEN) == PR_CAP_BE) {
|
(px->cap & PR_CAP_LISTEN) == PR_CAP_BE) {
|
||||||
/* If the current default proxy defines tcpcheck rules, the
|
/* If the current default proxy defines tcpcheck rules, the
|
||||||
* current proxy will keep a reference on it. but only if the
|
* current proxy will keep a reference on it. but only if the
|
||||||
@ -3403,15 +3396,13 @@ static int proxy_defproxy_cpy(struct proxy *curproxy, const struct proxy *defpro
|
|||||||
curproxy->redispatch_after = defproxy->redispatch_after;
|
curproxy->redispatch_after = defproxy->redispatch_after;
|
||||||
curproxy->max_ka_queue = defproxy->max_ka_queue;
|
curproxy->max_ka_queue = defproxy->max_ka_queue;
|
||||||
|
|
||||||
curproxy->tcpcheck_rules.flags = (defproxy->tcpcheck_rules.flags & ~TCPCHK_RULES_UNUSED_RS);
|
curproxy->tcpcheck.flags = (defproxy->tcpcheck.flags & ~TCPCHK_FL_UNUSED_RS);
|
||||||
curproxy->tcpcheck_rules.list = defproxy->tcpcheck_rules.list;
|
curproxy->tcpcheck.rs = defproxy->tcpcheck.rs;
|
||||||
if (!LIST_ISEMPTY(&defproxy->tcpcheck_rules.preset_vars)) {
|
if (!dup_tcpcheck_vars(&curproxy->tcpcheck.preset_vars,
|
||||||
if (!dup_tcpcheck_vars(&curproxy->tcpcheck_rules.preset_vars,
|
&defproxy->tcpcheck.preset_vars)) {
|
||||||
&defproxy->tcpcheck_rules.preset_vars)) {
|
|
||||||
memprintf(errmsg, "proxy '%s': failed to duplicate tcpcheck preset-vars", curproxy->id);
|
memprintf(errmsg, "proxy '%s': failed to duplicate tcpcheck preset-vars", curproxy->id);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
curproxy->ck_opts = defproxy->ck_opts;
|
curproxy->ck_opts = defproxy->ck_opts;
|
||||||
|
|
||||||
@ -4908,7 +4899,7 @@ static int cli_parse_add_backend(char **args, char *payload, struct appctx *appc
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!(defpx->flags & PR_FL_DEF_EXPLICIT_MODE) && !mode) {
|
if (!(defpx->flags & PR_FL_DEF_EXPLICIT_MODE) && !mode) {
|
||||||
cli_dynerr(appctx, memprintf(&msg, "Mode is required as '%s' default proxy does not explicitely defines it.\n", def_name));
|
cli_dynerr(appctx, memprintf(&msg, "Mode is required as '%s' default proxy does not explicitly defines it.\n", def_name));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (defpx->mode != PR_MODE_TCP && defpx->mode != PR_MODE_HTTP) {
|
if (defpx->mode != PR_MODE_TCP && defpx->mode != PR_MODE_HTTP) {
|
||||||
@ -4916,10 +4907,6 @@ static int cli_parse_add_backend(char **args, char *payload, struct appctx *appc
|
|||||||
def_name, proxy_mode_str(defpx->mode)));
|
def_name, proxy_mode_str(defpx->mode)));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!LIST_ISEMPTY(&defpx->conf.errors)) {
|
|
||||||
cli_dynerr(appctx, memprintf(&msg, "Dynamic backends cannot inherit from default proxy '%s' because it references HTTP errors.\n", def_name));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_isolate();
|
thread_isolate();
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ size_t qcs_http_rcv_buf(struct qcs *qcs, struct buffer *buf, size_t count,
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
htx_xfer_blks(cs_htx, qcs_htx, count, HTX_BLK_UNUSED);
|
htx_xfer(cs_htx, qcs_htx, count, HTX_XFER_DEFAULT);
|
||||||
BUG_ON(qcs_htx->flags & HTX_FL_PARSING_ERROR);
|
BUG_ON(qcs_htx->flags & HTX_FL_PARSING_ERROR);
|
||||||
|
|
||||||
/* Copy EOM from src to dst buffer if all data copied. */
|
/* Copy EOM from src to dst buffer if all data copied. */
|
||||||
|
|||||||
@ -143,18 +143,19 @@ static char *qcc_app_st_to_str(const enum qcc_app_st st)
|
|||||||
|
|
||||||
void qmux_dump_qcc_info(struct buffer *msg, const struct qcc *qcc)
|
void qmux_dump_qcc_info(struct buffer *msg, const struct qcc *qcc)
|
||||||
{
|
{
|
||||||
const struct quic_conn *qc = qcc->conn->handle.qc;
|
const struct quic_conn *qc = conn_is_quic(qcc->conn) ? qcc->conn->handle.qc : NULL;
|
||||||
|
|
||||||
chunk_appendf(msg, " qcc=%p(%c)", qcc, (qcc->flags & QC_CF_IS_BACK) ? 'B' : 'F');
|
chunk_appendf(msg, " qcc=%p(%c)", qcc, (qcc->flags & QC_CF_IS_BACK) ? 'B' : 'F');
|
||||||
if (qcc->conn->handle.qc)
|
if (qc)
|
||||||
chunk_appendf(msg, " qc=%p", qcc->conn->handle.qc);
|
chunk_appendf(msg, " qc=%p", qcc->conn->handle.qc);
|
||||||
chunk_appendf(msg, " .st=%s .sc=%llu .hreq=%llu .flg=0x%04x",
|
chunk_appendf(msg, " .st=%s .sc=%llu .hreq=%llu .flg=0x%04x",
|
||||||
qcc_app_st_to_str(qcc->app_st), (ullong)qcc->nb_sc,
|
qcc_app_st_to_str(qcc->app_st), (ullong)qcc->nb_sc,
|
||||||
(ullong)qcc->nb_hreq, qcc->flags);
|
(ullong)qcc->nb_hreq, qcc->flags);
|
||||||
|
|
||||||
chunk_appendf(msg, " .tx=%llu %llu/%llu bwnd=%llu/%llu",
|
chunk_appendf(msg, " .tx=%llu %llu/%llu",
|
||||||
(ullong)qcc->tx.fc.off_soft, (ullong)qcc->tx.fc.off_real, (ullong)qcc->tx.fc.limit,
|
(ullong)qcc->tx.fc.off_soft, (ullong)qcc->tx.fc.off_real, (ullong)qcc->tx.fc.limit);
|
||||||
(ullong)qcc->tx.buf_in_flight, (ullong)qc->path->cwnd);
|
if (qc)
|
||||||
|
chunk_appendf(msg, " bwnd=%llu/%llu", (ullong)qcc->tx.buf_in_flight, (ullong)qc->path->cwnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmux_dump_qcs_info(struct buffer *msg, const struct qcs *qcs)
|
void qmux_dump_qcs_info(struct buffer *msg, const struct qcs *qcs)
|
||||||
@ -169,8 +170,8 @@ void qmux_dump_qcs_info(struct buffer *msg, const struct qcs *qcs)
|
|||||||
(ullong)qcs->tx.fc.off_real,
|
(ullong)qcs->tx.fc.off_real,
|
||||||
(ullong)qcs->tx.fc.limit);
|
(ullong)qcs->tx.fc.limit);
|
||||||
|
|
||||||
if (qcs->stream)
|
if (conn_is_quic(qcs->qcc->conn) && qcs->tx.stream)
|
||||||
bdata_ctr_print(msg, &qcs->stream->data, " buf=");
|
bdata_ctr_print(msg, &qcs->tx.stream->data, " buf=");
|
||||||
|
|
||||||
chunk_appendf(msg, " .ti=%u/%u/%u",
|
chunk_appendf(msg, " .ti=%u/%u/%u",
|
||||||
tot_time_read(&qcs->timer.base),
|
tot_time_read(&qcs->timer.base),
|
||||||
|
|||||||
@ -61,10 +61,10 @@
|
|||||||
static uint64_t qpack_get_varint(const unsigned char **buf, uint64_t *len_in, int b)
|
static uint64_t qpack_get_varint(const unsigned char **buf, uint64_t *len_in, int b)
|
||||||
{
|
{
|
||||||
uint64_t ret = 0;
|
uint64_t ret = 0;
|
||||||
int len = *len_in;
|
uint64_t len = *len_in;
|
||||||
const uint8_t *raw = *buf;
|
const uint8_t *raw = *buf;
|
||||||
uint64_t v, max = ~0;
|
uint64_t v, limit = (1ULL << 62) - 1;
|
||||||
uint8_t shift = 0;
|
int shift = 0;
|
||||||
|
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
goto too_short;
|
goto too_short;
|
||||||
@ -77,24 +77,26 @@ static uint64_t qpack_get_varint(const unsigned char **buf, uint64_t *len_in, in
|
|||||||
do {
|
do {
|
||||||
if (!len)
|
if (!len)
|
||||||
goto too_short;
|
goto too_short;
|
||||||
|
|
||||||
v = *raw++;
|
v = *raw++;
|
||||||
len--;
|
len--;
|
||||||
if (v & 127) { // make UBSan happy
|
/* This check is sufficient to prevent any overflow
|
||||||
if ((v & 127) > max)
|
* and implicitly limits shift to 63.
|
||||||
|
*/
|
||||||
|
if ((v & 127) > (limit - ret) >> shift)
|
||||||
goto too_large;
|
goto too_large;
|
||||||
|
|
||||||
ret += (v & 127) << shift;
|
ret += (v & 127) << shift;
|
||||||
}
|
|
||||||
max >>= 7;
|
|
||||||
shift += 7;
|
shift += 7;
|
||||||
} while (v & 128);
|
} while (v & 128);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
*buf = raw;
|
*buf = raw;
|
||||||
*len_in = len;
|
*len_in = len;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
too_large:
|
too_large:
|
||||||
too_short:
|
too_short:
|
||||||
*len_in = (uint64_t)-1;
|
*len_in = (uint64_t)-1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -402,7 +404,10 @@ int qpack_decode_fs(const unsigned char *raw, uint64_t len, struct buffer *tmp,
|
|||||||
n = efl_type & 0x20;
|
n = efl_type & 0x20;
|
||||||
static_tbl = efl_type & 0x10;
|
static_tbl = efl_type & 0x10;
|
||||||
index = qpack_get_varint(&raw, &len, 4);
|
index = qpack_get_varint(&raw, &len, 4);
|
||||||
if (len == (uint64_t)-1) {
|
/* There must be at least one byte available for <h> value after this
|
||||||
|
* decoding before the next call to qpack_get_varint().
|
||||||
|
*/
|
||||||
|
if ((int64_t)len <= 0) {
|
||||||
qpack_debug_printf(stderr, "##ERR@%d\n", __LINE__);
|
qpack_debug_printf(stderr, "##ERR@%d\n", __LINE__);
|
||||||
ret = -QPACK_RET_TRUNCATED;
|
ret = -QPACK_RET_TRUNCATED;
|
||||||
goto out;
|
goto out;
|
||||||
@ -474,7 +479,10 @@ int qpack_decode_fs(const unsigned char *raw, uint64_t len, struct buffer *tmp,
|
|||||||
n = *raw & 0x10;
|
n = *raw & 0x10;
|
||||||
hname = *raw & 0x08;
|
hname = *raw & 0x08;
|
||||||
name_len = qpack_get_varint(&raw, &len, 3);
|
name_len = qpack_get_varint(&raw, &len, 3);
|
||||||
if (len == (uint64_t)-1 || len < name_len) {
|
/* There must be at least one byte available for <hvalue> after this
|
||||||
|
* decoding before the next call to qpack_get_varint().
|
||||||
|
*/
|
||||||
|
if ((int64_t)len < (int64_t)name_len + 1) {
|
||||||
qpack_debug_printf(stderr, "##ERR@%d\n", __LINE__);
|
qpack_debug_printf(stderr, "##ERR@%d\n", __LINE__);
|
||||||
ret = -QPACK_RET_TRUNCATED;
|
ret = -QPACK_RET_TRUNCATED;
|
||||||
goto out;
|
goto out;
|
||||||
|
|||||||
213
src/quic_frame.c
213
src/quic_frame.c
@ -17,7 +17,7 @@
|
|||||||
#include <haproxy/quic_enc.h>
|
#include <haproxy/quic_enc.h>
|
||||||
#include <haproxy/quic_frame.h>
|
#include <haproxy/quic_frame.h>
|
||||||
#include <haproxy/quic_rx-t.h>
|
#include <haproxy/quic_rx-t.h>
|
||||||
#include <haproxy/quic_tp-t.h>
|
#include <haproxy/quic_tp.h>
|
||||||
#include <haproxy/quic_trace.h>
|
#include <haproxy/quic_trace.h>
|
||||||
#include <haproxy/quic_tx.h>
|
#include <haproxy/quic_tx.h>
|
||||||
#include <haproxy/trace.h>
|
#include <haproxy/trace.h>
|
||||||
@ -1011,6 +1011,47 @@ static int quic_build_handshake_done_frame(unsigned char **pos, const unsigned c
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Encodes a QX_TRANSPORT_PARAMETER <frm> frame at <pos> buffer position.
|
||||||
|
* Returns 1 on success else 0.
|
||||||
|
*/
|
||||||
|
static int quic_build_qmux_transport_parameters(unsigned char **pos, const unsigned char *end,
|
||||||
|
struct quic_frame *frm, struct quic_conn *conn)
|
||||||
|
{
|
||||||
|
unsigned char *old = *pos;
|
||||||
|
struct buffer buf;
|
||||||
|
|
||||||
|
/* Reserve space for Length field encoded as a max varint size. */
|
||||||
|
if (end - *pos < 8)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Encode a 0 length field for now. */
|
||||||
|
buf = b_make((char *)*pos, end - *pos, 0, 0);
|
||||||
|
if (!b_quic_enc_int(&buf, 0, 8))
|
||||||
|
return 0;
|
||||||
|
*pos += 8;
|
||||||
|
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_MAX_IDLE_TIMEOUT, 30000))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_DATA, 16384))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, 16384))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, 16384))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI, 16384))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_STREAMS_BIDI, 100))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_STREAMS_UNI, 100))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Re-encode the real length field now. */
|
||||||
|
buf = b_make((char *)old, 8, 0, 0);
|
||||||
|
b_quic_enc_int(&buf, *pos - old - 8, 8);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Parse a HANDSHAKE_DONE frame at QUIC layer at <pos> buffer position with <end> as end into <frm> frame.
|
/* Parse a HANDSHAKE_DONE frame at QUIC layer at <pos> buffer position with <end> as end into <frm> frame.
|
||||||
* Always succeed.
|
* Always succeed.
|
||||||
*/
|
*/
|
||||||
@ -1021,6 +1062,28 @@ static int quic_parse_handshake_done_frame(struct quic_frame *frm, struct quic_c
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parse a QX_TRANSPORT_PARAMETER frame at <pos> buffer position.
|
||||||
|
* Returns 1 on success else 0.
|
||||||
|
*/
|
||||||
|
static int quic_parse_qmux_transport_parameters(struct quic_frame *frm, struct quic_conn *qc,
|
||||||
|
const unsigned char **pos, const unsigned char *end)
|
||||||
|
{
|
||||||
|
struct qf_qx_transport_parameters *params_frm = &frm->qmux_transport_params;
|
||||||
|
uint64_t len;
|
||||||
|
|
||||||
|
if (!quic_dec_int(&len, pos, end))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (len > end - *pos)
|
||||||
|
return 0;
|
||||||
|
end = *pos + len;
|
||||||
|
|
||||||
|
if (!quic_transport_params_decode(¶ms_frm->params, 1, *pos, end))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
struct quic_frame_builder {
|
struct quic_frame_builder {
|
||||||
int (*func)(unsigned char **pos, const unsigned char *end,
|
int (*func)(unsigned char **pos, const unsigned char *end,
|
||||||
struct quic_frame *frm, struct quic_conn *conn);
|
struct quic_frame *frm, struct quic_conn *conn);
|
||||||
@ -1121,11 +1184,25 @@ const struct quic_frame_parser quic_frame_parsers[] = {
|
|||||||
* };
|
* };
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* quic-on-streams transport parameters frame. */
|
||||||
|
const uint64_t QUIC_FT_QX_TRANSPORT_PARAMETERS = 0x3f5153300d0a0d0a;
|
||||||
|
const struct quic_frame_parser qf_parser_qx_transport_parameters = {
|
||||||
|
.func = quic_parse_qmux_transport_parameters,
|
||||||
|
.mask = 0,
|
||||||
|
.flags = 0,
|
||||||
|
};
|
||||||
|
const struct quic_frame_builder qf_builder_qx_transport_parameters = {
|
||||||
|
.func = quic_build_qmux_transport_parameters,
|
||||||
|
.mask = 0,
|
||||||
|
.flags = 0,
|
||||||
|
};
|
||||||
|
|
||||||
/* Returns true if frame <type> is supported. */
|
/* Returns true if frame <type> is supported. */
|
||||||
static inline int quic_frame_type_is_known(uint64_t type)
|
static inline int quic_frame_type_is_known(uint64_t type)
|
||||||
{
|
{
|
||||||
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
||||||
return type < QUIC_FT_MAX;
|
return type < QUIC_FT_MAX ||
|
||||||
|
(type == QUIC_FT_QX_TRANSPORT_PARAMETERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct quic_frame_parser *qf_parser(uint64_t type)
|
static const struct quic_frame_parser *qf_parser(uint64_t type)
|
||||||
@ -1134,6 +1211,8 @@ static const struct quic_frame_parser *qf_parser(uint64_t type)
|
|||||||
return &quic_frame_parsers[type];
|
return &quic_frame_parsers[type];
|
||||||
|
|
||||||
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
||||||
|
if (type == QUIC_FT_QX_TRANSPORT_PARAMETERS)
|
||||||
|
return &qf_parser_qx_transport_parameters;
|
||||||
|
|
||||||
ABORT_NOW();
|
ABORT_NOW();
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1145,22 +1224,26 @@ const struct quic_frame_builder *qf_builder(uint64_t type)
|
|||||||
return &quic_frame_builders[type];
|
return &quic_frame_builders[type];
|
||||||
|
|
||||||
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
||||||
|
if (type == QUIC_FT_QX_TRANSPORT_PARAMETERS)
|
||||||
|
return &qf_builder_qx_transport_parameters;
|
||||||
|
|
||||||
ABORT_NOW();
|
ABORT_NOW();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Decode a QUIC frame at <pos> buffer position into <frm> frame.
|
/* Parse frame type in buffer starting at <pos> and ending at <end> and store
|
||||||
* Returns 1 if succeeded (enough data at <pos> buffer position to parse the frame), 0 if not.
|
* it in <frm> object.
|
||||||
|
*
|
||||||
|
* Returns 1 on success else 0.
|
||||||
*/
|
*/
|
||||||
int qc_parse_frm(struct quic_frame *frm, struct quic_rx_packet *pkt,
|
int qc_parse_frm_type(struct quic_frame *frm,
|
||||||
const unsigned char **pos, const unsigned char *end,
|
const unsigned char **pos, const unsigned char *end,
|
||||||
struct quic_conn *qc)
|
struct quic_conn *qc)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
const struct quic_frame_parser *parser;
|
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_PRSFRM, qc);
|
TRACE_ENTER(QUIC_EV_CONN_PRSFRM, qc);
|
||||||
|
|
||||||
if (end <= *pos) {
|
if (end <= *pos) {
|
||||||
TRACE_DEVEL("wrong frame", QUIC_EV_CONN_PRSFRM, qc);
|
TRACE_DEVEL("wrong frame", QUIC_EV_CONN_PRSFRM, qc);
|
||||||
goto leave;
|
goto leave;
|
||||||
@ -1168,62 +1251,78 @@ int qc_parse_frm(struct quic_frame *frm, struct quic_rx_packet *pkt,
|
|||||||
|
|
||||||
if (!quic_dec_int(&frm->type, pos, end)) {
|
if (!quic_dec_int(&frm->type, pos, end)) {
|
||||||
TRACE_ERROR("malformed frame type", QUIC_EV_CONN_PRSFRM, qc);
|
TRACE_ERROR("malformed frame type", QUIC_EV_CONN_PRSFRM, qc);
|
||||||
quic_set_connection_close(qc, quic_err_transport(QC_ERR_FRAME_ENCODING_ERROR));
|
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!quic_frame_type_is_known(frm->type)) {
|
if (!quic_frame_type_is_known(frm->type)) {
|
||||||
/* RFC 9000 12.4. Frames and Frame Types
|
|
||||||
*
|
|
||||||
* An endpoint MUST treat the receipt of a frame of unknown type as a
|
|
||||||
* connection error of type FRAME_ENCODING_ERROR.
|
|
||||||
*/
|
|
||||||
TRACE_DEVEL("wrong frame type", QUIC_EV_CONN_PRSFRM, qc, frm);
|
TRACE_DEVEL("wrong frame type", QUIC_EV_CONN_PRSFRM, qc, frm);
|
||||||
quic_set_connection_close(qc, quic_err_transport(QC_ERR_FRAME_ENCODING_ERROR));
|
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser = qf_parser(frm->type);
|
|
||||||
if (!(parser->mask & (1U << pkt->type))) {
|
|
||||||
TRACE_DEVEL("unauthorized frame", QUIC_EV_CONN_PRSFRM, qc, frm);
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parser->func(frm, qc, pos, end)) {
|
|
||||||
TRACE_DEVEL("parsing error", QUIC_EV_CONN_PRSFRM, qc, frm);
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE_PROTO("RX frm", QUIC_EV_CONN_PSTRM, qc, frm);
|
|
||||||
|
|
||||||
pkt->flags |= parser->flags;
|
|
||||||
|
|
||||||
ret = 1;
|
ret = 1;
|
||||||
leave:
|
leave:
|
||||||
TRACE_LEAVE(QUIC_EV_CONN_PRSFRM, qc);
|
TRACE_LEAVE(QUIC_EV_CONN_PRSFRM, qc);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Encode <frm> QUIC frame at <pos> buffer position.
|
/* Checks that <frm> frame is authorized in <pkt> packet. Output parameter <flags>
|
||||||
* Returns 1 if succeeded (enough room at <pos> buffer position to encode the frame), 0 if not.
|
* may be updated if the frame characteristics impacts the containing packet.
|
||||||
* The buffer is updated to point to one byte past the end of the built frame
|
*
|
||||||
* only if succeeded.
|
* Returns true for a valid frame else false.
|
||||||
*/
|
*/
|
||||||
int qc_build_frm(unsigned char **pos, const unsigned char *end,
|
int qc_parse_frm_pkt(const struct quic_frame *frm,
|
||||||
struct quic_frame *frm, struct quic_tx_packet *pkt,
|
const struct quic_rx_packet *pkt, int *flags)
|
||||||
|
{
|
||||||
|
const struct quic_frame_parser *parser = qf_parser(frm->type);
|
||||||
|
if (!(parser->mask & (1U << pkt->type)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*flags = parser->flags;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse frame content in buffer starting at <pos> and ending at <end>.
|
||||||
|
*
|
||||||
|
* Returns 1 on success else 0.
|
||||||
|
*/
|
||||||
|
int qc_parse_frm_payload(struct quic_frame *frm,
|
||||||
|
const unsigned char **pos, const unsigned char *end,
|
||||||
struct quic_conn *qc)
|
struct quic_conn *qc)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
const struct quic_frame_builder *builder;
|
const struct quic_frame_parser *parser;
|
||||||
|
|
||||||
|
TRACE_ENTER(QUIC_EV_CONN_PRSFRM, qc);
|
||||||
|
|
||||||
|
parser = qf_parser(frm->type);
|
||||||
|
if (!parser->func(frm, qc, pos, end)) {
|
||||||
|
TRACE_DEVEL("parsing error", QUIC_EV_CONN_PRSFRM, qc, frm);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_PROTO("RX frm", QUIC_EV_CONN_PSTRM, qc, frm);
|
||||||
|
|
||||||
|
ret = 1;
|
||||||
|
leave:
|
||||||
|
TRACE_LEAVE(QUIC_EV_CONN_PRSFRM, qc);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encodes a <frm> QUIC frame in buffer starting at <pos> and ending
|
||||||
|
* at <end>. On success, <pos> is advanced up to the end of the newly encoded
|
||||||
|
* data.
|
||||||
|
*
|
||||||
|
* Returns 1 on success else 0.
|
||||||
|
*/
|
||||||
|
int qc_build_frm(struct quic_frame *frm,
|
||||||
|
unsigned char **pos, const unsigned char *end,
|
||||||
|
struct quic_conn *qc)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
const struct quic_frame_builder *builder = qf_builder(frm->type);
|
||||||
unsigned char *p = *pos;
|
unsigned char *p = *pos;
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_BFRM, qc);
|
TRACE_ENTER(QUIC_EV_CONN_BFRM, qc);
|
||||||
builder = qf_builder(frm->type);
|
|
||||||
if (!(builder->mask & (1U << pkt->type))) {
|
|
||||||
/* XXX This it a bug to send an unauthorized frame with such a packet type XXX */
|
|
||||||
TRACE_ERROR("unauthorized frame", QUIC_EV_CONN_BFRM, qc, frm);
|
|
||||||
BUG_ON(!(builder->mask & (1U << pkt->type)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!quic_enc_int(&p, end, frm->type)) {
|
if (!quic_enc_int(&p, end, frm->type)) {
|
||||||
TRACE_DEVEL("not enough room", QUIC_EV_CONN_BFRM, qc, frm);
|
TRACE_DEVEL("not enough room", QUIC_EV_CONN_BFRM, qc, frm);
|
||||||
@ -1236,9 +1335,39 @@ int qc_build_frm(unsigned char **pos, const unsigned char *end,
|
|||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt->flags |= builder->flags;
|
|
||||||
*pos = p;
|
*pos = p;
|
||||||
|
ret = 1;
|
||||||
|
leave:
|
||||||
|
TRACE_LEAVE(QUIC_EV_CONN_BFRM, qc);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encodes a <frm> QUIC frame in <pkt> packet buffer via qc_build_frm() after
|
||||||
|
* checking packet type compatibility. Packet <pkt> flags are updated if the
|
||||||
|
* frame characteristics impacts it.
|
||||||
|
*
|
||||||
|
* Returns 1 on success else 0.
|
||||||
|
*/
|
||||||
|
int qc_build_frm_pkt(struct quic_frame *frm, struct quic_tx_packet *pkt,
|
||||||
|
unsigned char **pos, const unsigned char *end,
|
||||||
|
struct quic_conn *qc)
|
||||||
|
{
|
||||||
|
const struct quic_frame_builder *builder = qf_builder(frm->type);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
TRACE_ENTER(QUIC_EV_CONN_BFRM, qc);
|
||||||
|
|
||||||
|
if (pkt && !(builder->mask & (1U << pkt->type))) {
|
||||||
|
/* XXX This it a bug to send an unauthorized frame with such a packet type XXX */
|
||||||
|
TRACE_ERROR("unauthorized frame", QUIC_EV_CONN_BFRM, qc, frm);
|
||||||
|
BUG_ON(!(builder->mask & (1U << pkt->type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qc_build_frm(frm, pos, end, qc);
|
||||||
|
if (!ret)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
pkt->flags |= builder->flags;
|
||||||
ret = 1;
|
ret = 1;
|
||||||
leave:
|
leave:
|
||||||
TRACE_LEAVE(QUIC_EV_CONN_BFRM, qc);
|
TRACE_LEAVE(QUIC_EV_CONN_BFRM, qc);
|
||||||
|
|||||||
@ -840,7 +840,7 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
|||||||
{
|
{
|
||||||
struct quic_frame *frm = NULL;
|
struct quic_frame *frm = NULL;
|
||||||
const unsigned char *pos, *end;
|
const unsigned char *pos, *end;
|
||||||
int fast_retrans = 0, ret;
|
int fast_retrans = 0, pkt_flags = 0, ret;
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_PRSHPKT, qc);
|
TRACE_ENTER(QUIC_EV_CONN_PRSHPKT, qc);
|
||||||
/* Skip the AAD */
|
/* Skip the AAD */
|
||||||
@ -867,7 +867,32 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!qc_parse_frm(frm, pkt, &pos, end, qc)) {
|
if (!qc_parse_frm_type(frm, &pos, end, qc)) {
|
||||||
|
/* RFC 9000 12.4. Frames and Frame Types
|
||||||
|
*
|
||||||
|
* An endpoint MUST treat the receipt of a frame of unknown type as a
|
||||||
|
* connection error of type FRAME_ENCODING_ERROR.
|
||||||
|
*/
|
||||||
|
quic_set_connection_close(qc, quic_err_transport(QC_ERR_FRAME_ENCODING_ERROR));
|
||||||
|
/* trace already emitted by above function */
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RFC 9000 12.4. Frames and Frame Types
|
||||||
|
*
|
||||||
|
* An endpoint MUST treat
|
||||||
|
* receipt of a frame in a packet type that is not permitted as a
|
||||||
|
* connection error of type PROTOCOL_VIOLATION.
|
||||||
|
*/
|
||||||
|
if (!qc_parse_frm_pkt(frm, pkt, &pkt_flags)) {
|
||||||
|
TRACE_ERROR("unauthorized frame", QUIC_EV_CONN_PRSFRM, qc, frm);
|
||||||
|
quic_set_connection_close(qc, quic_err_transport(QC_ERR_PROTOCOL_VIOLATION));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkt->flags |= pkt_flags;
|
||||||
|
|
||||||
|
if (!qc_parse_frm_payload(frm, &pos, end, qc)) {
|
||||||
// trace already emitted by function above
|
// trace already emitted by function above
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -1147,7 +1172,7 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
|||||||
qc->state = QUIC_HS_ST_CONFIRMED;
|
qc->state = QUIC_HS_ST_CONFIRMED;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* Unknown frame type must be rejected by qc_parse_frm(). */
|
/* Unknown frame type must be rejected by qc_parse_frm_type(). */
|
||||||
ABORT_NOW();
|
ABORT_NOW();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1363,7 +1363,7 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, void *target)
|
|||||||
/* This code is called by connect_server() by way of
|
/* This code is called by connect_server() by way of
|
||||||
* conn_prepare().
|
* conn_prepare().
|
||||||
* XXX TODO XXX: there is a remaining race condition where
|
* XXX TODO XXX: there is a remaining race condition where
|
||||||
* the negotiated alpn could be resetted before running this code
|
* the negotiated alpn could be reset before running this code
|
||||||
* here. In this case the app_ops for the mux will not be
|
* here. In this case the app_ops for the mux will not be
|
||||||
* set by quic_reuse_srv_params().
|
* set by quic_reuse_srv_params().
|
||||||
*
|
*
|
||||||
|
|||||||
@ -16,8 +16,6 @@ DECLARE_STATIC_TYPED_POOL(pool_head_quic_stream_desc, "qc_stream_desc", struct q
|
|||||||
DECLARE_STATIC_TYPED_POOL(pool_head_quic_stream_buf, "qc_stream_buf", struct qc_stream_buf);
|
DECLARE_STATIC_TYPED_POOL(pool_head_quic_stream_buf, "qc_stream_buf", struct qc_stream_buf);
|
||||||
DECLARE_STATIC_TYPED_POOL(pool_head_quic_stream_ack, "qc_stream_ack", struct qc_stream_ack);
|
DECLARE_STATIC_TYPED_POOL(pool_head_quic_stream_ack, "qc_stream_ack", struct qc_stream_ack);
|
||||||
|
|
||||||
static struct pool_head *pool_head_sbuf;
|
|
||||||
|
|
||||||
static void qc_stream_buf_free(struct qc_stream_desc *stream,
|
static void qc_stream_buf_free(struct qc_stream_desc *stream,
|
||||||
struct qc_stream_buf **stream_buf)
|
struct qc_stream_buf **stream_buf)
|
||||||
{
|
{
|
||||||
@ -39,13 +37,10 @@ static void qc_stream_buf_free(struct qc_stream_desc *stream,
|
|||||||
room = b_data(buf);
|
room = b_data(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((*stream_buf)->sbuf) {
|
b_free(buf);
|
||||||
pool_free(pool_head_sbuf, buf->area);
|
if (!(*stream_buf)->sbuf) {
|
||||||
}
|
|
||||||
else {
|
|
||||||
bdata_ctr_del(&stream->data, b_data(buf));
|
bdata_ctr_del(&stream->data, b_data(buf));
|
||||||
bdata_ctr_bdec(&stream->data);
|
bdata_ctr_bdec(&stream->data);
|
||||||
b_free(buf);
|
|
||||||
offer_buffers(NULL, 1);
|
offer_buffers(NULL, 1);
|
||||||
}
|
}
|
||||||
pool_free(pool_head_quic_stream_buf, *stream_buf);
|
pool_free(pool_head_quic_stream_buf, *stream_buf);
|
||||||
@ -412,9 +407,6 @@ void qc_stream_desc_free(struct qc_stream_desc *stream, int closing)
|
|||||||
pool_free(pool_head_quic_stream_ack, ack);
|
pool_free(pool_head_quic_stream_ack, ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf->sbuf)
|
|
||||||
pool_free(pool_head_sbuf, buf->buf.area);
|
|
||||||
else
|
|
||||||
b_free(&buf->buf);
|
b_free(&buf->buf);
|
||||||
|
|
||||||
eb64_delete(&buf->offset_node);
|
eb64_delete(&buf->offset_node);
|
||||||
@ -461,7 +453,7 @@ struct buffer *qc_stream_buf_alloc(struct qc_stream_desc *stream,
|
|||||||
stream->buf->buf = BUF_NULL;
|
stream->buf->buf = BUF_NULL;
|
||||||
stream->buf->offset_node.key = offset;
|
stream->buf->offset_node.key = offset;
|
||||||
|
|
||||||
if (!small) {
|
if (!small || !global.tune.bufsize_small) {
|
||||||
stream->buf->sbuf = 0;
|
stream->buf->sbuf = 0;
|
||||||
if (!b_alloc(&stream->buf->buf, DB_MUX_TX)) {
|
if (!b_alloc(&stream->buf->buf, DB_MUX_TX)) {
|
||||||
pool_free(pool_head_quic_stream_buf, stream->buf);
|
pool_free(pool_head_quic_stream_buf, stream->buf);
|
||||||
@ -470,16 +462,12 @@ struct buffer *qc_stream_buf_alloc(struct qc_stream_desc *stream,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
char *area;
|
if (!b_alloc_small(&stream->buf->buf)) {
|
||||||
|
|
||||||
if (!(area = pool_alloc(pool_head_sbuf))) {
|
|
||||||
pool_free(pool_head_quic_stream_buf, stream->buf);
|
pool_free(pool_head_quic_stream_buf, stream->buf);
|
||||||
stream->buf = NULL;
|
stream->buf = NULL;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->buf->sbuf = 1;
|
stream->buf->sbuf = 1;
|
||||||
stream->buf->buf = b_make(area, global.tune.bufsize_small, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eb64_insert(&stream->buf_tree, &stream->buf->offset_node);
|
eb64_insert(&stream->buf_tree, &stream->buf->offset_node);
|
||||||
@ -502,7 +490,7 @@ struct buffer *qc_stream_buf_realloc(struct qc_stream_desc *stream)
|
|||||||
BUG_ON(b_data(&stream->buf->buf));
|
BUG_ON(b_data(&stream->buf->buf));
|
||||||
|
|
||||||
/* Release buffer */
|
/* Release buffer */
|
||||||
pool_free(pool_head_sbuf, stream->buf->buf.area);
|
b_free(&stream->buf->buf);
|
||||||
stream->buf->buf = BUF_NULL;
|
stream->buf->buf = BUF_NULL;
|
||||||
stream->buf->sbuf = 0;
|
stream->buf->sbuf = 0;
|
||||||
|
|
||||||
@ -536,23 +524,3 @@ void qc_stream_buf_release(struct qc_stream_desc *stream)
|
|||||||
if (stream->notify_room && room)
|
if (stream->notify_room && room)
|
||||||
stream->notify_room(stream, room);
|
stream->notify_room(stream, room);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int create_sbuf_pool(void)
|
|
||||||
{
|
|
||||||
if (global.tune.bufsize_small > global.tune.bufsize) {
|
|
||||||
ha_warning("invalid small buffer size %d bytes which is greater to default bufsize %d bytes.\n",
|
|
||||||
global.tune.bufsize_small, global.tune.bufsize);
|
|
||||||
return ERR_FATAL|ERR_ABORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
pool_head_sbuf = create_pool("sbuf", global.tune.bufsize_small,
|
|
||||||
MEM_F_SHARED|MEM_F_EXACT);
|
|
||||||
if (!pool_head_sbuf) {
|
|
||||||
ha_warning("error on small buffer pool allocation.\n");
|
|
||||||
return ERR_FATAL|ERR_ABORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ERR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_POST_CHECK(create_sbuf_pool);
|
|
||||||
|
|||||||
196
src/quic_tp.c
196
src/quic_tp.c
@ -256,11 +256,12 @@ static int quic_transport_param_dec_version_info(struct tp_version_information *
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Decode into <p> struct a transport parameter found in <*buf> buffer with
|
/* Decode into <p> struct a transport parameter found in <*buf> buffer with
|
||||||
* <type> as type and <len> as length, depending on <server> boolean value which
|
* <type> as type and <len> as length. The boolean argument <server> must be
|
||||||
* must be set to 1 for a server (haproxy listener) or 0 for a client (connection
|
* set according to the origin of the parameters.
|
||||||
* to an haproxy server).
|
*
|
||||||
|
* Returns 1 on success else 0 if decoding is truncated.
|
||||||
*/
|
*/
|
||||||
static enum quic_tp_dec_err
|
static int
|
||||||
quic_transport_param_decode(struct quic_transport_params *p, int server,
|
quic_transport_param_decode(struct quic_transport_params *p, int server,
|
||||||
uint64_t type, const unsigned char **buf, size_t len)
|
uint64_t type, const unsigned char **buf, size_t len)
|
||||||
{
|
{
|
||||||
@ -268,19 +269,8 @@ quic_transport_param_decode(struct quic_transport_params *p, int server,
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case QUIC_TP_ORIGINAL_DESTINATION_CONNECTION_ID:
|
case QUIC_TP_ORIGINAL_DESTINATION_CONNECTION_ID:
|
||||||
/* RFC 9000 18.2. Transport Parameter Definitions
|
|
||||||
*
|
|
||||||
* A client MUST NOT include any server-only transport parameter:
|
|
||||||
* original_destination_connection_id, preferred_address,
|
|
||||||
* retry_source_connection_id, or stateless_reset_token. A server MUST
|
|
||||||
* treat receipt of any of these transport parameters as a connection
|
|
||||||
* error of type TRANSPORT_PARAMETER_ERROR.
|
|
||||||
*/
|
|
||||||
if (!server)
|
|
||||||
return QUIC_TP_DEC_ERR_INVAL;
|
|
||||||
|
|
||||||
if (len > sizeof p->original_destination_connection_id.data)
|
if (len > sizeof p->original_destination_connection_id.data)
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
if (len)
|
if (len)
|
||||||
memcpy(p->original_destination_connection_id.data, *buf, len);
|
memcpy(p->original_destination_connection_id.data, *buf, len);
|
||||||
p->original_destination_connection_id.len = len;
|
p->original_destination_connection_id.len = len;
|
||||||
@ -289,7 +279,7 @@ quic_transport_param_decode(struct quic_transport_params *p, int server,
|
|||||||
break;
|
break;
|
||||||
case QUIC_TP_INITIAL_SOURCE_CONNECTION_ID:
|
case QUIC_TP_INITIAL_SOURCE_CONNECTION_ID:
|
||||||
if (len > sizeof p->initial_source_connection_id.data)
|
if (len > sizeof p->initial_source_connection_id.data)
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
|
|
||||||
if (len)
|
if (len)
|
||||||
memcpy(p->initial_source_connection_id.data, *buf, len);
|
memcpy(p->initial_source_connection_id.data, *buf, len);
|
||||||
@ -298,115 +288,78 @@ quic_transport_param_decode(struct quic_transport_params *p, int server,
|
|||||||
p->initial_source_connection_id_present = 1;
|
p->initial_source_connection_id_present = 1;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_STATELESS_RESET_TOKEN:
|
case QUIC_TP_STATELESS_RESET_TOKEN:
|
||||||
/* see original_destination_connection_id RFC reference above. */
|
|
||||||
if (!server)
|
|
||||||
return QUIC_TP_DEC_ERR_INVAL;
|
|
||||||
|
|
||||||
if (len != sizeof p->stateless_reset_token)
|
if (len != sizeof p->stateless_reset_token)
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
memcpy(p->stateless_reset_token, *buf, len);
|
memcpy(p->stateless_reset_token, *buf, len);
|
||||||
*buf += len;
|
*buf += len;
|
||||||
p->with_stateless_reset_token = 1;
|
p->with_stateless_reset_token = 1;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_PREFERRED_ADDRESS:
|
case QUIC_TP_PREFERRED_ADDRESS:
|
||||||
/* see original_destination_connection_id RFC reference above. */
|
|
||||||
if (!server)
|
|
||||||
return QUIC_TP_DEC_ERR_INVAL;
|
|
||||||
|
|
||||||
if (!quic_transport_param_dec_pref_addr(&p->preferred_address, buf, *buf + len))
|
if (!quic_transport_param_dec_pref_addr(&p->preferred_address, buf, *buf + len))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
p->with_preferred_address = 1;
|
p->with_preferred_address = 1;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_MAX_IDLE_TIMEOUT:
|
case QUIC_TP_MAX_IDLE_TIMEOUT:
|
||||||
if (!quic_dec_int(&p->max_idle_timeout, buf, end))
|
if (!quic_dec_int(&p->max_idle_timeout, buf, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_MAX_UDP_PAYLOAD_SIZE:
|
case QUIC_TP_MAX_UDP_PAYLOAD_SIZE:
|
||||||
if (!quic_dec_int(&p->max_udp_payload_size, buf, end))
|
if (!quic_dec_int(&p->max_udp_payload_size, buf, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
|
|
||||||
/* RFC 9000 18.2. Transport Parameter Definitions
|
|
||||||
*
|
|
||||||
* max_udp_payload_size (0x03): [...]
|
|
||||||
* The default for this parameter is the maximum permitted UDP
|
|
||||||
* payload of 65527. Values below 1200 are invalid.
|
|
||||||
*/
|
|
||||||
if (p->max_udp_payload_size < 1200)
|
|
||||||
return QUIC_TP_DEC_ERR_INVAL;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_INITIAL_MAX_DATA:
|
case QUIC_TP_INITIAL_MAX_DATA:
|
||||||
if (!quic_dec_int(&p->initial_max_data, buf, end))
|
if (!quic_dec_int(&p->initial_max_data, buf, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL:
|
case QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL:
|
||||||
if (!quic_dec_int(&p->initial_max_stream_data_bidi_local, buf, end))
|
if (!quic_dec_int(&p->initial_max_stream_data_bidi_local, buf, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE:
|
case QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE:
|
||||||
if (!quic_dec_int(&p->initial_max_stream_data_bidi_remote, buf, end))
|
if (!quic_dec_int(&p->initial_max_stream_data_bidi_remote, buf, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI:
|
case QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI:
|
||||||
if (!quic_dec_int(&p->initial_max_stream_data_uni, buf, end))
|
if (!quic_dec_int(&p->initial_max_stream_data_uni, buf, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_INITIAL_MAX_STREAMS_BIDI:
|
case QUIC_TP_INITIAL_MAX_STREAMS_BIDI:
|
||||||
if (!quic_dec_int(&p->initial_max_streams_bidi, buf, end))
|
if (!quic_dec_int(&p->initial_max_streams_bidi, buf, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_INITIAL_MAX_STREAMS_UNI:
|
case QUIC_TP_INITIAL_MAX_STREAMS_UNI:
|
||||||
if (!quic_dec_int(&p->initial_max_streams_uni, buf, end))
|
if (!quic_dec_int(&p->initial_max_streams_uni, buf, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_ACK_DELAY_EXPONENT:
|
case QUIC_TP_ACK_DELAY_EXPONENT:
|
||||||
if (!quic_dec_int(&p->ack_delay_exponent, buf, end))
|
if (!quic_dec_int(&p->ack_delay_exponent, buf, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
|
|
||||||
/* RFC 9000 18.2. Transport Parameter Definitions
|
|
||||||
*
|
|
||||||
* ack_delay_exponent (0x0a): [...]
|
|
||||||
* Values above 20 are invalid.
|
|
||||||
*/
|
|
||||||
if (p->ack_delay_exponent > QUIC_TP_ACK_DELAY_EXPONENT_LIMIT)
|
|
||||||
return QUIC_TP_DEC_ERR_INVAL;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_MAX_ACK_DELAY:
|
case QUIC_TP_MAX_ACK_DELAY:
|
||||||
if (!quic_dec_int(&p->max_ack_delay, buf, end))
|
if (!quic_dec_int(&p->max_ack_delay, buf, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
|
|
||||||
/* RFC 9000 18.2. Transport Parameter Definitions
|
|
||||||
*
|
|
||||||
* max_ack_delay (0x0b): [...]
|
|
||||||
* Values of 2^14 or greater are invalid.
|
|
||||||
*/
|
|
||||||
if (p->max_ack_delay >= QUIC_TP_MAX_ACK_DELAY_LIMIT)
|
|
||||||
return QUIC_TP_DEC_ERR_INVAL;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_DISABLE_ACTIVE_MIGRATION:
|
case QUIC_TP_DISABLE_ACTIVE_MIGRATION:
|
||||||
/* Zero-length parameter type. */
|
/* Zero-length parameter type. */
|
||||||
if (len != 0)
|
if (len != 0)
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
p->disable_active_migration = 1;
|
p->disable_active_migration = 1;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT:
|
case QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT:
|
||||||
if (!quic_dec_int(&p->active_connection_id_limit, buf, end))
|
if (!quic_dec_int(&p->active_connection_id_limit, buf, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_VERSION_INFORMATION:
|
case QUIC_TP_VERSION_INFORMATION:
|
||||||
if (!quic_transport_param_dec_version_info(&p->version_information,
|
if (!quic_transport_param_dec_version_info(&p->version_information,
|
||||||
buf, *buf + len, server))
|
buf, *buf + len, server))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case QUIC_TP_RETRY_SOURCE_CONNECTION_ID:
|
case QUIC_TP_RETRY_SOURCE_CONNECTION_ID:
|
||||||
/* see original_destination_connection_id RFC reference above. */
|
|
||||||
if (!server)
|
|
||||||
return QUIC_TP_DEC_ERR_INVAL;
|
|
||||||
|
|
||||||
if (len > sizeof p->retry_source_connection_id.data)
|
if (len > sizeof p->retry_source_connection_id.data)
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
|
|
||||||
if (len)
|
if (len)
|
||||||
memcpy(p->retry_source_connection_id.data, *buf, len);
|
memcpy(p->retry_source_connection_id.data, *buf, len);
|
||||||
@ -417,7 +370,7 @@ quic_transport_param_decode(struct quic_transport_params *p, int server,
|
|||||||
*buf += len;
|
*buf += len;
|
||||||
};
|
};
|
||||||
|
|
||||||
return *buf == end ? QUIC_TP_DEC_ERR_NONE : QUIC_TP_DEC_ERR_TRUNC;
|
return *buf == end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Encode <type> and <len> variable length values in <buf>.
|
/* Encode <type> and <len> variable length values in <buf>.
|
||||||
@ -463,7 +416,7 @@ static int quic_transport_param_enc_mem(unsigned char **buf, const unsigned char
|
|||||||
/* Encode <val> 64-bits value as variable length integer into <buf>.
|
/* Encode <val> 64-bits value as variable length integer into <buf>.
|
||||||
* Returns 1 if succeeded, 0 if not.
|
* Returns 1 if succeeded, 0 if not.
|
||||||
*/
|
*/
|
||||||
static int quic_transport_param_enc_int(unsigned char **buf,
|
int quic_transport_param_enc_int(unsigned char **buf,
|
||||||
const unsigned char *end,
|
const unsigned char *end,
|
||||||
uint64_t type, uint64_t val)
|
uint64_t type, uint64_t val)
|
||||||
{
|
{
|
||||||
@ -675,33 +628,43 @@ int quic_transport_params_encode(unsigned char *buf,
|
|||||||
return pos - head;
|
return pos - head;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Decode transport parameters found in <buf> buffer into <p>, depending on
|
/* Decode transport parameters found in <buf> buffer into <p>. The boolean
|
||||||
* <server> boolean value which must be set to 1 for a server (haproxy listener)
|
* argument <server> must be set according to the origin of the parameters.
|
||||||
* or 0 for a client (connection to a haproxy server).
|
*
|
||||||
* Returns 1 if succeeded, 0 if not.
|
* Returns 1 on success else 0 if decoding is truncated.
|
||||||
*/
|
*/
|
||||||
static enum quic_tp_dec_err
|
int quic_transport_params_decode(struct quic_transport_params *p, int server,
|
||||||
quic_transport_params_decode(struct quic_transport_params *p, int server,
|
|
||||||
const unsigned char *buf, const unsigned char *end)
|
const unsigned char *buf, const unsigned char *end)
|
||||||
{
|
{
|
||||||
enum quic_tp_dec_err err;
|
|
||||||
const unsigned char *pos;
|
const unsigned char *pos;
|
||||||
uint64_t type, len = 0;
|
uint64_t type, len = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
pos = buf;
|
pos = buf;
|
||||||
|
|
||||||
while (pos != end) {
|
while (pos != end) {
|
||||||
if (!quic_transport_param_decode_type_len(&type, &len, &pos, end))
|
if (!quic_transport_param_decode_type_len(&type, &len, &pos, end))
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
|
|
||||||
if (end - pos < len)
|
if (end - pos < len)
|
||||||
return QUIC_TP_DEC_ERR_TRUNC;
|
return 0;
|
||||||
|
|
||||||
err = quic_transport_param_decode(p, server, type, &pos, len);
|
ret = quic_transport_param_decode(p, server, type, &pos, len);
|
||||||
if (err != QUIC_TP_DEC_ERR_NONE)
|
if (!ret)
|
||||||
return err;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check validity of decoded <p> transport parameters. The boolean argument
|
||||||
|
* <server> must be set according to the origin of the parameters.
|
||||||
|
*
|
||||||
|
* Returns 1 if params are valid, else 0 if TRANSPORT_PARAMETER_ERROR must be
|
||||||
|
* emitted.
|
||||||
|
*/
|
||||||
|
static int quic_transport_params_check(const struct quic_transport_params *p, int server)
|
||||||
|
{
|
||||||
/* RFC 9000 7.3. Authenticating Connection IDs
|
/* RFC 9000 7.3. Authenticating Connection IDs
|
||||||
*
|
*
|
||||||
* An endpoint MUST treat the absence of the
|
* An endpoint MUST treat the absence of the
|
||||||
@ -712,9 +675,48 @@ quic_transport_params_decode(struct quic_transport_params *p, int server,
|
|||||||
*/
|
*/
|
||||||
if (!p->initial_source_connection_id_present ||
|
if (!p->initial_source_connection_id_present ||
|
||||||
(server && !p->original_destination_connection_id_present)) {
|
(server && !p->original_destination_connection_id_present)) {
|
||||||
return QUIC_TP_DEC_ERR_INVAL;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* RFC 9000 18.2. Transport Parameter Definitions
|
||||||
|
*
|
||||||
|
* A client MUST NOT include any server-only transport parameter:
|
||||||
|
* original_destination_connection_id, preferred_address,
|
||||||
|
* retry_source_connection_id, or stateless_reset_token.
|
||||||
|
*/
|
||||||
|
if (!server &&
|
||||||
|
(p->original_destination_connection_id_present ||
|
||||||
|
p->retry_source_connection_id.len ||
|
||||||
|
p->with_stateless_reset_token ||
|
||||||
|
p->with_preferred_address)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RFC 9000 18.2. Transport Parameter Definitions
|
||||||
|
*
|
||||||
|
* max_udp_payload_size (0x03): [...]
|
||||||
|
* The default for this parameter is the maximum permitted UDP
|
||||||
|
* payload of 65527. Values below 1200 are invalid.
|
||||||
|
*/
|
||||||
|
if (p->max_udp_payload_size < 1200)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* RFC 9000 18.2. Transport Parameter Definitions
|
||||||
|
*
|
||||||
|
* ack_delay_exponent (0x0a): [...]
|
||||||
|
* Values above 20 are invalid.
|
||||||
|
*/
|
||||||
|
if (p->ack_delay_exponent > QUIC_TP_ACK_DELAY_EXPONENT_LIMIT)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* RFC 9000 18.2. Transport Parameter Definitions
|
||||||
|
*
|
||||||
|
* max_ack_delay (0x0b): [...]
|
||||||
|
* Values of 2^14 or greater are invalid.
|
||||||
|
*/
|
||||||
|
if (p->max_ack_delay >= QUIC_TP_MAX_ACK_DELAY_LIMIT)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* RFC 9000 18.2. Transport Parameter Definitions
|
/* RFC 9000 18.2. Transport Parameter Definitions
|
||||||
*
|
*
|
||||||
* active_connection_id_limit (0x0e):
|
* active_connection_id_limit (0x0e):
|
||||||
@ -724,14 +726,15 @@ quic_transport_params_decode(struct quic_transport_params *p, int server,
|
|||||||
* connection with an error of type TRANSPORT_PARAMETER_ERROR.
|
* connection with an error of type TRANSPORT_PARAMETER_ERROR.
|
||||||
*/
|
*/
|
||||||
if (p->active_connection_id_limit < QUIC_TP_DFLT_ACTIVE_CONNECTION_ID_LIMIT)
|
if (p->active_connection_id_limit < QUIC_TP_DFLT_ACTIVE_CONNECTION_ID_LIMIT)
|
||||||
return QUIC_TP_DEC_ERR_INVAL;
|
return 0;
|
||||||
|
|
||||||
return QUIC_TP_DEC_ERR_NONE;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store transport parameters found in <buf> buffer into <qc> QUIC connection
|
/* Store transport parameters found in <buf> buffer into <qc> QUIC connection.
|
||||||
* depending on <server> value which must be 1 for a server (haproxy listener)
|
* The boolean argument <server> must be set according to the origin of the
|
||||||
* or 0 for a client (connection to a haproxy server).
|
* parameters.
|
||||||
|
*
|
||||||
* Note that peer transport parameters are stored in the TX part of the connection:
|
* Note that peer transport parameters are stored in the TX part of the connection:
|
||||||
* they are used to send packets to the peer with its transport parameters as
|
* they are used to send packets to the peer with its transport parameters as
|
||||||
* limitations.
|
* limitations.
|
||||||
@ -745,7 +748,6 @@ int quic_transport_params_store(struct quic_conn *qc, int server,
|
|||||||
const unsigned char *buf,
|
const unsigned char *buf,
|
||||||
const unsigned char *end, int edata_accepted)
|
const unsigned char *end, int edata_accepted)
|
||||||
{
|
{
|
||||||
enum quic_tp_dec_err err;
|
|
||||||
struct quic_transport_params *tx_params = &qc->tx.params;
|
struct quic_transport_params *tx_params = &qc->tx.params;
|
||||||
struct quic_transport_params *rx_params = &qc->rx.params;
|
struct quic_transport_params *rx_params = &qc->rx.params;
|
||||||
/* Initial source connection ID */
|
/* Initial source connection ID */
|
||||||
@ -761,16 +763,16 @@ int quic_transport_params_store(struct quic_conn *qc, int server,
|
|||||||
/* initialize peer TPs to RFC default value */
|
/* initialize peer TPs to RFC default value */
|
||||||
quic_dflt_transport_params_cpy(tx_params);
|
quic_dflt_transport_params_cpy(tx_params);
|
||||||
|
|
||||||
err = quic_transport_params_decode(tx_params, server, buf, end);
|
if (!quic_transport_params_decode(tx_params, server, buf, end)) {
|
||||||
if (err == QUIC_TP_DEC_ERR_INVAL) {
|
TRACE_ERROR("error on transport parameters decoding", QUIC_EV_TRANSP_PARAMS, qc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quic_transport_params_check(tx_params, server)) {
|
||||||
TRACE_ERROR("invalid transport parameter value", QUIC_EV_TRANSP_PARAMS, qc);
|
TRACE_ERROR("invalid transport parameter value", QUIC_EV_TRANSP_PARAMS, qc);
|
||||||
quic_set_connection_close(qc, quic_err_transport(QC_ERR_TRANSPORT_PARAMETER_ERROR));
|
quic_set_connection_close(qc, quic_err_transport(QC_ERR_TRANSPORT_PARAMETER_ERROR));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else if (err == QUIC_TP_DEC_ERR_TRUNC) {
|
|
||||||
TRACE_ERROR("error on transport parameters decoding", QUIC_EV_TRANSP_PARAMS, qc);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (edata_accepted && !qc_early_tranport_params_validate(qc, tx_params, &etps)) {
|
if (edata_accepted && !qc_early_tranport_params_validate(qc, tx_params, &etps)) {
|
||||||
TRACE_ERROR("could not validate early transport parameters", QUIC_EV_TRANSP_PARAMS, qc);
|
TRACE_ERROR("could not validate early transport parameters", QUIC_EV_TRANSP_PARAMS, qc);
|
||||||
|
|||||||
@ -2087,7 +2087,7 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end,
|
|||||||
/* payload building (ack-eliciting or not frames) */
|
/* payload building (ack-eliciting or not frames) */
|
||||||
payload = pos;
|
payload = pos;
|
||||||
if (ack_frm_len) {
|
if (ack_frm_len) {
|
||||||
if (!qc_build_frm(&pos, end, &ack_frm, pkt, qc))
|
if (!qc_build_frm_pkt(&ack_frm, pkt, &pos, end, qc))
|
||||||
goto no_room;
|
goto no_room;
|
||||||
|
|
||||||
pkt->largest_acked_pn = quic_pktns_get_largest_acked_pn(qel->pktns);
|
pkt->largest_acked_pn = quic_pktns_get_largest_acked_pn(qel->pktns);
|
||||||
@ -2098,7 +2098,7 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end,
|
|||||||
if (!LIST_ISEMPTY(&frm_list)) {
|
if (!LIST_ISEMPTY(&frm_list)) {
|
||||||
struct quic_frame *tmp_cf;
|
struct quic_frame *tmp_cf;
|
||||||
list_for_each_entry_safe(cf, tmp_cf, &frm_list, list) {
|
list_for_each_entry_safe(cf, tmp_cf, &frm_list, list) {
|
||||||
if (!qc_build_frm(&pos, end, cf, pkt, qc)) {
|
if (!qc_build_frm_pkt(cf, pkt, &pos, end, qc)) {
|
||||||
ssize_t room = end - pos;
|
ssize_t room = end - pos;
|
||||||
TRACE_PROTO("Not enough room", QUIC_EV_CONN_TXPKT,
|
TRACE_PROTO("Not enough room", QUIC_EV_CONN_TXPKT,
|
||||||
qc, NULL, NULL, &room);
|
qc, NULL, NULL, &room);
|
||||||
@ -2118,13 +2118,13 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end,
|
|||||||
/* Build a PING frame if needed. */
|
/* Build a PING frame if needed. */
|
||||||
if (add_ping_frm) {
|
if (add_ping_frm) {
|
||||||
frm.type = QUIC_FT_PING;
|
frm.type = QUIC_FT_PING;
|
||||||
if (!qc_build_frm(&pos, end, &frm, pkt, qc))
|
if (!qc_build_frm_pkt(&frm, pkt, &pos, end, qc))
|
||||||
goto no_room;
|
goto no_room;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Build a CONNECTION_CLOSE frame if needed. */
|
/* Build a CONNECTION_CLOSE frame if needed. */
|
||||||
if (cc) {
|
if (cc) {
|
||||||
if (!qc_build_frm(&pos, end, &cc_frm, pkt, qc))
|
if (!qc_build_frm_pkt(&cc_frm, pkt, &pos, end, qc))
|
||||||
goto no_room;
|
goto no_room;
|
||||||
|
|
||||||
pkt->flags |= QUIC_FL_TX_PACKET_CC;
|
pkt->flags |= QUIC_FL_TX_PACKET_CC;
|
||||||
@ -2134,7 +2134,7 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end,
|
|||||||
if (padding_len) {
|
if (padding_len) {
|
||||||
frm.type = QUIC_FT_PADDING;
|
frm.type = QUIC_FT_PADDING;
|
||||||
frm.padding.len = padding_len;
|
frm.padding.len = padding_len;
|
||||||
if (!qc_build_frm(&pos, end, &frm, pkt, qc))
|
if (!qc_build_frm_pkt(&frm, pkt, &pos, end, qc))
|
||||||
goto no_room;
|
goto no_room;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
#include <import/cebis_tree.h>
|
#include <import/cebis_tree.h>
|
||||||
|
|
||||||
#include <haproxy/action.h>
|
#include <haproxy/action.h>
|
||||||
|
#include <haproxy/acme_resolvers.h>
|
||||||
#include <haproxy/api.h>
|
#include <haproxy/api.h>
|
||||||
#include <haproxy/applet.h>
|
#include <haproxy/applet.h>
|
||||||
#include <haproxy/cfgparse.h>
|
#include <haproxy/cfgparse.h>
|
||||||
@ -1326,15 +1327,33 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
|
|||||||
key = XXH32(reader, answer_record->data_len, answer_record->type);
|
key = XXH32(reader, answer_record->data_len, answer_record->type);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DNS_RTYPE_TXT: {
|
||||||
|
/* TXT: sequence of [1-byte length | string bytes] *.
|
||||||
|
* Only the first string is kept, further data is ignored.
|
||||||
|
*/
|
||||||
|
int slen = (unsigned char)reader[0];
|
||||||
|
|
||||||
|
if (answer_record->data_len < 1 || 1 + slen > answer_record->data_len)
|
||||||
|
goto invalid_resp;
|
||||||
|
offset = answer_record->data_len;
|
||||||
|
memcpy(answer_record->data.target, reader + 1, slen);
|
||||||
|
answer_record->data.target[slen] = 0;
|
||||||
|
answer_record->data_len = slen;
|
||||||
|
key = XXH32(answer_record->data.target, slen, answer_record->type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
} /* switch (record type) */
|
} /* switch (record type) */
|
||||||
|
|
||||||
/* Increment the counter for number of records saved into our
|
/* Increment the counter for number of records saved into our
|
||||||
* local response */
|
* local response */
|
||||||
nb_saved_records++;
|
nb_saved_records++;
|
||||||
|
|
||||||
/* Move forward answer_record->data_len for analyzing next
|
/* Move forward past the record data. For SRV and TXT, offset holds
|
||||||
* record in the response */
|
* the wire data length
|
||||||
reader += ((answer_record->type == DNS_RTYPE_SRV)
|
*/
|
||||||
|
reader += ((answer_record->type == DNS_RTYPE_SRV ||
|
||||||
|
answer_record->type == DNS_RTYPE_TXT)
|
||||||
? offset
|
? offset
|
||||||
: answer_record->data_len);
|
: answer_record->data_len);
|
||||||
|
|
||||||
@ -1370,6 +1389,12 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DNS_RTYPE_TXT:
|
||||||
|
if (answer_record->data_len == tmp_record->data_len &&
|
||||||
|
memcmp(answer_record->data.target, tmp_record->data.target, answer_record->data_len) == 0)
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2146,6 +2171,25 @@ int resolv_link_resolution(void *requester, int requester_type, int requester_lo
|
|||||||
? DNS_RTYPE_A
|
? DNS_RTYPE_A
|
||||||
: DNS_RTYPE_AAAA;
|
: DNS_RTYPE_AAAA;
|
||||||
break;
|
break;
|
||||||
|
#if defined(HAVE_ACME)
|
||||||
|
case OBJ_TYPE_ACME_RSLV: {
|
||||||
|
struct acme_rslv *acme_rslv = (struct acme_rslv *)requester;
|
||||||
|
|
||||||
|
req = resolv_get_requester(&acme_rslv->requester,
|
||||||
|
&acme_rslv->obj_type,
|
||||||
|
acme_rslv->success_cb,
|
||||||
|
acme_rslv->error_cb);
|
||||||
|
if (!req)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
hostname_dn = &acme_rslv->hostname_dn;
|
||||||
|
hostname_dn_len = acme_rslv->hostname_dn_len;
|
||||||
|
resolvers = acme_rslv->resolvers;
|
||||||
|
|
||||||
|
query_type = DNS_RTYPE_TXT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -2533,6 +2577,11 @@ struct task *process_resolvers(struct task *t, void *context, unsigned int state
|
|||||||
/* Always perform the resolution */
|
/* Always perform the resolution */
|
||||||
must_run = 1;
|
must_run = 1;
|
||||||
break;
|
break;
|
||||||
|
case OBJ_TYPE_ACME_RSLV:
|
||||||
|
/* Always perform the resolution */
|
||||||
|
must_run = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
75
src/server.c
75
src/server.c
@ -37,6 +37,7 @@
|
|||||||
#include <haproxy/namespace.h>
|
#include <haproxy/namespace.h>
|
||||||
#include <haproxy/port_range.h>
|
#include <haproxy/port_range.h>
|
||||||
#include <haproxy/protocol.h>
|
#include <haproxy/protocol.h>
|
||||||
|
#include <haproxy/proto_tcp.h>
|
||||||
#include <haproxy/proxy.h>
|
#include <haproxy/proxy.h>
|
||||||
#include <haproxy/queue.h>
|
#include <haproxy/queue.h>
|
||||||
#include <haproxy/quic_tp.h>
|
#include <haproxy/quic_tp.h>
|
||||||
@ -1452,6 +1453,15 @@ static int srv_parse_proto(char **args, int *cur_arg,
|
|||||||
memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
|
memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
|
||||||
return ERR_ALERT | ERR_FATAL;
|
return ERR_ALERT | ERR_FATAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newsrv->mux_proto->mux->flags & MX_FL_EXPERIMENTAL) {
|
||||||
|
if (!experimental_directives_allowed) {
|
||||||
|
memprintf(err, "'%s' : '%s' protocol is experimental, must be allowed via a global 'expose-experimental-directives'",
|
||||||
|
args[*cur_arg], args[*cur_arg + 1]);
|
||||||
|
return ERR_ALERT | ERR_FATAL;
|
||||||
|
}
|
||||||
|
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2938,7 +2948,9 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
|
|||||||
}
|
}
|
||||||
srv->use_ssl = src->use_ssl;
|
srv->use_ssl = src->use_ssl;
|
||||||
srv->check.addr = src->check.addr;
|
srv->check.addr = src->check.addr;
|
||||||
|
srv->check.proto = src->check.proto;
|
||||||
srv->agent.addr = src->agent.addr;
|
srv->agent.addr = src->agent.addr;
|
||||||
|
srv->agent.proto = src->agent.proto;
|
||||||
srv->check.use_ssl = src->check.use_ssl;
|
srv->check.use_ssl = src->check.use_ssl;
|
||||||
srv->check.port = src->check.port;
|
srv->check.port = src->check.port;
|
||||||
if (src->check.sni != NULL)
|
if (src->check.sni != NULL)
|
||||||
@ -2966,14 +2978,14 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
|
|||||||
srv->agent.use_ssl = src->agent.use_ssl;
|
srv->agent.use_ssl = src->agent.use_ssl;
|
||||||
srv->agent.port = src->agent.port;
|
srv->agent.port = src->agent.port;
|
||||||
|
|
||||||
if (src->agent.tcpcheck_rules) {
|
if (src->agent.tcpcheck) {
|
||||||
srv->agent.tcpcheck_rules = calloc(1, sizeof(*srv->agent.tcpcheck_rules));
|
srv->agent.tcpcheck = calloc(1, sizeof(*srv->agent.tcpcheck));
|
||||||
if (srv->agent.tcpcheck_rules) {
|
if (srv->agent.tcpcheck) {
|
||||||
srv->agent.tcpcheck_rules->flags = src->agent.tcpcheck_rules->flags;
|
srv->agent.tcpcheck->flags = (src->agent.tcpcheck->flags & ~TCPCHK_FL_UNUSED_RS);
|
||||||
srv->agent.tcpcheck_rules->list = src->agent.tcpcheck_rules->list;
|
srv->agent.tcpcheck->rs = src->agent.tcpcheck->rs;
|
||||||
LIST_INIT(&srv->agent.tcpcheck_rules->preset_vars);
|
LIST_INIT(&srv->agent.tcpcheck->preset_vars);
|
||||||
dup_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars,
|
dup_tcpcheck_vars(&srv->agent.tcpcheck->preset_vars,
|
||||||
&src->agent.tcpcheck_rules->preset_vars);
|
&src->agent.tcpcheck->preset_vars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3120,7 +3132,7 @@ struct server *new_server(struct proxy *proxy)
|
|||||||
srv->check.status = HCHK_STATUS_INI;
|
srv->check.status = HCHK_STATUS_INI;
|
||||||
srv->check.server = srv;
|
srv->check.server = srv;
|
||||||
srv->check.proxy = proxy;
|
srv->check.proxy = proxy;
|
||||||
srv->check.tcpcheck_rules = &proxy->tcpcheck_rules;
|
srv->check.tcpcheck = &proxy->tcpcheck;
|
||||||
|
|
||||||
srv->agent.obj_type = OBJ_TYPE_CHECK;
|
srv->agent.obj_type = OBJ_TYPE_CHECK;
|
||||||
srv->agent.status = HCHK_STATUS_INI;
|
srv->agent.status = HCHK_STATUS_INI;
|
||||||
@ -4635,6 +4647,11 @@ out:
|
|||||||
set_srv_agent_addr(s, &sk);
|
set_srv_agent_addr(s, &sk);
|
||||||
if (port)
|
if (port)
|
||||||
set_srv_agent_port(s, new_port);
|
set_srv_agent_port(s, new_port);
|
||||||
|
/* Agent currently only uses TCP */
|
||||||
|
if (sk.ss_family == AF_INET)
|
||||||
|
s->agent.proto = &proto_tcpv4;
|
||||||
|
else
|
||||||
|
s->agent.proto = &proto_tcpv6;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -4646,7 +4663,8 @@ out:
|
|||||||
*/
|
*/
|
||||||
const char *srv_update_check_addr_port(struct server *s, const char *addr, const char *port)
|
const char *srv_update_check_addr_port(struct server *s, const char *addr, const char *port)
|
||||||
{
|
{
|
||||||
struct sockaddr_storage sk;
|
struct sockaddr_storage *sk = NULL;
|
||||||
|
struct protocol *proto = NULL;
|
||||||
struct buffer *msg;
|
struct buffer *msg;
|
||||||
int new_port;
|
int new_port;
|
||||||
|
|
||||||
@ -4658,8 +4676,8 @@ const char *srv_update_check_addr_port(struct server *s, const char *addr, const
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (addr) {
|
if (addr) {
|
||||||
memset(&sk, 0, sizeof(struct sockaddr_storage));
|
sk = str2sa_range(addr, NULL, NULL, NULL, NULL, &proto, NULL, NULL, NULL, NULL, NULL, 0);
|
||||||
if (str2ip2(addr, &sk, 0) == NULL) {
|
if (sk == NULL) {
|
||||||
chunk_appendf(msg, "invalid addr '%s'", addr);
|
chunk_appendf(msg, "invalid addr '%s'", addr);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -4683,8 +4701,10 @@ out:
|
|||||||
if (msg->data)
|
if (msg->data)
|
||||||
return msg->area;
|
return msg->area;
|
||||||
else {
|
else {
|
||||||
if (addr)
|
if (sk) {
|
||||||
s->check.addr = sk;
|
s->check.addr = *sk;
|
||||||
|
s->check.proto = proto;
|
||||||
|
}
|
||||||
if (port)
|
if (port)
|
||||||
s->check.port = new_port;
|
s->check.port = new_port;
|
||||||
|
|
||||||
@ -6230,7 +6250,7 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
|
|||||||
int proto_mode = conn_pr_mode_to_proto_mode(be->mode);
|
int proto_mode = conn_pr_mode_to_proto_mode(be->mode);
|
||||||
const struct mux_proto_list *mux_ent;
|
const struct mux_proto_list *mux_ent;
|
||||||
|
|
||||||
mux_ent = conn_get_best_mux_entry(srv->mux_proto->token, PROTO_SIDE_BE, proto_mode);
|
mux_ent = conn_get_best_mux_entry(srv->mux_proto->token, PROTO_SIDE_BE, srv_is_quic(srv), proto_mode);
|
||||||
|
|
||||||
if (!mux_ent || !isteq(mux_ent->token, srv->mux_proto->token)) {
|
if (!mux_ent || !isteq(mux_ent->token, srv->mux_proto->token)) {
|
||||||
ha_alert("MUX protocol is not usable for server.\n");
|
ha_alert("MUX protocol is not usable for server.\n");
|
||||||
@ -6251,7 +6271,7 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
|
|||||||
/* ensure minconn/maxconn consistency */
|
/* ensure minconn/maxconn consistency */
|
||||||
srv_minmax_conn_apply(srv);
|
srv_minmax_conn_apply(srv);
|
||||||
|
|
||||||
if (srv->use_ssl == 1 || (srv->proxy->options & PR_O_TCPCHK_SSL) ||
|
if (srv->use_ssl == 1 || (srv->check.tcpcheck->flags & TCPCHK_FL_USE_SSL) ||
|
||||||
srv->check.use_ssl == 1) {
|
srv->check.use_ssl == 1) {
|
||||||
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv) {
|
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv) {
|
||||||
if (xprt_get(XPRT_SSL)->prepare_srv(srv))
|
if (xprt_get(XPRT_SSL)->prepare_srv(srv))
|
||||||
@ -6675,13 +6695,17 @@ int srv_apply_track(struct server *srv, struct proxy *curproxy)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curproxy != px &&
|
if (curproxy != px) {
|
||||||
(curproxy->options & PR_O_DISABLE404) != (px->options & PR_O_DISABLE404)) {
|
int val1 = curproxy->tcpcheck.rs && (curproxy->tcpcheck.rs->flags & TCPCHK_RULES_DISABLE404);
|
||||||
|
int val2 = px->tcpcheck.rs && (px->tcpcheck.rs->flags & TCPCHK_RULES_DISABLE404);
|
||||||
|
|
||||||
|
if (val1 != val2) {
|
||||||
ha_alert("unable to use %s/%s for"
|
ha_alert("unable to use %s/%s for"
|
||||||
"tracking: disable-on-404 option inconsistency.\n",
|
"tracking: disable-on-404 option inconsistency.\n",
|
||||||
px->id, strack->id);
|
px->id, strack->id);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
srv->track = strack;
|
srv->track = strack;
|
||||||
srv->tracknext = strack->trackers;
|
srv->tracknext = strack->trackers;
|
||||||
@ -7612,7 +7636,7 @@ static void srv_close_idle_conns(struct server *srv)
|
|||||||
|
|
||||||
REGISTER_SERVER_DEINIT(srv_close_idle_conns);
|
REGISTER_SERVER_DEINIT(srv_close_idle_conns);
|
||||||
|
|
||||||
/* config parser for global "tune.idle-pool.shared", accepts "on" or "off" */
|
/* config parser for global "tune.idle-pool.shared", accepts "full", "on" or "off" */
|
||||||
static int cfg_parse_idle_pool_shared(char **args, int section_type, struct proxy *curpx,
|
static int cfg_parse_idle_pool_shared(char **args, int section_type, struct proxy *curpx,
|
||||||
const struct proxy *defpx, const char *file, int line,
|
const struct proxy *defpx, const char *file, int line,
|
||||||
char **err)
|
char **err)
|
||||||
@ -7620,12 +7644,17 @@ static int cfg_parse_idle_pool_shared(char **args, int section_type, struct prox
|
|||||||
if (too_many_args(1, args, err, NULL))
|
if (too_many_args(1, args, err, NULL))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (strcmp(args[1], "on") == 0)
|
if (strcmp(args[1], "full") == 0) {
|
||||||
global.tune.options |= GTUNE_IDLE_POOL_SHARED;
|
global.tune.options |= GTUNE_IDLE_POOL_SHARED;
|
||||||
else if (strcmp(args[1], "off") == 0)
|
global.tune.tg_takeover = FULL_THREADGROUP_TAKEOVER;
|
||||||
|
} else if (strcmp(args[1], "on") == 0) {
|
||||||
|
global.tune.options |= GTUNE_IDLE_POOL_SHARED;
|
||||||
|
global.tune.tg_takeover = RESTRICTED_THREADGROUP_TAKEOVER;
|
||||||
|
} else if (strcmp(args[1], "off") == 0) {
|
||||||
global.tune.options &= ~GTUNE_IDLE_POOL_SHARED;
|
global.tune.options &= ~GTUNE_IDLE_POOL_SHARED;
|
||||||
else {
|
global.tune.tg_takeover = NO_THREADGROUP_TAKEOVER;
|
||||||
memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]);
|
} else {
|
||||||
|
memprintf(err, "'%s' expects 'auto', 'on' or 'off' but got '%s'.", args[0], args[1]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -241,6 +241,9 @@ int session_accept_fd(struct connection *cli_conn)
|
|||||||
if (l->bind_conf->options & BC_O_ACC_CIP)
|
if (l->bind_conf->options & BC_O_ACC_CIP)
|
||||||
cli_conn->flags |= CO_FL_ACCEPT_CIP;
|
cli_conn->flags |= CO_FL_ACCEPT_CIP;
|
||||||
|
|
||||||
|
if (l->bind_conf->mux_proto && isteq(l->bind_conf->mux_proto->token, ist("qmux")))
|
||||||
|
cli_conn->flags |= (CO_FL_QSTRM_RECV|CO_FL_QSTRM_SEND);
|
||||||
|
|
||||||
/* Add the handshake pseudo-XPRT */
|
/* Add the handshake pseudo-XPRT */
|
||||||
if (cli_conn->flags & (CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP)) {
|
if (cli_conn->flags & (CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP)) {
|
||||||
if (xprt_add_hs(cli_conn) != 0)
|
if (xprt_add_hs(cli_conn) != 0)
|
||||||
|
|||||||
@ -162,8 +162,8 @@ struct connection *sock_accept_conn(struct listener *l, int *status)
|
|||||||
case ENFILE:
|
case ENFILE:
|
||||||
if (p)
|
if (p)
|
||||||
send_log(p, LOG_EMERG,
|
send_log(p, LOG_EMERG,
|
||||||
"Proxy %s reached system FD limit (maxsock=%d). Please check system tunables.\n",
|
"Proxy %s reached system FD limit (actconn=%d). Please check system tunables.\n",
|
||||||
p->id, global.maxsock);
|
p->id, actconn);
|
||||||
ret = CO_AC_PAUSE;
|
ret = CO_AC_PAUSE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -179,8 +179,8 @@ struct connection *sock_accept_conn(struct listener *l, int *status)
|
|||||||
case ENOMEM:
|
case ENOMEM:
|
||||||
if (p)
|
if (p)
|
||||||
send_log(p, LOG_EMERG,
|
send_log(p, LOG_EMERG,
|
||||||
"Proxy %s reached system memory limit (maxsock=%d). Please check system tunables.\n",
|
"Proxy %s reached system memory limit (actconn=%d). Please check system tunables.\n",
|
||||||
p->id, global.maxsock);
|
p->id, actconn);
|
||||||
ret = CO_AC_PAUSE;
|
ret = CO_AC_PAUSE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@ -1510,6 +1510,9 @@ static int cli_parse_update_ocsp_response(char **args, char *payload, struct app
|
|||||||
unsigned char key[OCSP_MAX_CERTID_ASN1_LENGTH] = {};
|
unsigned char key[OCSP_MAX_CERTID_ASN1_LENGTH] = {};
|
||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (!*args[3]) {
|
if (!*args[3]) {
|
||||||
memprintf(&err, "'update ssl ocsp-response' expects a filename\n");
|
memprintf(&err, "'update ssl ocsp-response' expects a filename\n");
|
||||||
return cli_dynerr(appctx, err);
|
return cli_dynerr(appctx, err);
|
||||||
@ -1590,6 +1593,9 @@ static int cli_parse_set_ocspresponse(char **args, char *payload, struct appctx
|
|||||||
char *err = NULL;
|
char *err = NULL;
|
||||||
int i, j, ret;
|
int i, j, ret;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (!payload)
|
if (!payload)
|
||||||
payload = args[3];
|
payload = args[3];
|
||||||
|
|
||||||
@ -1630,10 +1636,12 @@ static int cli_parse_set_ocspresponse(char **args, char *payload, struct appctx
|
|||||||
static int cli_parse_show_ocspresponse(char **args, char *payload, struct appctx *appctx, void *private)
|
static int cli_parse_show_ocspresponse(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
{
|
{
|
||||||
#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
|
#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
|
||||||
|
|
||||||
struct show_ocspresp_cli_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_ocspresp_cli_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
int arg_idx = 3;
|
int arg_idx = 3;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (*args[3]) {
|
if (*args[3]) {
|
||||||
struct certificate_ocsp *ocsp = NULL;
|
struct certificate_ocsp *ocsp = NULL;
|
||||||
char key[OCSP_MAX_CERTID_ASN1_LENGTH] = {};
|
char key[OCSP_MAX_CERTID_ASN1_LENGTH] = {};
|
||||||
@ -1817,6 +1825,9 @@ static int cli_parse_show_ocsp_updates(char **args, char *payload, struct appctx
|
|||||||
#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
|
#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
|
||||||
struct show_ocsp_updates_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_ocsp_updates_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
|
HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -6957,9 +6957,30 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state)
|
|||||||
if (ctx->conn->xprt_ctx == ctx) {
|
if (ctx->conn->xprt_ctx == ctx) {
|
||||||
int closed_connection = 0;
|
int closed_connection = 0;
|
||||||
|
|
||||||
if (!ctx->conn->mux)
|
if (!ctx->conn->mux) {
|
||||||
|
if (ctx->conn->flags & (CO_FL_QSTRM_RECV|CO_FL_QSTRM_SEND)) {
|
||||||
|
const struct xprt_ops *ops = xprt_get(XPRT_QSTRM);
|
||||||
|
void *xprt_ctx_hs = NULL;
|
||||||
|
|
||||||
|
ret = ops->init(conn, &xprt_ctx_hs);
|
||||||
|
BUG_ON(ret);
|
||||||
|
|
||||||
|
ret = ops->add_xprt(conn, xprt_ctx_hs,
|
||||||
|
conn->xprt_ctx, conn->xprt, NULL, NULL);
|
||||||
|
BUG_ON(ret);
|
||||||
|
|
||||||
|
conn->xprt = ops;
|
||||||
|
conn->xprt_ctx = xprt_ctx_hs;
|
||||||
|
|
||||||
|
|
||||||
|
ret = conn->xprt->start(conn, xprt_ctx_hs);
|
||||||
|
BUG_ON(ret);
|
||||||
|
}
|
||||||
|
else
|
||||||
ret = conn_create_mux(ctx->conn, &closed_connection);
|
ret = conn_create_mux(ctx->conn, &closed_connection);
|
||||||
if (ret >= 0 && !woke && ctx->conn->mux && ctx->conn->mux->wake) {
|
}
|
||||||
|
|
||||||
|
if (ret >= 0 && ctx->conn->mux && !woke && ctx->conn->mux && ctx->conn->mux->wake) {
|
||||||
ret = CALL_MUX_WITH_RET(ctx->conn->mux, wake(ctx->conn));
|
ret = CALL_MUX_WITH_RET(ctx->conn->mux, wake(ctx->conn));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
closed_connection = 1;
|
closed_connection = 1;
|
||||||
@ -8086,6 +8107,9 @@ static int cli_parse_show_tlskeys(char **args, char *payload, struct appctx *app
|
|||||||
{
|
{
|
||||||
struct show_keys_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_keys_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
/* no parameter, shows only file list */
|
/* no parameter, shows only file list */
|
||||||
if (!*args[2]) {
|
if (!*args[2]) {
|
||||||
ctx->names_only = 1;
|
ctx->names_only = 1;
|
||||||
@ -8110,6 +8134,9 @@ static int cli_parse_set_tlskeys(char **args, char *payload, struct appctx *appc
|
|||||||
struct tls_keys_ref *ref;
|
struct tls_keys_ref *ref;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
/* Expect two parameters: the filename and the new new TLS key in encoding */
|
/* Expect two parameters: the filename and the new new TLS key in encoding */
|
||||||
if (!*args[3] || !*args[4])
|
if (!*args[3] || !*args[4])
|
||||||
return cli_err(appctx, "'set ssl tls-key' expects a filename and the new TLS key in base64 encoding.\n");
|
return cli_err(appctx, "'set ssl tls-key' expects a filename and the new TLS key in base64 encoding.\n");
|
||||||
|
|||||||
@ -289,7 +289,7 @@ void stats_dump_html_info(struct stconn *sc)
|
|||||||
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
|
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
|
||||||
"<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
|
"<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
|
||||||
"",
|
"",
|
||||||
(ctx->flags & STAT_F_HIDEVER) ? "" : (stats_version_string),
|
(ctx->flags & STAT_F_SHOWVER) ? (stats_version_string) : "",
|
||||||
pid, (ctx->flags & STAT_F_SHNODE) ? " on " : "",
|
pid, (ctx->flags & STAT_F_SHNODE) ? " on " : "",
|
||||||
(ctx->flags & STAT_F_SHNODE) ? (uri->node ? uri->node : global.node) : "",
|
(ctx->flags & STAT_F_SHNODE) ? (uri->node ? uri->node : global.node) : "",
|
||||||
(ctx->flags & STAT_F_SHDESC) ? ": " : "",
|
(ctx->flags & STAT_F_SHDESC) ? ": " : "",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user