Compare commits

..

No commits in common. "master" and "v3.4-dev13" have entirely different histories.

71 changed files with 346 additions and 955 deletions

View file

@ -1,89 +1,6 @@
ChangeLog :
===========
2026/05/26 : 3.4-dev14
- MINOR: config: shm-stats-file is no longer experimental
- BUILD: proxy: unstatify the proxies_del_lock to avoid a warning without threads
- BUG/MEDIUM: net_helper: fix a remaining possibly infinite loop in converters
- MINOR: ssl_sock: remove unneeded check on QMux flags
- MINOR: connection: define xprt_add_l6hs()
- MINOR: xprt_qmux: define default value for get_alpn
- MINOR: connection: define mask CO_FL_WAIT_XPRT_L6
- MINOR: session: support QMux in clear on FE side
- MINOR: backend: support QMux in clear for BE side
- BUG/MINOR: ocsp: Manage date too far away in the future
- MINOR: mux_quic: handle STOP_SENDING in QMux
- MINOR: mux_quic: handle MAX_STREAMS for uni stream in QMux
- MINOR: mux_quic: do not crash on unhandled QMux frame reception
- BUG/MEDIUM: applet: Properly handle receives of size 0
- BUG/MEDIUM: resolvers: Fix test on dn label size in resolv_dn_label_to_str()
- BUG/MEDIUM: ssl-gencert: Unlock LRU cache if failing to generate certificate
- BUG/MINOR: quic: fix ODCID lookup from derived value
- BUG/MEDIUM: dict: hold lock while decrementing refcount in dict_entry_unref
- BUG/MINOR: tcpchecks: Limit parsing of agent-check reply to the buffer
- BUG/MEDIUM: hlua: Fix integer underflow when receiving line from lua cosocket
- BUG/MEDIUM: cli: Fix parsing of pattern finishing a command payload
- BUG/MEDIUM: acme: NUL terminate response buffer before PEM parsing
- BUILD: intops: mask the fail value in array_size_or_fail()
- BUG/MEDIUM: log-forward: make sure the month is unsigned
- BUG/MEDIUM: regex: allocate a large enough pcre2 match for all matches
- BUG/MEDIUM: tcpcheck/spoe: bound the SPOP error code to valid values
- BUG/MEDIUM: cache: fix a refcount leak for missed secondary entries
- BUG/MINOR: log: free logformat expr on compile failure in cfg_parse_log_profile
- BUG/MINOR: resolvers: fix room for trailing zero in resolv_dn_label_to_str()
- BUG/MINOR: resolvers: fix risk of appending garbage past the domain name
- BUG/MINOR: mux-h2: validate HEADERS frame length before reading stream dep
- BUG/MINOR: log: look for the end of priority before the end of the buffer
- BUG/MINOR: dict: fix refcount race on insert collision
- BUG/MINOR: init: use more than ha_random64() for the cluster secret
- BUG/MINOR: sample: limit the be2hex converter's chunk size
- CLEANUP: resolvers: use read_n32() instead of open-coded big-endian read
- CLEANUP: resolvers: remove pool_free(NULL) in SRV additional record matching
- CLEANUP: resolvers: fix comment typos and wrong filenames in file headers
- BUG/MINOR: haterm: fix the random suffix multiplication
- MINOR: haterm: enable h3 for TCP bindings
- MINOR: haterm: do not emit a warning when not using SSL
- BUG/MEDIUM: h1: drop headers whose names contain invalid chars
- BUG/MEDIUM: h1: limit status codes to 3 digits by default
- BUG/MEDIUM: cache: always verify the primary hash in get_secondary_entry()
- BUG/MINOR: cache: also recognize directives in the form "token="
- BUG/MINOR: resolvers: relax size checks in authority record parsing
- BUG/MINOR: sample: request an extra output byte for the url_dec converter
- BUG/MINOR: http-fetch: check against the whole token in get_http_auth()
- BUG/MEDIUM: acme: protect against risk of null-deref on connection failure
- BUG/MINOR: http-ext: always check remaining data when reading rfc7239 nodeport
- BUG/MINOR: base64: return empty string for empty input in base64dec()
- BUG/MINOR: payload: fix the handshake length bounds check smp_client_hello_parse()
- BUG/MINOR: ssl-hello: make use of the null-terminated servername
- BUG/MINOR: resolvers: switch to a better PRNG for query IDs
- BUG/MINOR: addons/51d: NUL-terminate headers before passing them to Trie API
- BUG/MEDIUM: tools: insert an XXH64 layer on the PRNG output
- MINOR: tools: provide a function to generate a hashed random pair
- MEDIUM: init: fall back to ha_random64_pair_hashed() for the cluster secret
- MEDIUM: tools: use the hashed random pair for UUID generation
- MEDIUM: h1: use ha_random64_pair_hashed() for the WebSocket key
- MEDIUM: quic: use ha_random64_pair_hashed() to generate the QUIC retry tokens
- MEDIUM: tools: switch the main PRNG to a thread-local xoshiro256**
- BUG/MEDIUM: h3: reject client push stream
- BUG/MINOR: h3: reject server push stream
- BUG/MINOR: h3: reject client CANCEL_PUSH frame
- BUG/MINOR: h3: adjust error on PUSH_PROMISE frame reception
- BUG/MINOR: h3: reject server MAX_PUSH_ID frame
- BUG/MEDIUM: auth: fix unconfigured password NULL deref
- BUG/MINOR: h3: add missing break on rcv_buf()
- BUG/MINOR: hlua: prevent Lua from passing CR/LF/NUL in HTTP headers
- BUG/MINOR: qmux: do not crash on frame parsing issue
- BUG/MINOR: quic: reject packet too short for HP decryption
- BUG/MINOR: jwe: enforce GCM tag length to 128 bits
- BUG/MEDIUM: jwe: substitute random CEK on RSA1_5 decryption failure per RFC 7516 #11.5
- BUG/MEDIUM: mux-fcgi: reject stream ID 0 for application records
- MINOR: http: Add function to remove all occurrences of a value in a header
- MINOR: h1: Add a H1M flag to specify a non-empty 'Upgrade:' header was parsed
- BUG/MEDIUM: h1-htx: Sanitize parsing to properly handle upgrade requests
- BUG/MINOR: mux-fcgi: Use relative offset to compute contig data in demux buf
- BUG/MINOR: mux-spop: Use relative offset to compute contig data in demux buf
- CLEANUP: mux-fcgi/mux-spop: Remove copy/pasted comment about slow realign
2026/05/20 : 3.4-dev13
- BUG/MINOR: backend: correct parameter value validation in get_server_ph_post()
- BUG/MINOR: config/dns: properly fail on duplicate nameserver name detection

View file

@ -1,2 +1,2 @@
$Format:%ci$
2026/05/26
2026/05/20

View file

@ -1 +1 @@
3.4-dev14
3.4-dev13

View file

@ -303,7 +303,6 @@ static void _51d_init_device_offsets(fiftyoneDegreesDeviceOffsets *offsets) {
static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets)
{
struct buffer *temp = get_trash_chunk();
struct channel *chn;
struct htx *htx;
struct http_hdr_ctx ctx;
@ -325,15 +324,7 @@ static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOff
if (http_find_header(htx, name, &ctx, 1)) {
(offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i);
/* Copy value into trash and NUL-terminate before passing to the
* 51Degrees Trie API, which expects a C string.
*/
if (ctx.value.len >= temp->size)
continue;
memcpy(temp->area, ctx.value.ptr, ctx.value.len);
temp->area[ctx.value.len] = '\0';
temp->data = ctx.value.len + 1;
(offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, temp->area);
(offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, ctx.value.ptr);
offsets->size++;
}
}

View file

@ -3,7 +3,7 @@
Configuration Manual
----------------------
version 3.4
2026/05/26
2026/05/20
This document covers the configuration language as implemented in the version
@ -3329,7 +3329,7 @@ setenv <name> <value>
the configuration file sees the new value. See also "presetenv", "resetenv",
and "unsetenv".
shm-stats-file <name>
shm-stats-file <name> [ EXPERIMENTAL ]
When this directive is set, it enables the use of shared memory for storing
stats counters. <name> is used as argument to shm_open() to open the shared
memory at a unique location. It also means that the directive is only
@ -3345,7 +3345,7 @@ shm-stats-file <name>
See also "guid", "guid-prefix" and "shm-stats-file-max-objects"
shm-stats-file-max-objects <number>
shm-stats-file-max-objects <number> [ EXPERIMENTAL ]
This setting defines the maximum number of objects the shared memory used
for shared counters will be able to store per thread group. It is directly
related to the maximum memory size of the shm and is used to "premap" the
@ -9962,7 +9962,7 @@ no option accept-unsafe-violations-in-http-request
When this option is set, the following rules are observed:
* In H1 only, invalid characters, including NULL character, in header name
will not be rejected; however the header will be dropped.
will be accepted;
* In H1 only, NULL character in header value will be accepted;
@ -10027,11 +10027,8 @@ no option accept-unsafe-violations-in-http-response
When this option is set, the following rules are observed:
* In H1 only, status codes longer than 3 digits but whose value fits in 16
bits are not rejected.
* In H1 only, invalid characters, including NULL character, in header name
will not be rejected; however the header will be dropped.
will be accepted;
* In H1 only, NULL character in header value will be accepted;

View file

@ -179,8 +179,6 @@ enum {
/* below we have all handshake flags grouped into one */
CO_FL_HANDSHAKE = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
CO_FL_WAIT_XPRT = CO_FL_WAIT_L4_CONN | CO_FL_HANDSHAKE | CO_FL_WAIT_L6_CONN,
/* handshake running on top of a layer6 */
CO_FL_WAIT_XPRT_L6 = CO_FL_QMUX_SEND | CO_FL_QMUX_RECV,
CO_FL_SSL_WAIT_HS = 0x08000000, /* wait for an SSL handshake to complete */

View file

@ -114,7 +114,6 @@ int conn_reverse(struct connection *conn);
const char *conn_err_code_name(struct connection *c);
const char *conn_err_code_str(struct connection *c);
int xprt_add_hs(struct connection *conn);
int xprt_add_l6hs(struct connection *conn, int xprt);
void register_mux_proto(struct mux_proto_list *list);
static inline void conn_report_term_evt(struct connection *conn, enum term_event_loc loc, unsigned char type);

View file

@ -98,7 +98,7 @@ enum h1m_state {
#define H1_MF_UPG_WEBSOCKET 0x00008000 // Set for a Websocket upgrade handshake
#define H1_MF_TE_CHUNKED 0x00010000 // T-E "chunked"
#define H1_MF_TE_OTHER 0x00020000 // T-E other than supported ones found (only "chunked" is supported for now)
#define H1_MF_UPG_HDR 0x00040000 // non-empty Upgrapde header found
/* unused: 0x00040000 */
#define H1_MF_NOT_HTTP 0x00080000 // Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted)
/* Mask to use to reset H1M flags when we restart headers parsing.
*

View file

@ -326,50 +326,6 @@ static inline int is_immutable_header(struct ist hdr)
}
}
/* This function parses comma-separated values from <hv> and rewrite it in place,
* skip all occurrences of <value>. It is the caller responsibility to deal with
* empty header value.
*/
static inline void http_remove_header_value(struct ist *hv, struct ist value)
{
char *e, *n, *p;
struct ist word;
word.ptr = hv->ptr - 1; // -1 for next loop's pre-increment
p = hv->ptr;
e = hv->ptr + hv->len;
hv->len = 0;
while (++word.ptr < e) {
/* skip leading delimiter and blanks */
if (HTTP_IS_LWS(*word.ptr))
continue;
n = http_find_hdr_value_end(word.ptr, e); // next comma or end of line
word.len = n - word.ptr;
/* trim trailing blanks */
while (word.len && HTTP_IS_LWS(word.ptr[word.len-1]))
word.len--;
if (isteqi(word, value))
goto skip_val;
if (hv->ptr + hv->len == p) {
/* no rewrite done till now */
hv->len = n - hv->ptr;
}
else {
if (hv->len)
hv->ptr[hv->len++] = ',';
istcat(hv, word, e - hv->ptr);
}
skip_val:
word.ptr = p = n;
}
}
#endif /* _HAPROXY_HTTP_H */
/*

View file

@ -141,7 +141,6 @@
#define HTX_SL_F_CONN_UPG 0x00001000 /* The message contains "connection: upgrade" header */
#define HTX_SL_F_BODYLESS_RESP 0x00002000 /* The response to this message is bodyless (only for request) */
#define HTX_SL_F_NOT_HTTP 0x00004000 /* Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted) */
#define HTX_SL_F_UPG_HDR 0x00008000 /* non-empty Upgrapde header found */
/* This function is used to report flags in debugging tools. Please reflect
* below any single-bit flag addition above in the same order via the
@ -158,8 +157,7 @@ static forceinline char *hsl_show_flags(char *buf, size_t len, const char *delim
_(HTX_SL_F_CLEN, _(HTX_SL_F_CHNK, _(HTX_SL_F_VER_11,
_(HTX_SL_F_BODYLESS, _(HTX_SL_F_HAS_SCHM, _(HTX_SL_F_SCHM_HTTP,
_(HTX_SL_F_SCHM_HTTPS, _(HTX_SL_F_HAS_AUTHORITY,
_(HTX_SL_F_NORMALIZED_URI, _(HTX_SL_F_CONN_UPG, _(HTX_SL_F_BODYLESS_RESP,
_(HTX_SL_F_NOT_HTTP, _(HTX_SL_F_UPG_HDR))))))))))))))));
_(HTX_SL_F_NORMALIZED_URI, _(HTX_SL_F_CONN_UPG)))))))))))));
/* epilogue */
_(~0U);
return buf;

View file

@ -121,8 +121,8 @@ static inline size_t array_size_or_fail(size_t m, size_t n)
{
size_t size;
if (unlikely(mulsz_overflow(m, n, &size)))
return DISGUISE(~(size_t)0);
if (mulsz_overflow(m, n, &size))
return ~(size_t)0;
return size;
}

View file

@ -43,14 +43,6 @@
#define QPACK_DEC_INST_SCCL 0x40 // Stream Cancellation
#define QPACK_DEC_INST_SACK 0x80 // Section Acknowledgment
/* Encoded field line bitmasks (shared between encoder and decoder) */
#define QPACK_EFL_BITMASK 0xf0
#define QPACK_LFL_WPBNM 0x00 // Literal field line with post-base name reference
#define QPACK_IFL_WPBI 0x10 // Indexed field line with post-based index
#define QPACK_LFL_WLN_BIT 0x20 // Literal field line with literal name
#define QPACK_LFL_WNR_BIT 0x40 // Literal field line with name reference
#define QPACK_IFL_BIT 0x80 // Indexed field line
/* RFC 9204 6. Error Handling */
enum qpack_err {
QPACK_ERR_DECOMPRESSION_FAILED = 0x200,

View file

@ -93,30 +93,24 @@ static inline struct ist qpack_get_value(const struct qpack_dht *dht, const stru
return ret;
}
/* takes an absolute idx (including static table offset), returns the associated name */
/* takes an idx, returns the associated name */
static inline struct ist qpack_idx_to_name(const struct qpack_dht *dht, uint32_t idx)
{
const struct qpack_dte *dte;
if (idx < QPACK_SHT_SIZE)
return ist("### ERR ###"); /* static table entries not accessible via dht */
dte = qpack_get_dte(dht, idx - QPACK_SHT_SIZE);
dte = qpack_get_dte(dht, idx);
if (!dte)
return ist("### ERR ###"); // error
return qpack_get_name(dht, dte);
}
/* takes an absolute idx (including static table offset), returns the associated value */
/* takes an idx, returns the associated value */
static inline struct ist qpack_idx_to_value(const struct qpack_dht *dht, uint32_t idx)
{
const struct qpack_dte *dte;
if (idx < QPACK_SHT_SIZE)
return ist("### ERR ###"); /* static table entries not accessible via dht */
dte = qpack_get_dte(dht, idx - QPACK_SHT_SIZE);
dte = qpack_get_dte(dht, idx);
if (!dte)
return ist("### ERR ###"); // error

View file

@ -1,5 +1,5 @@
/*
* include/haproxy/resolvers-t.h
* include/haproxy/dns-t.h
* This file provides structures and types for DNS.
*
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
@ -114,7 +114,7 @@ struct resolv_answer_item {
char name[DNS_MAX_NAME_SIZE+1]; /* answer name */
int16_t type; /* question type */
int16_t class; /* query class */
uint32_t ttl; /* response TTL */
int32_t ttl; /* response TTL */
int16_t priority; /* SRV type priority */
uint16_t weight; /* SRV type weight */
uint16_t port; /* SRV type port */
@ -281,7 +281,7 @@ enum {
* matching preference was found.
*/
RSLV_UPD_SRVIP_NOT_FOUND, /* provided IP not found
* OR provided IP found and preference is not matched and an IP
* OR provided IP found and preference is not match and an IP
* matching preference was found.
*/
RSLV_UPD_NO_IP_FOUND, /* no IP could be found in the response */

View file

@ -1,5 +1,5 @@
/*
* include/haproxy/resolvers.h
* include/haproxy/dns.h
* This file provides functions related to DNS protocol
*
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>

View file

@ -50,7 +50,7 @@ struct certificate_ocsp {
int refcount_store; /* Number of ckch_store that reference this certificate_ocsp */
int refcount; /* Number of actual references to this certificate_ocsp (SSL_CTXs mostly) */
struct buffer response;
unsigned long expire;
long expire;
X509 *issuer;
STACK_OF(X509) *chain;
struct eb64_node next_update; /* Key of items inserted in ocsp_update_tree (sorted by absolute date) */

View file

@ -188,12 +188,4 @@ struct file_name_node {
char name[VAR_ARRAY]; /* storage, used with cebus_*() */
};
/* a pair of uint64_t. It's purposely arranged in little endian to help
* being vectorized on modern processors.
*/
struct uint64_pair {
uint64_t l;
uint64_t h;
};
#endif /* _HAPROXY_TOOLS_T_H */

View file

@ -1288,27 +1288,11 @@ static inline void _ha_aligned_free(void *ptr)
int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz);
/* PRNG */
struct uint64_pair _ha_random64_pair_hashed(void);
void ha_generate_uuid_v4(struct buffer *output);
void ha_generate_uuid_v7(struct buffer *output);
void ha_random_seed(const unsigned char *seed, size_t len);
void ha_random_seed_thread(void);
void ha_random_jump128(uint32_t dist);
void ha_random_jump192(uint32_t dist);
void ha_random_jump96(uint32_t dist);
uint64_t ha_random64(void);
uint64_t ha_random64_internal(void);
/* Returns a pair of uint64_t randoms hashed so as not to disclose the internal
* PRNG state.
*/
static inline void ha_random64_pair_hashed(uint64_t *l, uint64_t *h)
{
struct uint64_pair ret = _ha_random64_pair_hashed();
*l = ret.l;
*h = ret.h;
}
static inline uint32_t ha_random32()
{

View file

@ -1532,7 +1532,7 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v);
@ -1562,16 +1562,6 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
key = ctx->store->data->key;
ctx->store->data->key = NULL;
/* OpenSSL's BIO_new_mem_buf() expects a NUL-terminated string when
* passed -1. The httpclient buffer lacks this, so manually terminate
* it here to prevent an out-of-bounds heap read during PEM parsing.
*/
if (b_room(&hc->res.buf) < 1) {
memprintf(errmsg, "ACME certificate response has no room for NUL terminator");
goto error;
}
hc->res.buf.area[hc->res.buf.data] = '\0';
/* XXX: might need a function dedicated to this, which does not read a private key */
if (ssl_sock_load_pem_into_ckch(ctx->store->path, hc->res.buf.area, ctx->store->data , errmsg) != 0)
goto error;
@ -1745,7 +1735,7 @@ int acme_res_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v);
@ -1846,7 +1836,7 @@ enum acme_ret acme_res_challenge(struct task *task, struct acme_ctx *ctx, struct
TRACE_DATA(__FUNCTION__, ACME_EV_RES, ctx, NULL, &hc->res.buf);
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v);
@ -1973,7 +1963,7 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v);
@ -2289,7 +2279,7 @@ int acme_res_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v);
@ -2468,7 +2458,7 @@ int acme_res_account(struct task *task, struct acme_ctx *ctx, int newaccount, ch
hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Location"))) {
istfree(&ctx->kid);
ctx->kid = istdup(hdr->v);
@ -2535,7 +2525,7 @@ int acme_nonce(struct task *task, struct acme_ctx *ctx, char **errmsg)
hdrs = hc->res.hdrs;
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
for (hdr = hdrs; isttest(hdr->v); hdr++) {
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
istfree(&ctx->nonce);
ctx->nonce = istdup(hdr->v);

View file

@ -539,6 +539,9 @@ size_t appctx_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, unsig
if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC))
goto end;
if (!count)
goto end;
if (!appctx_get_buf(appctx, &appctx->outbuf)) {
TRACE_STATE("waiting for appctx outbuf allocation", APPLET_EV_RECV|APPLET_EV_BLK, appctx);
goto end;
@ -547,8 +550,7 @@ size_t appctx_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, unsig
if (flags & CO_RFL_BUF_FLUSH)
applet_fl_set(appctx, APPCTX_FL_FASTFWD);
if (count)
ret = CALL_APPLET_WITH_RET(appctx->applet, rcv_buf(appctx, buf, count, flags));
ret = CALL_APPLET_WITH_RET(appctx->applet, rcv_buf(appctx, buf, count, flags));
if (ret)
applet_fl_clr(appctx, APPCTX_FL_OUTBLK_FULL);

View file

@ -297,7 +297,7 @@ check_user(struct userlist *ul, const char *user, const char *pass)
fprintf(stderr, ", crypt=%s\n", ((ep) ? ep : ""));
#endif
if (ep && u->pass && strcmp(ep, u->pass) == 0)
if (ep && strcmp(ep, u->pass) == 0)
return 1;
else
return 0;

View file

@ -1818,7 +1818,7 @@ int connect_server(struct stream *s)
{
struct connection *cli_conn = objt_conn(strm_orig(s));
struct connection *srv_conn = NULL;
const struct mux_proto_list *mux_proto = NULL;
const struct mux_proto_list *mux_proto;
struct server *srv;
struct ist name = IST_NULL;
struct sample *name_smp;
@ -2139,10 +2139,12 @@ int connect_server(struct stream *s)
}
if (may_start_mux_now) {
/* Delay MUX init if an XPRT handshake is required prior. */
/* Delay QMux MUX init to let xprt_qmux handshake runs first. */
mux_proto = conn_select_mux_be(srv_conn);
if (mux_proto && mux_proto->init_xprt)
if (mux_proto && mux_proto->init_xprt == XPRT_QMUX) {
srv_conn->flags |= (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
may_start_mux_now = 0;
}
}
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
@ -2252,13 +2254,6 @@ int connect_server(struct stream *s)
}
}
}
else if (mux_proto && mux_proto->init_xprt) {
/* Add handshake layer prior to MUX init if required. Does nothing if SSL layer is active though. */
if (xprt_add_l6hs(srv_conn, mux_proto->init_xprt)) {
conn_full_close(srv_conn);
return SF_ERR_INTERNAL;
}
}
/*
* Now that the mux may have been created, we can start the xprt.

View file

@ -125,9 +125,6 @@ int base64dec(const char *in, size_t ilen, char *out, size_t olen) {
signed char b;
int convlen = 0, i = 0, pad = 0;
if (!ilen)
return 0;
if (ilen % 4)
return -1;

View file

@ -374,7 +374,7 @@ static int secondary_key_cmp(const char *ref_key, const char *new_key)
* delete_expired==0, write otherwise.
*/
struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_entry *entry,
const char *primary_hash, const char *secondary_key, int delete_expired)
const char *secondary_key, int delete_expired)
{
struct eb32_node *node = &entry->eb;
@ -395,12 +395,6 @@ struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_e
entry = node ? eb32_entry(node, struct cache_entry, eb) : NULL;
}
/* Now verify the full primary hash matches: eb32 only compares 32 bits so
* we could have ended up on a different, unrelated entry.
*/
if (entry && primary_hash && memcmp(entry->hash, primary_hash, sizeof(entry->hash)))
entry = NULL;
/* Expired entry */
if (entry && entry->expire <= date.tv_sec) {
if (delete_expired) {
@ -1309,7 +1303,7 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px,
if (old) {
if (vary_signature)
old = get_secondary_entry(cache_tree, old,
txn->cache_hash, txn->cache_secondary_hash, 1);
txn->cache_secondary_hash, 1);
if (old) {
if (!old->complete) {
/* An entry with the same primary key is already being
@ -2184,20 +2178,9 @@ enum act_return http_action_req_cache_use(struct act_rule *rule, struct proxy *p
if (!http_request_build_secondary_key(s, res->secondary_key_signature)) {
cache_rdlock(cache_tree);
sec_entry = get_secondary_entry(cache_tree, res,
s->txn.http->cache_hash,
s->txn.http->cache_secondary_hash,
0);
if (!sec_entry) {
/* Secondary key miss: release the retained primary entry
* and reattach the detached row before returning.
*/
release_entry(cache_tree, res, 0);
shctx_wrlock(shctx);
if (detached)
shctx_row_reattach(shctx, entry_block);
shctx_wrunlock(shctx);
}
else if (sec_entry != res) {
if (sec_entry && sec_entry != res) {
/* The wrong row was added to the hot list. */
release_entry(cache_tree, res, 0);
retain_entry(sec_entry);

View file

@ -1629,6 +1629,11 @@ static int cfg_parse_global_shm_stats_file(char **args, int section_type,
struct proxy *curpx, const struct proxy *defpx,
const char *file, int line, char **err)
{
if (!experimental_directives_allowed) {
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'", args[0]);
return -1;
}
if (global.shm_stats_file != NULL) {
memprintf(err, "'%s' already specified.\n", args[0]);
return -1;
@ -1639,6 +1644,7 @@ static int cfg_parse_global_shm_stats_file(char **args, int section_type,
return -1;
}
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
global.shm_stats_file = strdup(args[1]);
return 0;
}
@ -1647,6 +1653,11 @@ static int cfg_parse_global_shm_stats_file_max_objects(char **args, int section_
struct proxy *curpx, const struct proxy *defpx,
const char *file, int line, char **err)
{
if (!experimental_directives_allowed) {
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'", args[0]);
return -1;
}
if (shm_stats_file_max_objects != -1) {
memprintf(err, "'%s' already specified.\n", args[0]);
return -1;
@ -1657,6 +1668,7 @@ static int cfg_parse_global_shm_stats_file_max_objects(char **args, int section_
return -1;
}
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
shm_stats_file_max_objects = atoi(args[1]);
return 0;
}

View file

@ -1151,13 +1151,8 @@ int cli_parse_cmdline(struct appctx *appctx)
*/
if (len-1 == strlen(appctx->cli_ctx.payload_pat)) {
if (strncmp(str, appctx->cli_ctx.payload_pat, len-1) == 0) {
/* end of payload was reached, rewind before the previous \n, if any, and replace it by a \0
* Otherwise, the payload is empty, just
*/
if (b_data(buf) > len)
b_sub(buf, len+1);
else
b_sub(buf, len);
/* end of payload was reached, rewind before the previous \n and replace it by a \0 */
b_sub(buf, strlen(appctx->cli_ctx.payload_pat) + 2);
*b_tail(buf) = '\0';
appctx->st1 &= ~APPCTX_CLI_ST1_PAYLOAD;
}

View file

@ -196,7 +196,7 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake)
* information to create one, typically from the ALPN. If we're
* done with the handshake, attempt to create one.
*/
if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_WAIT_XPRT_L6))) {
if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_QMUX_RECV|CO_FL_QMUX_SEND))) {
ret = conn_create_mux(conn, NULL);
if (ret < 0)
goto done;
@ -847,43 +847,6 @@ int xprt_add_hs(struct connection *conn)
return 0;
}
/* Activates an <xprt> layer on top of <conn> connection. This handshake layer
* should be designed to work on top of the layer 6. If SSL is active and its
* handshake still in progress, this function does nothing.
*
* Returns 0 on success else a negative error code.
*/
int xprt_add_l6hs(struct connection *conn, int xprt)
{
const struct xprt_ops *ops = xprt_get(xprt);
void *ops_ctx = NULL;
/* Only QMux is supported as handshake on top of layer6 for now. */
BUG_ON(xprt != XPRT_QMUX);
if (conn->flags & CO_FL_ERROR)
return -1;
/* Do nothing if SSL is in used but handshake still in progress. In
* this case, xprt layer will be added on handshake completion.
*/
if (conn->xprt == xprt_get(XPRT_SSL) &&
(conn->flags & CO_FL_WAIT_L6_CONN)) {
return 0;
}
if (ops->init(conn, &ops_ctx))
return -1;
ops->add_xprt(conn, ops_ctx, conn->xprt_ctx, conn->xprt, NULL, NULL);
conn->xprt = ops;
conn->xprt_ctx = ops_ctx;
/* Reset XPRT READY flag before the next conn_xprt_start(). */
conn->flags &= ~CO_FL_XPRT_READY;
return 0;
}
/* returns a short name for an error, typically the same as the enum name
* without the "CO_ER_" prefix, or an empty string for no error (better eye
* catching in logs). This is more compact for some debug cases.

View file

@ -1520,8 +1520,7 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
div = ha_cpu_policy[policy].arg;
div = div ? div : 1;
while (global.nbtgroups < MAX_TGROUPS && (global.nbthread < MAX_THREADS) &&
(global.thread_limit == 0 || global.nbthread < global.thread_limit)) {
while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS) {
ha_cpuset_zero(&node_cpu_set);
ha_cpuset_zero(&touse_tsid);
ha_cpuset_zero(&touse_ccx);
@ -1551,10 +1550,6 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
thr_count++;
if (global.thread_limit != 0 &&
thr_count + global.nbthread >= global.thread_limit)
break;
}
/* now cid = next cluster_id or -1 if none; cpu_count is the
@ -1613,8 +1608,7 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
div = ha_cpu_policy[policy].arg;
div = div ? div : 1;
while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS &&
(global.thread_limit == 0 || global.nbthread < global.thread_limit)) {
while (global.nbtgroups < MAX_TGROUPS && global.nbthread < MAX_THREADS) {
ha_cpuset_zero(&node_cpu_set);
ha_cpuset_zero(&touse_tsid);
ha_cpuset_zero(&touse_ccx);
@ -1644,9 +1638,6 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
ha_cpuset_set(&touse_tsid, ha_cpu_topo[cpu].ts_id);
} else if (!(cpu_policy_conf.flags & CPU_POLICY_ONE_THREAD_PER_CORE))
thr_count++;
if (global.thread_limit != 0 &&
global.nbthread + thr_count >= global.thread_limit)
break;
}
/* now l3id = next L3 ID or -1 if none; cpu_count is the

View file

@ -79,7 +79,7 @@ static struct dict_entry *__dict_lookup(struct dict *d, const char *s)
*/
struct dict_entry *dict_insert(struct dict *d, char *s)
{
struct dict_entry *de, *tree_de;
struct dict_entry *de;
struct ebpt_node *n;
HA_RWLOCK_RDLOCK(DICT_LOCK, &d->rwlock);
@ -97,18 +97,13 @@ struct dict_entry *dict_insert(struct dict *d, char *s)
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
n = ebis_insert(&d->values, &de->value);
tree_de = container_of(n, struct dict_entry, value);
if (tree_de == de)
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
else {
/* another entry was already there, we'll return it, kill
* ours and bump the other's refcount before returning it.
*/
HA_ATOMIC_INC(&tree_de->refcount);
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
if (n != &de->value) {
free_dict_entry(de);
de = container_of(n, struct dict_entry, value);
}
return tree_de;
return de;
}
@ -122,11 +117,10 @@ void dict_entry_unref(struct dict *d, struct dict_entry *de)
if (!de)
return;
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0) {
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0)
return;
}
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
ebpt_delete(&de->value);
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);

View file

@ -316,9 +316,6 @@ void h1_parse_upgrade_header(struct h1m *h1m, struct ist *value)
skip_val:
word.ptr = p = n;
}
if (istlen(*value))
h1m->flags |= H1_MF_UPG_HDR;
}
/* Macros used in the HTTP/1 parser, to check for the expected presence of
@ -713,16 +710,6 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
case H1_MSG_RPCODE:
http_msg_rpcode:
if (likely(HTTP_IS_DIGIT(*ptr))) {
if (ptr - sl.st.c.ptr >= 3) {
/* more than 3 digits */
if (h1m->err_pos == -1) /* only capture the error pointer */
h1m->err_pos = ptr - start + skip;
else if (h1m->err_pos < -1 || sl.st.status >= ((uint16_t)~0 - 9) / 10) {
/* strict checks or risk of overflow */
state = H1_MSG_RPCODE;
goto http_msg_invalid;
}
}
sl.st.status = sl.st.status * 10 + *ptr - '0';
EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpcode, http_msg_ood, state, H1_MSG_RPCODE);
}
@ -965,20 +952,6 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
goto http_output_full;
}
/* Skip headers whose names contain forbidden
* chars. When any is detected, h1m->err_pos >= 0,
* so we recheck the name only when an error was
* detected.
*/
if (unlikely(h1m->err_pos >= 0)) {
size_t i = 0;
while (i < n.len && HTTP_IS_TOKEN(n.ptr[i]))
i++;
if (i < n.len)
break;
}
if (isteqi(n, ist("transfer-encoding"))) {
ret = h1_parse_xfer_enc_header(h1m, v);
if (ret < 0) {
@ -1275,10 +1248,9 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
void h1_generate_random_ws_input_key(char key_out[25])
{
/* generate a random websocket key */
uint64_t rand1, rand2;
const uint64_t rand1 = ha_random64(), rand2 = ha_random64();
char key[16];
ha_random64_pair_hashed(&rand1, &rand2);
memcpy(key, &rand1, 8);
memcpy(&key[8], &rand2, 8);
a2base64(key, 16, key_out, 25);

View file

@ -162,8 +162,6 @@ static unsigned int h1m_htx_sl_flags(struct h1m *h1m)
}
if (h1m->flags & H1_MF_CONN_UPG)
flags |= HTX_SL_F_CONN_UPG;
if (h1m->flags & H1_MF_UPG_HDR)
flags |= HTX_SL_F_UPG_HDR;
return flags;
}
@ -215,31 +213,6 @@ static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx
}
}
/* Remove Upgrade header if no 'connection: upgrade' found */
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_HDR)) == H1_MF_UPG_HDR) {
int i;
for (i = 0; hdrs[i].n.len; i++) {
if (isteqi(hdrs[i].n, ist("upgrade")))
hdrs[i].v = IST_NULL;
}
h1m->flags &=~ (H1_MF_CONN_UPG|H1_MF_UPG_HDR);
}
/* Remove 'Upgrade' value from connection header if not Upgrade header found */
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_HDR)) == H1_MF_CONN_UPG) {
int i;
for (i = 0; hdrs[i].n.len; i++) {
if (isteqi(hdrs[i].n, ist("connection"))) {
http_remove_header_value(&hdrs[i].v, ist("upgrade"));
if (!istlen(hdrs[i].v))
hdrs[i].v = IST_NULL;
}
}
h1m->flags &=~ (H1_MF_CONN_UPG|H1_MF_UPG_HDR);
}
flags |= h1m_htx_sl_flags(h1m);
sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);

View file

@ -212,33 +212,8 @@ static ssize_t h3_init_uni_stream(struct h3c *h3c, struct qcs *qcs,
break;
case H3_UNI_S_T_PUSH:
if (!conn_is_back(qcs->qcc->conn)) {
/* RFC 9114 6.2.2. Push Streams
*
* Only servers can push; if a server receives a client-initiated push
* stream, this MUST be treated as a connection error of type
* H3_STREAM_CREATION_ERROR.
*/
TRACE_ERROR("reject push from client", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, H3_ERR_STREAM_CREATION_ERROR, 1,
muxc_tevt_type_proto_err);
qcc_report_glitch(qcs->qcc, 1);
goto err;
}
else {
/* RFC 9114 4.6. Server Push
*
* A client MUST treat receipt of a push stream as a connection
* error of type H3_ID_ERROR when no MAX_PUSH_ID frame has been sent or
* when the stream references a push ID that is greater than the maximum
* push ID.
*/
TRACE_ERROR("reject push from server outside of MAX_PUSH_ID", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, H3_ERR_ID_ERROR, 1,
muxc_tevt_type_proto_err);
qcc_report_glitch(qcs->qcc, 1);
goto err;
}
/* TODO not supported for the moment */
h3s->type = H3S_T_PUSH;
break;
case H3_UNI_S_T_QPACK_DEC:
@ -390,6 +365,7 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
case H3_FT_CANCEL_PUSH:
case H3_FT_GOAWAY:
case H3_FT_MAX_PUSH_ID:
/* RFC 9114 7.2.3. CANCEL_PUSH
*
* A CANCEL_PUSH frame is sent on the control stream. Receiving a
@ -436,33 +412,14 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
case H3_FT_PUSH_PROMISE:
/* RFC 9114 7.2.5. PUSH_PROMISE
*
* If a PUSH_PROMISE frame is received on the control stream, the client
* MUST respond with a connection error of type H3_FRAME_UNEXPECTED.
*
* A client MUST NOT send a PUSH_PROMISE frame. A server MUST treat the
* receipt of a PUSH_PROMISE frame as a connection error of type
* H3_FRAME_UNEXPECTED.
*/
if (h3s->type == H3S_T_CTRL || !conn_is_back(qcs->qcc->conn))
ret = H3_ERR_FRAME_UNEXPECTED;
break;
case H3_FT_MAX_PUSH_ID:
/* RFC 9114 7.2.7. MAX_PUSH_ID
*
* The MAX_PUSH_ID frame is always sent on the control stream. Receipt
* of a MAX_PUSH_ID frame on any other stream MUST be treated as a
* connection error of type H3_FRAME_UNEXPECTED.
*
* A server MUST NOT send a MAX_PUSH_ID frame. A client MUST treat the
* receipt of a MAX_PUSH_ID frame as a connection error of type
* H3_FRAME_UNEXPECTED.
*/
if (h3s->type == H3S_T_CTRL || conn_is_back(qcs->qcc->conn))
ret = H3_ERR_FRAME_UNEXPECTED;
else if (!(h3c->flags & H3_CF_SETTINGS_RECV))
ret = H3_ERR_MISSING_SETTINGS;
/* TODO server-side only. */
ret = H3_ERR_FRAME_UNEXPECTED;
break;
default:
@ -1973,25 +1930,6 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
h3s->st_req = H3S_ST_REQ_TRAILERS;
}
break;
case H3_FT_CANCEL_PUSH:
if (!conn_is_back(qcs->qcc->conn)) {
/* RFC 9114 7.2.3. CANCEL_PUSH
*
* If a server receives a CANCEL_PUSH frame for a push ID
* that has not yet been mentioned by a PUSH_PROMISE frame, this MUST be
* treated as a connection error of type H3_ID_ERROR.
*/
TRACE_ERROR("reject CANCEL_PUSH from client", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, H3_ERR_ID_ERROR, 1,
muxc_tevt_type_proto_err);
qcc_report_glitch(qcs->qcc, 1);
goto err;
}
else {
/* Not supported */
ret = flen;
}
break;
case H3_FT_GOAWAY:
ret = h3_parse_goaway_frm(qcs->qcc->ctx, b, flen);
if (ret < 0) {
@ -2000,6 +1938,12 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
goto err;
}
break;
case H3_FT_CANCEL_PUSH:
case H3_FT_PUSH_PROMISE:
case H3_FT_MAX_PUSH_ID:
/* Not supported */
ret = flen;
break;
case H3_FT_SETTINGS:
ret = h3_parse_settings_frm(qcs->qcc->ctx, b, flen);
if (ret < 0) {
@ -2009,25 +1953,6 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
}
h3c->flags |= H3_CF_SETTINGS_RECV;
break;
case H3_FT_PUSH_PROMISE:
/* h3_check_frame_valid() must reject on server side. */
BUG_ON(!conn_is_back(qcs->qcc->conn));
/* RFC 9114 7.2.5. PUSH_PROMISE
*
* A client MUST treat
* receipt of a PUSH_PROMISE frame that contains a larger push ID than
* the client has advertised as a connection error of H3_ID_ERROR.
*/
ret = H3_ERR_ID_ERROR;
break;
case H3_FT_MAX_PUSH_ID:
/* h3_check_frame_valid() must reject on client side. */
BUG_ON(conn_is_back(qcs->qcc->conn));
/* Not supported. */
ret = flen;
break;
default:
/* RFC 9114 Section 9. Extensions to HTTP/3
*

View file

@ -1926,30 +1926,20 @@ static void dump_registered_keywords(void)
/* Generate a random cluster-secret in case the setting is not provided in the
* configuration. This allows to use features which rely on it albeit with some
* limitations. The function prefers RAND_bytes() if available, otherwise falls
* back to ha_random64_pair_hashed().
* limitations.
*/
static void generate_random_cluster_secret()
{
/* used as a default random cluster-secret if none defined. */
union {
uint64_t by64[2];
uchar by8[16];
} rand;
uint64_t rand;
/* The caller must not overwrite an already defined secret. */
BUG_ON(cluster_secret_isset);
BUG_ON(sizeof(global.cluster_secret) != sizeof(rand));
#ifdef USE_OPENSSL
if (RAND_bytes(rand.by8, sizeof(rand.by8)) != 1)
#endif
{
/* no SSL or not working, fall back to other sources */
ha_random64_pair_hashed(&rand.by64[0], &rand.by64[1]);
}
rand = ha_random64();
memcpy(global.cluster_secret, &rand, sizeof(rand));
rand = ha_random64();
memcpy(global.cluster_secret + sizeof(rand), &rand, sizeof(rand));
cluster_secret_isset = 1;
}
@ -3098,7 +3088,6 @@ void *run_thread_poll_loop(void *data)
ha_set_thread(data);
set_thread_cpu_affinity();
clock_set_local_source();
ha_random_seed_thread();
#ifdef USE_THREAD
ha_thread_info[tid].pth_id = ha_get_pthread_id(tid);

View file

@ -788,7 +788,7 @@ static void hstream_parse_uri(struct ist uri, struct hstream *hs)
} while (*next);
if (use_rand)
result = ((long long)statistical_prng() * result) / 0xFFFFFFFFU;
result = ((long long)ha_random64() * result) / ((long long)RAND_MAX + 1);
switch (*arg) {
case 's':

View file

@ -401,7 +401,7 @@ void haproxy_init_args(int argc, char **argv)
/* SSL/TCP binding */
hbuf_appendf(&fbuf, "\tbind %s:%s shards by-thread ssl "
"alpn h3,h2,http1.1,http1.0"
"alpn h2,http1.1,http1.0"
" crt " HATERM_RSA_CERT_NAME
" crt " HATERM_ECDSA_CERT_NAME "%s%s\n",
ip, port2,
@ -438,8 +438,6 @@ void haproxy_init_args(int argc, char **argv)
}
hbuf_appendf(&gbuf, "global\n");
hbuf_appendf(&gbuf, "\ttune.memory.hot-size 3145728\n");
if (has_ssl)
hbuf_appendf(&gbuf, "\texpose-experimental-directives\n");
}
/* "global" section */

View file

@ -2949,20 +2949,20 @@ __LJMP static int hlua_socket_receive_yield(struct lua_State *L, int status, lua
/* remove final \r\n. */
if (nblk == 1) {
if (len1 && blk1[len1-1] == '\n') {
if (blk1[len1-1] == '\n') {
len1--;
skip_at_end++;
if (len1 && blk1[len1-1] == '\r') {
if (blk1[len1-1] == '\r') {
len1--;
skip_at_end++;
}
}
}
else {
if (len2 && blk2[len2-1] == '\n') {
if (blk2[len2-1] == '\n') {
len2--;
skip_at_end++;
if (len2 && blk2[len2-1] == '\r') {
if (blk2[len2-1] == '\r') {
len2--;
skip_at_end++;
}
@ -6709,20 +6709,6 @@ __LJMP static inline int hlua_http_add_hdr(lua_State *L, struct http_msg *msg)
size_t value_len;
const char *value = MAY_LJMP(luaL_checklstring(L, 3, &value_len));
struct htx *htx = htxbuf(&msg->chn->buf);
size_t i;
/* Reject header values containing CR/LF/NUL to prevent HTTP header
* injection on HTTP/1 output.
*/
for (i = 0; i < name_len; i++) {
if (name[i] == 0 || name[i] == '\r' || name[i] == '\n')
WILL_LJMP(lua_error(L));
}
for (i = 0; i < value_len; i++) {
if (value[i] == 0 || value[i] == '\r' || value[i] == '\n')
WILL_LJMP(lua_error(L));
}
lua_pushboolean(L, http_add_header(htx, ist2(name, name_len),
ist2(value, value_len), 1));

View file

@ -3996,19 +3996,19 @@ void http_check_response_for_cacheability(struct stream *s, struct channel *res)
continue;
}
/* We might have a no-cache="set-cookie" form. */
if (isteqi(ctx.value, ist("no-cache=\"set-cookie\""))) {
txn->flags &= ~TX_CACHE_COOK;
continue;
}
if (isteqi(ctx.value, ist("private")) || istmatchi(ctx.value, ist("private=")) ||
isteqi(ctx.value, ist("no-cache")) || istmatchi(ctx.value, ist("no-cache=")) ||
isteqi(ctx.value, ist("no-store")) || istmatchi(ctx.value, ist("no-store=")) ||
if (isteqi(ctx.value, ist("private")) ||
isteqi(ctx.value, ist("no-cache")) ||
isteqi(ctx.value, ist("no-store")) ||
isteqi(ctx.value, ist("s-maxage=0"))) {
txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
continue;
}
/* We might have a no-cache="set-cookie" form. */
if (istmatchi(ctx.value, ist("no-cache=\"set-cookie"))) {
txn->flags &= ~TX_CACHE_COOK;
continue;
}
if (istmatchi(ctx.value, ist("s-maxage"))) {
has_freshness_info = 1;
has_null_maxage = 0; /* The null max-age is overridden, ignore it */

View file

@ -263,7 +263,7 @@ static int sample_conv_url_dec(const struct arg *args, struct sample *smp, void
* before decoding.
*/
if (smp->flags & SMP_F_CONST || smp->data.u.str.size <= smp->data.u.str.data) {
struct buffer *str = get_trash_chunk_sz(smp->data.u.str.data + 1);
struct buffer *str = get_trash_chunk_sz(smp->data.u.str.data);
if (!str)
return 0;

View file

@ -356,7 +356,7 @@ static inline int http_7239_extract_node(struct ist *input, struct forwarded_hea
if (!quoted)
return 0; /* not supported */
*input = istnext(*input);
if (!istlen(*input) || !http_7239_extract_nodeport(input, nodeport))
if (!http_7239_extract_nodeport(input, nodeport))
return 0; /* invalid nodeport */
out:
/* ok */

View file

@ -135,7 +135,7 @@ static int get_http_auth(struct sample *smp, struct htx *htx)
chunk_initlen(&txn->auth.method_data, p, 0, istend(ctx.value) - p);
if (isteqi(ist2(auth_method.area, auth_method.data), ist("Basic"))) {
if (!strncasecmp("Basic", auth_method.area, auth_method.data)) {
struct buffer *http_auth = get_trash_chunk();
len = base64dec(txn->auth.method_data.area,
@ -159,7 +159,7 @@ static int get_http_auth(struct sample *smp, struct htx *htx)
txn->auth.method = HTTP_AUTH_BASIC;
return 1;
} else if (isteqi(ist2(auth_method.area, auth_method.data), ist("Bearer"))) {
} else if (!strncasecmp("Bearer", auth_method.area, auth_method.data)) {
txn->auth.method = HTTP_AUTH_BEARER;
return 1;
}

View file

@ -266,10 +266,8 @@ static int parse_jose(struct buffer *decoded_jose, int *alg, int *enc, struct jo
if (gcm) {
/* Look for "tag" field (used by aes gcm encryption).
* GCMKW tag must be exactly 16 bytes per RFC 7518 */
if (decode_jose_field(decoded_jose, "$.tag", &jose_fields->tag, 1) ||
b_data(jose_fields->tag) != 16)
/* Look for "tag" field (used by aes gcm encryption) */
if (decode_jose_field(decoded_jose, "$.tag", &jose_fields->tag, 1))
goto end;
/* Look for "iv" field (used by aes gcm encryption) */
@ -558,12 +556,6 @@ static int decrypt_ciphertext(jwe_enc enc, struct jwt_item items[JWE_ELT_MAX],
(*aead_tag)->data = size;
if (gcm) {
/* RFC 7518 mandates a 128-bit (16 byte) authentication tag for A*GCM.
* OpenSSL accepts 1-16 bytes but only verifies that many bytes, so a
* truncated tag reduces forgery work factor to ~256 per byte short. */
if ((*aead_tag)->data != 16)
goto end;
aad = alloc_trash_chunk();
if (!aad)
goto end;
@ -584,13 +576,8 @@ static int decrypt_ciphertext(jwe_enc enc, struct jwt_item items[JWE_ELT_MAX],
goto end;
/* Only use the second part of the decrypted key for actual
* content decryption.
* Because of the RSAES-PKCS1-V1_5 algorithm, we might have a
* bigger than expected decrypted_cek (if it was filled with
* random bytes in do_decrypt_cek_rsa) and still want to call
* aes_process on the ciphertext in order to avoid timing
* attacks. */
if (b_data(decrypted_cek) < key_size * 2)
* content decryption. */
if (b_data(decrypted_cek) != key_size * 2)
goto end;
chunk_memcpy(aes_key, decrypted_cek->area + key_size, key_size);
}
@ -824,31 +811,8 @@ static int do_decrypt_cek_rsa(struct buffer *cek, struct buffer *decrypted_cek,
}
if (EVP_PKEY_decrypt(ctx, (unsigned char*)b_orig(decrypted_cek), &outl,
(unsigned char*)b_orig(cek), b_data(cek)) <= 0) {
/* Per RFC 7516 #11.5, on RSAES-PKCS1-V1_5 decryption failure,
* substitute a random CEK and continue into content decryption.
* This prevents the Bleichenbacher timing oracle: without this
* guard, "padding invalid" (fast exit) is distinguishable from
* "padding valid + AEAD tag fail" (full decrypt path).
* We will build the biggest decrypted_cek necessary rather than
* filling the entire buffer, which would be a key for the
* A256CBC_HS512 encrypting algorithm for which the decrypted
* cek contains the actual key as well as the tag.
*/
if (pad == RSA_PKCS1_PADDING) {
#define MAX_DECRYPTED_CEK_LEN (32 * 2) /* See https://datatracker.ietf.org/doc/html/rfc7518#section-5.2.2.1 */
int i;
unsigned char *p = (unsigned char *)b_orig(decrypted_cek);
for (i = 0; i < MAX_DECRYPTED_CEK_LEN; i++) {
uint64_t r = ha_random64();
memcpy(p, &r, 8);
p+=8;
}
outl = MAX_DECRYPTED_CEK_LEN;
} else
goto end;
}
(unsigned char*)b_orig(cek), b_data(cek)) <= 0)
goto end;
decrypted_cek->data = outl;

View file

@ -3319,7 +3319,7 @@ struct ist *build_log_header(struct log_header hdr, size_t *nbelem)
break;
}
else if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
uint month;
int month;
char *timestamp = metadata[LOG_META_TIME].ptr;
/* iso time always begins like this: '1970-01-01T00:00:00' */
@ -5499,7 +5499,7 @@ void parse_log_message(char *buf, size_t buflen, int *level, int *facility,
return;
fac_level = 10*fac_level + (*p - '0');
p++;
if ((p - buf) >= buflen)
if ((p - buf) > buflen)
return;
}
@ -6743,7 +6743,6 @@ int cfg_parse_log_profile(const char *file, int linenum, char **args, int kwm)
SMP_VAL_FE_LOG_END, &errmsg)) {
ha_alert("Parsing [%s:%d]: failed to parse logformat: %s.\n",
file, linenum, errmsg);
lf_expr_deinit(target_lf);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}

View file

@ -1595,7 +1595,7 @@ static int fcgi_conn_handle_values_result(struct fcgi_conn *fconn)
return 0;
}
if (unlikely(b_contig_data(dbuf, 0) < fconn->drl)) {
if (unlikely(b_contig_data(dbuf, b_head_ofs(dbuf)) < fconn->drl)) {
/* Realign the dmux buffer if the record wraps. It is unexpected
* at this stage because it should be the first record received
* from the FCGI application.
@ -2516,8 +2516,13 @@ static int fcgi_strm_handle_end_request(struct fcgi_conn *fconn, struct fcgi_str
return 0;
}
if (unlikely(b_contig_data(dbuf, 0) < fconn->drl))
if (unlikely(b_contig_data(dbuf, b_head_ofs(dbuf)) < fconn->drl)) {
/* Realign the dmux buffer if the record wraps. It is unexpected
* at this stage because it should be the first record received
* from the FCGI application.
*/
b_slow_realign_ofs(dbuf, trash.area, 0);
}
inbuf = b_make(b_head(dbuf), b_data(dbuf), 0, fconn->drl);
@ -2639,16 +2644,6 @@ static void fcgi_process_demux(struct fcgi_conn *fconn)
}
fstrm = tmp_fstrm;
if (fconn->dsi == 0 && fconn->drt != FCGI_GET_VALUES_RESULT && fconn->drt != FCGI_UNKNOWN_TYPE) {
/* Stream ID 0 is reserved for management records and
* must not used for application record type.
*/
fconn->state = FCGI_CS_CLOSED;
TRACE_ERROR("Application record with SID 0", FCGI_EV_RX_RECORD|FCGI_EV_RX_FHDR|FCGI_EV_RX_GETVAL|FCGI_EV_FCONN_ERR, fconn->conn);
TRACE_STATE("switching to CLOSED", FCGI_EV_RX_RECORD|FCGI_EV_RX_FHDR|FCGI_EV_RX_GETVAL|FCGI_EV_FCONN_ERR, fconn->conn);
goto fail;
}
if (fstrm->state == FCGI_SS_CLOSED && fconn->dsi != 0) {
/* ignore all record for closed streams */
goto ignore_record;

View file

@ -4257,11 +4257,11 @@ static int h1_process(struct h1c * h1c)
h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event);
}
}
no_parsing:
if (h1c->glitches != prev_glitches && !(h1c->flags & H1C_F_IS_BACK))
session_add_glitch_ctr(sess, h1c->glitches - prev_glitches);
}
no_parsing:
h1_send(h1c);
/* H1 connection must be released ASAP if:

View file

@ -6236,13 +6236,6 @@ next_frame:
/* Skip StreamDep and weight for now (we don't support PRIORITY) */
if (h2c->dff & H2_F_HEADERS_PRIORITY) {
if (flen < 5) {
h2c_report_glitch(h2c, 1, "too short PRIORITY frame");
TRACE_STATE("too short PRIORITY frame", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
goto fail;
}
if (read_n32(hdrs) == h2c->dsi) {
/* RFC7540#5.3.1 : stream dep may not depend on itself */
h2c_report_glitch(h2c, 1, "PRIORITY frame referencing itself");
@ -6252,6 +6245,13 @@ next_frame:
goto fail;
}
if (flen < 5) {
h2c_report_glitch(h2c, 1, "too short PRIORITY frame");
TRACE_STATE("too short PRIORITY frame", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
goto fail;
}
hdrs += 5; // stream dep = 4, weight = 1
flen -= 5;
}

View file

@ -1652,10 +1652,10 @@ static int spop_conn_handle_hello(struct spop_conn *spop_conn)
return 0;
}
if (unlikely(b_contig_data(dbuf, 0) < spop_conn->dfl)) {
if (unlikely(b_contig_data(dbuf, b_head_ofs(dbuf)) < spop_conn->dfl)) {
/* Realign the dmux buffer if the frame wraps. It is unexpected
* at this stage because it should be the first record received
* from the SPOA.
* from the FCGI application.
*/
b_slow_realign_ofs(dbuf, trash.area, 0);
}
@ -1824,8 +1824,13 @@ static int spop_conn_handle_disconnect(struct spop_conn *spop_conn)
return 0;
}
if (unlikely(b_contig_data(dbuf, 0) < spop_conn->dfl))
if (unlikely(b_contig_data(dbuf, b_head_ofs(dbuf)) < spop_conn->dfl)) {
/* Realign the dmux buffer if the frame wraps. It is unexpected
* at this stage because it should be the first record received
* from the FCGI application.
*/
b_slow_realign_ofs(dbuf, trash.area, 0);
}
p = b_head(dbuf);
end = p + spop_conn->dfl;
@ -1931,8 +1936,13 @@ static int spop_conn_handle_ack(struct spop_conn *spop_conn, struct spop_strm *s
return 0;
}
if (unlikely(b_contig_data(dbuf, 0) < spop_conn->dfl))
if (unlikely(b_contig_data(dbuf, b_head_ofs(dbuf)) < spop_conn->dfl)) {
/* Realign the dmux buffer if the frame wraps. It is unexpected
* at this stage because it should be the first record received
* from the FCGI application.
*/
b_slow_realign_ofs(dbuf, trash.area, 0);
}
spop_conn->flags &= ~SPOP_CF_DEM_SFULL;
rxbuf = spop_get_buf(spop_conn, &spop_strm->rxbuf);

View file

@ -1298,7 +1298,7 @@ void mworker_apply_master_worker_mode(void)
/* This one must not be exported, it's internal! */
unsetenv("HAPROXY_MWORKER_REEXEC");
ha_random_jump128(1);
ha_random_jump96(1);
list_for_each_entry(child, &proc_list, list) {
if ((child->options & PROC_O_TYPE_WORKER) && (child->options & PROC_O_INIT)) {

View file

@ -447,8 +447,8 @@ static size_t tcp_fullhdr_find_opt(const struct sample *smp, uint8_t opt)
/* kind1 = NOP and is a single byte, others have a length field */
if (smp->data.u.str.area[next] == 1)
next++;
else if (next + 1 < len && smp->data.u.str.area[next + 1] > 1)
next += (uchar)smp->data.u.str.area[next + 1];
else if (next + 1 < len)
next += smp->data.u.str.area[next + 1];
else
break;
if (smp->data.u.str.area[curr] == opt && next <= len)
@ -605,7 +605,7 @@ static int sample_conv_tcp_options_list(const struct arg *arg_p, struct sample *
/* kind1 = NOP and is a single byte, others have a length field */
if (smp->data.u.str.area[ofs] == 1)
ofs++;
else if (ofs + 1 < len && smp->data.u.str.area[ofs + 1] > 1)
else if (ofs + 1 < len && smp->data.u.str.area[ofs + 1])
ofs += (uchar)smp->data.u.str.area[ofs + 1];
else
break;
@ -780,7 +780,7 @@ static int sample_conv_ip_fp(const struct arg *arg_p, struct sample *smp, void *
/* kind1 = NOP and is a single byte, others have a length field */
if (smp->data.u.str.area[ofs] == 1)
next = ofs + 1;
else if ((ofs + 1 < tcplen) && smp->data.u.str.area[ofs + 1] > 1)
else if ((ofs + 1 < tcplen) && smp->data.u.str.area[ofs + 1]) /* optlen 0 will cause an infinite loop */
next = ofs + (uchar)smp->data.u.str.area[ofs + 1];
else
break;

View file

@ -116,9 +116,6 @@ smp_client_hello_parse( struct sample *smp, enum client_hello_type type, unsigne
data += 5; /* enter TLS handshake */
bleft -= 5;
if (bleft < hs_len)
goto too_short;
/* Check for a complete client hello starting at <data> */
if (bleft < 1)
goto too_short;
@ -132,18 +129,15 @@ smp_client_hello_parse( struct sample *smp, enum client_hello_type type, unsigne
if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
goto not_ssl_hello; /* too short to have an extension */
data += 4;
bleft -= 4;
/* We want the full handshake here */
if (bleft < hs_len)
goto too_short;
data += 4;
/* Start of the ClientHello message */
if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
goto not_ssl_hello;
/* Note: covered by the hs_len test 30 lines above */
ext_len = data[34]; /* session_id_len */
if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct session_id len */
goto not_ssl_hello;

View file

@ -625,10 +625,8 @@ static int quic_deallocate_dghdlrs(void)
int i;
if (quic_dghdlrs) {
for (i = 0; i < global.nbthread; ++i) {
free(quic_dghdlrs[i].buf.buffer);
for (i = 0; i < global.nbthread; ++i)
tasklet_free(quic_dghdlrs[i].task);
}
free(quic_dghdlrs);
}

View file

@ -69,7 +69,7 @@
#include <haproxy/uri_auth.h>
/* Lock to ensure multiple backends deletion concurrently is safe */
__decl_spinlock(proxies_del_lock);
static __decl_spinlock(proxies_del_lock);
int listeners; /* # of proxy listeners, set by cfgparse */
struct proxy *proxies_list = NULL; /* list of main proxies */

View file

@ -70,10 +70,6 @@ static int qmux_parse_frm(struct qcc *qcc, struct buffer *buf)
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_STOP_SENDING) {
struct qf_stop_sending *ss_frm = &frm.stop_sending;
qcc_recv_stop_sending(qcc, ss_frm->id, ss_frm->app_error_code);
}
else if (frm.type == QUIC_FT_MAX_DATA) {
struct qf_max_data *md_frm = &frm.max_data;
qcc_recv_max_data(qcc, md_frm->max_data);
@ -86,26 +82,13 @@ static int qmux_parse_frm(struct qcc *qcc, struct buffer *buf)
struct qf_max_streams *ms_frm = &frm.max_streams_bidi;
qcc_recv_max_streams(qcc, ms_frm->max_streams, 1);
}
else if (frm.type == QUIC_FT_MAX_STREAMS_UNI) {
struct qf_max_streams *ms_frm = &frm.max_streams_uni;
qcc_recv_max_streams(qcc, ms_frm->max_streams, 0);
}
else if (frm.type == QUIC_FT_DATA_BLOCKED ||
frm.type == QUIC_FT_STREAM_DATA_BLOCKED ||
frm.type == QUIC_FT_STREAMS_BLOCKED_BIDI ||
frm.type == QUIC_FT_STREAMS_BLOCKED_UNI) {
/* TODO */
CHECK_IF("received flow control blocked frame not yet handled in QMux");
}
else if (frm.type == QUIC_FT_PADDING) {
CHECK_IF("received padding frame not yet handled in QMux");
}
else if (frm.type == QUIC_FT_CONNECTION_CLOSE ||
frm.type == QUIC_FT_CONNECTION_CLOSE_APP) {
CHECK_IF("received connection_close frame not yet handled in QMux");
}
else {
/* qmux_is_frm_valid() must prevent this */
ABORT_NOW();
}
@ -145,8 +128,7 @@ int qcc_qmux_recv(struct qcc *qcc)
*/
if (b_head(buf) + qcc->rx.rlen > b_wrap(buf) ||
(!dec && b_head(buf) + b_data(buf) == b_wrap(buf))) {
/* A too large record should have been rejected earlier. */
BUG_ON(qcc->rx.rlen > b_size(buf));
BUG_ON(qcc->rx.rlen > b_size(buf)); /* TODO max_record_size */
memmove(b_orig(buf), b_head(buf), b_data(buf));
buf->head = 0;
}
@ -172,12 +154,6 @@ int qcc_qmux_recv(struct qcc *qcc)
if (b_data(buf) && !qcc->rx.rlen) {
dec = b_quic_dec_int(&qcc->rx.rlen, buf, NULL);
if (qcc->rx.rlen > b_size(buf)) {
/* TODO report FRAME_ENCODING_ERROR on max_record_size violation */
qcc->conn->flags |= CO_FL_ERROR;
goto out;
}
/* Restart read if an incomplete record has been received
* until there is no more new data available.
*/
@ -195,10 +171,11 @@ int qcc_qmux_recv(struct qcc *qcc)
buf_rec = b_make(b_orig(buf), b_size(buf),
b_head_ofs(buf), qcc->rx.rlen);
frm_ret = qmux_parse_frm(qcc, &buf_rec);
BUG_ON(frm_ret < 0); /* TODO handle fatal errors */
if (!frm_ret) {
/* TODO implement proper connection closure */
conn->flags |= CO_FL_ERROR;
goto out;
/* emit FRAME_ENCODING_ERROR */
ABORT_NOW();
}
/* A frame cannot be bigger than a record thanks to <buf_rec> delimitation. */

View file

@ -44,6 +44,14 @@
#define qpack_debug_hexdump(...) do { } while (0)
#endif
/* Encoded field line bitmask */
#define QPACK_EFL_BITMASK 0xf0
#define QPACK_LFL_WPBNM 0x00 // Literal field line with post-base name reference
#define QPACK_IFL_WPBI 0x10 // Indexed field line with post-based index
#define QPACK_LFL_WLN_BIT 0x20 // Literal field line with literal name
#define QPACK_LFL_WNR_BIT 0x40 // Literal field line with name reference
#define QPACK_IFL_BIT 0x80 // Indexed field line
/* reads a varint from <raw>'s lowest <b> bits and <len> bytes max (raw included).
* returns the 64-bit value on success after updating buf and len_in. Forces
* len_in to (uint64_t)-1 on truncated input.
@ -231,7 +239,7 @@ static int qpack_decode_fs_pfx(uint64_t *enc_ric, uint64_t *db, int *sign_bit,
return -QPACK_RET_TRUNCATED;
/* Safe access to the sign bit thanks to the check above */
*sign_bit = **raw & 0x80;
*sign_bit = **raw & 0x8;
*db = qpack_get_varint(raw, len, 7);
if (*len == (uint64_t)-1)
return -QPACK_RET_DB;
@ -448,13 +456,13 @@ int qpack_decode_fs(const unsigned char *raw, uint64_t len, struct buffer *tmp,
}
nlen = huff_dec(raw, length, trash, tmp->size - tmp->data);
if (nlen < 0) {
if (nlen == (uint32_t)-1) {
qpack_debug_printf(stderr, " can't decode huffman.\n");
ret = -QPACK_RET_HUFFMAN;
goto out;
}
qpack_debug_printf(stderr, " [value huff %d->%d '%s']", (int)length, (int)nlen, trash);
qpack_debug_printf(stderr, " [name huff %d->%d '%s']", (int)length, (int)nlen, trash);
/* makes an ist from tmp storage */
b_add(tmp, nlen);
value = ist2(trash, nlen);
@ -498,7 +506,7 @@ int qpack_decode_fs(const unsigned char *raw, uint64_t len, struct buffer *tmp,
goto out;
}
nlen = huff_dec(raw, name_len, trash, tmp->size - tmp->data);
if (nlen < 0) {
if (nlen == (uint32_t)-1) {
qpack_debug_printf(stderr, " can't decode huffman.\n");
ret = -QPACK_RET_HUFFMAN;
goto out;
@ -537,13 +545,13 @@ int qpack_decode_fs(const unsigned char *raw, uint64_t len, struct buffer *tmp,
goto out;
}
nlen = huff_dec(raw, value_len, trash, tmp->size - tmp->data);
if (nlen < 0) {
if (nlen == (uint32_t)-1) {
qpack_debug_printf(stderr, " can't decode huffman.\n");
ret = -QPACK_RET_HUFFMAN;
goto out;
}
qpack_debug_printf(stderr, " [value huff %d->%d '%s']", (int)value_len, (int)nlen, trash);
qpack_debug_printf(stderr, " [name huff %d->%d '%s']", (int)value_len, (int)nlen, trash);
/* makes an ist from tmp storage */
b_add(tmp, nlen);
value = ist2(trash, nlen);

View file

@ -1,7 +1,6 @@
#include <haproxy/qpack-enc.h>
#include <haproxy/buf.h>
#include <haproxy/qpack-t.h>
#include <haproxy/intops.h>
/* Returns the byte size required to encode <i> as a <prefix_size>-prefix
@ -266,6 +265,8 @@ int qpack_encode_field_section_line(struct buffer *out)
return 0;
}
#define QPACK_LFL_WLN_BIT 0x20 // Literal field line with literal name
/* Encode a header in literal field line with literal name.
* Returns 0 on success else non-zero.
*/

View file

@ -394,9 +394,6 @@ int qpack_dht_insert(struct qpack_dht *dht, struct ist name, struct ist value)
else {
/* need to defragment the table before inserting upfront */
dht = qpack_dht_defrag(dht);
if (!dht)
return -1;
wrap = dht->wrap + 1;
head = dht->head + 1;
dht->dte[head].addr = dht->dte[dht->front].addr - (name.len + value.len);

View file

@ -378,7 +378,7 @@ int quic_get_cid_tid(const unsigned char *cid, size_t cid_len,
tree = &quic_fe_cid_trees[quic_cid_tree_idx(&derive_cid)];
HA_RWLOCK_RDLOCK(QC_CID_LOCK, &tree->lock);
node = ebmb_lookup(&tree->root, derive_cid.data, derive_cid.len);
node = ebmb_lookup(&tree->root, cid, cid_len);
if (node) {
conn_id = ebmb_entry(node, struct quic_connection_id, node);
cid_tid = HA_ATOMIC_LOAD(&conn_id->tid);

View file

@ -566,11 +566,8 @@ struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int sta
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
TRACE_STATE("connection handshake state", QUIC_EV_CONN_IO_CB, qc, &qc->state);
if (qc_test_fd(qc) && qc_rcv_buf(qc) < 0) {
TRACE_ERROR("recvmsg fatal error", QUIC_EV_CONN_SPPKTS, qc);
qc_kill_conn(qc);
goto no_rx_pkts;
}
if (qc_test_fd(qc))
qc_rcv_buf(qc);
/* Prepare post-handshake frames
* - after connection is instantiated (accept is done)
@ -594,7 +591,6 @@ struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int sta
goto out;
}
no_rx_pkts:
if (qc->flags & QUIC_FL_CONN_TO_KILL) {
TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_IO_CB, qc);
goto out;
@ -661,10 +657,8 @@ static struct task *quic_conn_closed_io_cb(struct task *t, void *context, unsign
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
if (qc_test_fd(qc) && qc_rcv_buf(qc) < 0) {
TRACE_ERROR("recvmsg fatal error", QUIC_EV_CONN_IO_CB, qc);
goto fatal_error;
}
if (qc_test_fd(qc))
qc_rcv_buf(qc);
/* Do not send too much data if the peer address was not validated. */
if ((qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE) &&
@ -676,7 +670,11 @@ static struct task *quic_conn_closed_io_cb(struct task *t, void *context, unsign
QUIC_MAX_CC_BUFSIZE - headlen, 0, cc_qc->cc_dgram_len);
if (qc_snd_buf(qc, &buf, buf.data, 0, 0) < 0) {
TRACE_ERROR("sendto fatal error", QUIC_EV_CONN_IO_CB, qc);
goto fatal_error;
quic_release_cc_conn(cc_qc);
cc_qc = NULL;
qc = NULL;
t = NULL;
goto leave;
}
qc->flags &= ~QUIC_FL_CONN_IMMEDIATE_CLOSE;
@ -685,13 +683,6 @@ static struct task *quic_conn_closed_io_cb(struct task *t, void *context, unsign
TRACE_LEAVE(QUIC_EV_CONN_IO_CB, qc);
return t;
fatal_error:
quic_release_cc_conn(cc_qc);
cc_qc = NULL;
qc = NULL;
t = NULL;
goto leave;
}
/* The task handling the idle timeout of a connection in "connection close" state */
@ -811,16 +802,8 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
goto out;
}
if (qc_test_fd(qc) && qc_rcv_buf(qc) < 0) {
TRACE_ERROR("recvmsg fatal error", QUIC_EV_CONN_SPPKTS, qc);
qc_kill_conn(qc);
goto out;
}
if (qc->flags & QUIC_FL_CONN_TO_KILL) {
TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_PHPKTS, qc);
goto out;
}
if (qc_test_fd(qc))
qc_rcv_buf(qc);
if (!qc_treat_rx_pkts(qc))
goto out;
@ -1962,14 +1945,6 @@ void qc_notify_err(struct quic_conn *qc)
*/
tasklet_wakeup(qc->qcc->wait_event.tasklet);
}
else if (qc->conn) {
qc->conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
/* Note: this creation will failed, but the upper layer will be informed
* about this connection errors.
*/
if (conn_create_mux(qc->conn, NULL) < 0)
TRACE_ERROR("mux creation failed", QUIC_EV_CONN_IO_CB, qc);
}
TRACE_LEAVE(QUIC_EV_CONN_CLOSE, qc);
}

View file

@ -88,7 +88,7 @@ static int qc_do_rm_hp(struct quic_conn *qc,
ret = 0;
/* Check there is enough data in this packet. */
if (pkt->len - pkt->pn_offset < QUIC_PACKET_PN_MAXLEN + QUIC_TLS_TAG_LEN) {
if (pkt->len - (pn - byte0) < QUIC_PACKET_PN_MAXLEN + sizeof mask) {
TRACE_PROTO("too short packet", QUIC_EV_CONN_RMHP, qc, pkt);
goto leave;
}

View file

@ -846,17 +846,9 @@ int qc_rcv_buf(struct quic_conn *qc)
(struct sockaddr *)&daddr, sizeof(daddr),
get_net_port(&qc->local_addr), !!l);
if (ret <= 0) {
/* Only negative errors are fatal */
ret = 0;
/* Subscribe FD for future reception. */
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN)
fd_want_recv(qc->fd);
}
else if (errno == ECONNREFUSED) {
TRACE_PRINTF(TRACE_LEVEL_USER, QUIC_EV_CONN_RCV, qc, 0, 0, 0,
"UDP recv failure errno=%d (%s)", errno, strerror(errno));
ret = -errno;
}
/* TODO handle other error codes as fatal on the connection. */
break;
}

View file

@ -24,10 +24,8 @@ int quic_generate_token(unsigned char *token, size_t len,
unsigned char aad[sizeof(struct in6_addr)];
size_t aadlen;
uint32_t ts = (uint32_t)date.tv_sec;
union {
uint64_t u64[2];
uchar u8[QUIC_TOKEN_RAND_DLEN];
} rand;
uint64_t rand_u64;
unsigned char rand[QUIC_TOKEN_RAND_DLEN];
unsigned char key[16];
unsigned char iv[QUIC_TLS_IV_LEN];
const unsigned char *sec = global.cluster_secret;
@ -37,7 +35,10 @@ int quic_generate_token(unsigned char *token, size_t len,
TRACE_ENTER(QUIC_EV_CONN_TXPKT);
/* Generate random data to be used as salt to derive the token secret. */
ha_random64_pair_hashed(&rand.u64[0], &rand.u64[1]);
rand_u64 = ha_random64();
write_u64(rand, rand_u64);
rand_u64 = ha_random64();
write_u64(rand + sizeof(rand_u64), rand_u64);
if (len < QUIC_TOKEN_LEN) {
TRACE_ERROR("too small buffer", QUIC_EV_CONN_TXPKT);
@ -47,7 +48,7 @@ int quic_generate_token(unsigned char *token, size_t len,
/* Generate the AAD. */
aadlen = ipaddrcpy(aad, addr);
if (!quic_tls_derive_token_secret(EVP_sha256(), key, sizeof key,
iv, sizeof iv, rand.u8, sizeof(rand.u8),
iv, sizeof iv, rand, sizeof(rand),
sec, seclen)) {
TRACE_ERROR("quic_tls_derive_token_secret() failed", QUIC_EV_CONN_TXPKT);
goto err;
@ -70,8 +71,8 @@ int quic_generate_token(unsigned char *token, size_t len,
}
p += QUIC_TLS_TAG_LEN;
memcpy(p, rand.u8, sizeof(rand.u8));
p += sizeof(rand.u8);
memcpy(p, rand, sizeof(rand));
p += sizeof(rand);
ret = p - token;
leave:

View file

@ -444,7 +444,7 @@ INITCALL0(STG_REGISTER, regex_register_build_options);
#ifdef USE_PCRE2
static int init_pcre2_per_thread(void)
{
local_pcre2_match = pcre2_match_data_create(MAX_MATCH, NULL);
local_pcre2_match = pcre2_match_data_create(MAX_MATCH - 1, NULL);
if (!local_pcre2_match) {
ha_alert("Failed to allocate PCRE2 match data context for thread %u.\n", tid);
return 0;

View file

@ -63,6 +63,7 @@ struct list resolv_srvrq_list = LIST_HEAD_INIT(resolv_srvrq_list);
static THREAD_LOCAL struct list death_row; /* list of deferred resolutions to kill, local validity only */
static THREAD_LOCAL unsigned int recurse = 0; /* counter to track calls to public functions */
static THREAD_LOCAL uint64_t resolv_query_id_seed = 0; /* random seed */
struct resolvers *curr_resolvers = NULL;
DECLARE_STATIC_TYPED_POOL(resolv_answer_item_pool, "resolv_answer_item", struct resolv_answer_item);
@ -225,7 +226,7 @@ struct show_resolvers_ctx {
};
/* returns the currently accepted address families as a combination of
* RSLV_ACCEPT_IPV4 and RSLV_ACCEPT_IPV6 only. It will dynamically adapt
* RSLV_ACCEPT_IPV4 and RSLV_ACCEPT_IPV6 only. It will dynamically adapt adapt
* the IPv6 status to sock_inet6_seems_reachable if RSLV_AUTO_FAMILY is set,
* otherwise returns the relevant bits of resolv_accept_families.
*/
@ -364,6 +365,18 @@ struct resolv_answer_item *find_srvrq_answer_record(const struct resolv_requeste
return NULL;
}
/* 2 bytes random generator to generate DNS query ID */
static inline uint16_t resolv_rnd16(void)
{
if (!resolv_query_id_seed)
resolv_query_id_seed = now_ms;
resolv_query_id_seed ^= resolv_query_id_seed << 13;
resolv_query_id_seed ^= resolv_query_id_seed >> 7;
resolv_query_id_seed ^= resolv_query_id_seed << 17;
return resolv_query_id_seed;
}
static inline int resolv_resolution_timeout(struct resolv_resolution *res)
{
return (!LIST_ISEMPTY(&res->requesters) ? res->resolvers->timeout.resolve : res->resolvers->hold.valid);
@ -496,14 +509,14 @@ resolv_run_resolution(struct resolv_resolution *resolution)
return 0;
/* Check if a resolution has already been started for this server return
* directly to avoid resolution pile up. */
* directly to avoid resolution pill up. */
if (resolution->step != RSLV_STEP_NONE)
return 0;
/* Generates a new query id. We try at most 100 times to find a free
* query id */
for (i = 0; i < 100; ++i) {
query_id = (uint16_t)ha_random32();
query_id = resolv_rnd16();
if (!eb32_lookup(&resolvers->query_ids, query_id))
break;
query_id = -1;
@ -1223,7 +1236,8 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
if (reader + 4 > bufend)
goto invalid_resp;
answer_record->ttl = read_n32(reader);
answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536
+ reader[2] * 256 + reader[3];
reader += 4;
/* Now reading data len */
@ -1420,9 +1434,9 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE,
&offset, 0);
if (len == 0)
continue;
goto invalid_resp;
if (reader + offset + 10 > bufend)
if (reader + offset + 10 >= bufend)
goto invalid_resp;
reader += offset;
@ -1436,7 +1450,7 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
len = reader[0] * 256 + reader[1];
reader += 2;
if (reader + len > bufend)
if (reader + len >= bufend)
goto invalid_resp;
reader += len;
@ -1456,11 +1470,8 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
offset = 0;
len = resolv_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
if (len == 0) {
pool_free(resolv_answer_item_pool, answer_record);
answer_record = NULL;
continue;
}
if (len == 0)
goto invalid_resp;
memcpy(answer_record->name, tmpname, len);
answer_record->name[len] = 0;
@ -1487,7 +1498,8 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
if (reader + 4 > bufend)
goto invalid_resp;
answer_record->ttl = read_n32(reader);
answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536
+ reader[2] * 256 + reader[3];
reader += 4;
/* Now reading data len */
@ -1587,6 +1599,7 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
tmp_record->ar_item == NULL &&
memcmp(tmp_record->data.target, answer_record->name, tmp_record->data_len) == 0) {
/* Always use the received additional record to refresh info */
pool_free(resolv_answer_item_pool, tmp_record->ar_item);
tmp_record->ar_item = answer_record;
answer_record = NULL;
break;
@ -1842,15 +1855,7 @@ int resolv_dn_label_to_str(const char *dn, int dn_len, char *str, int str_len)
ptr = str;
for (i = 0; i < dn_len; ++i) {
sz = (unsigned char)dn[i];
if (!sz)
break;
/* Check str_len adding 1 for the dot if (i!=0) and 1 for null terminator */
if (str_len < sz+i+(!!i)+1)
return -1;
sz = dn[i];
if (i)
*ptr++ = '.';
/* copy the string at i+1 to lower case */

View file

@ -2150,11 +2150,11 @@ static int sample_conv_be2hex_check(struct arg *args, struct sample_conv *conv,
*/
static int sample_conv_be2hex(const struct arg *args, struct sample *smp, void *private)
{
struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data * 2);
struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data);
int chunk_size = args[1].data.sint;
const int last = args[2].data.sint ? smp->data.u.str.data - chunk_size + 1 : smp->data.u.str.data;
int i;
size_t max_size;
int max_size;
int ptr = 0;
unsigned char c;
@ -2163,9 +2163,7 @@ static int sample_conv_be2hex(const struct arg *args, struct sample *smp, void *
trash->data = 0;
if (args[0].data.str.data == 0 && args[2].data.sint == 0)
chunk_size = smp->data.u.str.data;
if (2 * (size_t)chunk_size > trash->size)
return 0;
max_size = trash->size - 2 * (size_t)chunk_size;
max_size = trash->size - 2 * chunk_size;
while (ptr < last && trash->data <= max_size) {
if (ptr) {

View file

@ -241,17 +241,14 @@ int session_accept_fd(struct connection *cli_conn)
if (l->bind_conf->options & BC_O_ACC_CIP)
cli_conn->flags |= CO_FL_ACCEPT_CIP;
if (l->bind_conf->mux_proto && l->bind_conf->mux_proto->init_xprt == XPRT_QMUX)
cli_conn->flags |= (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
/* Add the handshake pseudo-XPRT */
if (cli_conn->flags & (CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP)) {
if (xprt_add_hs(cli_conn) != 0)
goto out_free_conn;
}
/* Add handshake layer prior to MUX init if required. Does nothing if SSL layer is active though. */
if (l->bind_conf->mux_proto && l->bind_conf->mux_proto->init_xprt) {
if (xprt_add_l6hs(cli_conn, l->bind_conf->mux_proto->init_xprt))
goto out_free_conn;
}
}
/* Reversed conns already have an assigned session, do not recreate it. */
@ -354,7 +351,7 @@ int session_accept_fd(struct connection *cli_conn)
* v | | |
* conn -- owner ---> task <-----+
*/
if (cli_conn->flags & (CO_FL_WAIT_XPRT | CO_FL_EARLY_SSL_HS | CO_FL_WAIT_XPRT_L6)) {
if (cli_conn->flags & (CO_FL_WAIT_XPRT | CO_FL_EARLY_SSL_HS)) {
int timeout;
int clt_tmt = p->timeout.client;
int hs_tmt = p->timeout.client_hs;

View file

@ -448,7 +448,6 @@ sni_lookup:
for (i = 0; i < trash.size && i < servername_len; i++)
trash.area[i] = tolower((unsigned char)servername[i]);
trash.area[i] = 0;
servername = trash.area;
HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock);
sni_ctx = ssl_sock_choose_sni_ctx(s, conn, trash.area, has_rsa_sig, has_ecdsa_sig);

View file

@ -44,29 +44,6 @@ __decl_rwlock(ssl_ctx_lru_rwlock);
#ifndef SSL_NO_GENERATE_CERTIFICATES
/* Validate that <servername> contains only DNS-label-legal characters
* (letters, digits, hyphens, dots). Rejects commas, colons, and other
* characters that OpenSSL's nconf parser would interpret as SAN entry
* separators or type prefixes, preventing certificate injection. */
static int ssl_sock_sni_is_valid(const char *s)
{
size_t i;
if (!s || !*s)
return 0;
for (i = 0; s[i]; i++) {
unsigned char c = (unsigned char)s[i];
if (!(
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '-' || c == '.'
))
return 0;
}
return 1;
}
/* Configure a DNS SAN extension on a certificate. */
int ssl_sock_add_san_ext(X509V3_CTX* ctx, X509* cert, const char *servername) {
int failure = 0;
@ -123,14 +100,6 @@ static SSL_CTX *ssl_sock_do_create_cert(const char *servername, struct bind_conf
int key_type;
struct sni_ctx *sni_ctx;
/* Reject SNI values containing characters that OpenSSL's nconf
* parser would interpret as SAN entry separators (commas), type
* prefixes (colons), or other special constructs. This prevents
* attackers from injecting arbitrary GENERAL_NAME entries into
* certificates signed by HAProxy's configured CA. */
if (!ssl_sock_sni_is_valid(servername))
goto mkcert_error;
sni_ctx = ssl_sock_choose_sni_ctx(bind_conf, NULL, "", 1, 1);
if (!sni_ctx)
goto mkcert_error;
@ -387,10 +356,8 @@ int ssl_sock_generate_certificate(const char *servername, struct bind_conf *bind
ssl_ctx = (SSL_CTX *)lru->data;
if (!ssl_ctx && lru) {
ssl_ctx = ssl_sock_do_create_cert(servername, bind_conf, ssl);
if (!ssl_ctx) {
HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock);
if (!ssl_ctx)
goto error;
}
lru64_commit(lru, ssl_ctx, cacert, 0, (void (*)(void *))SSL_CTX_free);
}
SSL_set_SSL_CTX(ssl, ssl_ctx);

View file

@ -290,8 +290,6 @@ int ssl_sock_load_ocsp_response(struct buffer *ocsp_response,
int ret = 1;
#ifdef HAVE_ASN1_TIME_TO_TM
struct tm nextupd_tm = {0};
#else
long expire = 0;
#endif
resp = d2i_OCSP_RESPONSE(NULL, (const unsigned char **)&p,
@ -393,12 +391,11 @@ int ssl_sock_load_ocsp_response(struct buffer *ocsp_response,
}
ocsp->expire = my_timegm(&nextupd_tm) - OCSP_MAX_RESPONSE_TIME_SKEW;
#else
expire = asn1_generalizedtime_to_epoch(nextupd) - OCSP_MAX_RESPONSE_TIME_SKEW;
if (expire < 0) {
ocsp->expire = asn1_generalizedtime_to_epoch(nextupd) - OCSP_MAX_RESPONSE_TIME_SKEW;
if (ocsp->expire < 0) {
memprintf(err, "OCSP single response: Invalid \"Next Update\" time");
goto out;
}
ocsp->expire = expire;
#endif
if (ocsp->expire < date.tv_sec) {

View file

@ -6973,8 +6973,12 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state)
mux = !conn_is_back(conn) ?
conn_select_mux_fe(conn) : conn_select_mux_be(conn);
if (mux->init_xprt) {
ret = xprt_add_l6hs(conn, mux->init_xprt);
if (ctx->conn->flags & (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND) ||
mux->init_xprt == XPRT_QMUX) {
const struct xprt_ops *ops = xprt_get(XPRT_QMUX);
void *xprt_ctx_hs = NULL;
ret = ops->init(conn, &xprt_ctx_hs);
/* Frontend conn must be freed in case of XPRT init failure. */
if (ret) {
if (!conn_is_back(conn)) {
@ -6986,7 +6990,15 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state)
goto leave;
}
ret = conn_xprt_start(conn);
ret = ops->add_xprt(conn, xprt_ctx_hs,
conn->xprt_ctx, conn->xprt, NULL, NULL);
BUG_ON(ret); /* xprt_qmux add_xprt always succeeds */
conn->xprt = ops;
conn->xprt_ctx = xprt_ctx_hs;
ret = conn->xprt->start(conn, xprt_ctx_hs);
BUG_ON(ret);
}
else {
/* TODO MUX selection already performs by conn_select_mux_fe/be().

View file

@ -664,7 +664,7 @@ enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct
enum healthcheck_status status;
struct buffer *msg = NULL;
struct ist desc = IST_NULL;
char *ptr, *end;
char *ptr;
unsigned short nbytes = 0;
size_t msglen = 0;
@ -674,12 +674,7 @@ enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct
* http://en.wikipedia.org/wiki/Basic_Encoding_Rules
* http://tools.ietf.org/html/rfc4511
*/
ptr = b_head(&check->bi);
end = ptr + b_data(&check->bi);
ptr++; /* First byte was already matched by the previous expect rule
* and at least 14 bytes are available
* (see do_parse_ldap_check_opt)
*/
ptr = b_head(&check->bi) + 1;
/* size of LDAPMessage */
if (*ptr & 0x80) {
@ -689,7 +684,7 @@ enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct
* encode BindReponse length on 4 bytes.
*/
nbytes = (*ptr & 0x7f);
if (end - ptr < 1 + nbytes)
if (b_data(&check->bi) < 1 + nbytes)
goto too_short;
switch (nbytes) {
case 4: msglen = read_n32(ptr+1); break;
@ -704,7 +699,7 @@ enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct
msglen = *ptr;
ptr += 1 + nbytes;
if (end - ptr < msglen)
if (b_data(&check->bi) < 2 + nbytes + msglen)
goto too_short;
/* http://tools.ietf.org/html/rfc4511#section-4.2.2
@ -722,10 +717,6 @@ enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct
nbytes = 0;
if (*ptr & 0x80)
nbytes = (*ptr & 0x7f);
if (end - ptr < 1 + nbytes + 2 + 1)
goto too_short;
ptr += 1 + nbytes;
/* http://tools.ietf.org/html/rfc4511#section-4.1.9
@ -843,8 +834,6 @@ enum tcpcheck_eval_ret tcpcheck_spop_expect_hello(struct check *check, struct tc
goto invalid_frame;
if (decode_varint(&ptr, end, &sz) == -1)
goto invalid_frame;
if (sz >= SPOP_ERR_ENTRIES)
sz = SPOP_ERR_UNKNOWN;
check->code = sz;
}
@ -1000,7 +989,7 @@ enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct t
const char *sc = NULL; /* maxconn */
const char *err = NULL; /* first error to report */
const char *wrn = NULL; /* first warning to report */
char *cmd, *p, *end;
char *cmd, *p;
TRACE_ENTER(CHK_EV_TCPCHK_EXP, check);
@ -1029,11 +1018,10 @@ enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct t
*/
p = b_head(&check->bi);
end = b_tail(&check->bi);
while (p < end && *p && *p != '\n' && *p != '\r')
while (*p && *p != '\n' && *p != '\r')
p++;
if (!*p || p == end) {
if (!*p) {
if (!last_read)
goto wait_more_data;

View file

@ -6235,106 +6235,48 @@ int varint_bytes(uint64_t v)
return len;
}
/* secret used for XXH hash involved in PRNG */
static char ha_random_xxh_secret[XXH3_SECRET_DEFAULT_SIZE] ALIGNED(64);
/* 2^256 sequnce thread-local PRNG state known as "XOSHIRO256**".
* See details here:
* https://prng.di.unimi.it/
* https://prng.di.unimi.it/xoshiro256starstar.c
* It features a 2^256 long sequence, returns 64 high-quality bits on each call,
* supports fast jumps and passes all common quality tests. Supporting 128-bit
* jumps, it allows to run thread-local with non-overlapping sequences. It must
* be seeded otherwise the ratio of zeroes is a bit high initially.
*/
static THREAD_LOCAL uint64_t ha_random_state[4];
/* Random number generator state, see below */
static uint64_t ha_random_state[2] ALIGNED(2*sizeof(uint64_t));
/* Returns the next 64-bit PRNG number from the thread-local 256-bit state and
* makes the internal state progress by one step. This is meant to be used by
* other local functions. Since its discloses the PRNG's internal state, it
* must not be called to produce externally visible randoms.
/* This is a thread-safe implementation of xoroshiro128** described below:
* http://prng.di.unimi.it/
* It features a 2^128 long sequence, returns 64 high-quality bits on each call,
* supports fast jumps and passes all common quality tests. It is thread-safe,
* uses a double-cas on 64-bit architectures supporting it, and falls back to a
* local lock on other ones.
*/
static inline uint64_t _ha_random64_internal(void)
uint64_t ha_random64()
{
const uint64_t result = rotl64(ha_random_state[1] * 5, 7) * 9;
const uint64_t t = ha_random_state[1] << 17;
uint64_t old[2] ALIGNED(2*sizeof(uint64_t));
uint64_t new[2] ALIGNED(2*sizeof(uint64_t));
ha_random_state[2] ^= ha_random_state[0];
ha_random_state[3] ^= ha_random_state[1];
ha_random_state[1] ^= ha_random_state[2];
ha_random_state[0] ^= ha_random_state[3];
ha_random_state[2] ^= t;
ha_random_state[3] = rotl64(ha_random_state[3], 45);
return result;
}
#if defined(USE_THREAD) && (!defined(HA_CAS_IS_8B) || !defined(HA_HAVE_CAS_DW))
static HA_SPINLOCK_T rand_lock;
/* Returns the next 64-bit PRNG number from the thread-local 256-bit state and
* makes the internal state progress by one step. Since its discloses the PRNG's
* internal state, it must not be called to produce externally visible randoms.
*/
uint64_t ha_random64_internal(void)
{
return _ha_random64_internal();
}
HA_SPIN_LOCK(OTHER_LOCK, &rand_lock);
#endif
/* This function uses a pre-calculated jump table to of 4 uint64_t to perform a
* jump equivalent to multiple calls to ha_random_next(). It shouldn't be
* used directly but only from the next functions.
*/
static void _ha_random_jump(const uint64_t *table)
{
uint64_t s0, s1, s2, s3;
uint i, j;
old[0] = ha_random_state[0];
old[1] = ha_random_state[1];
s0 = s1 = s2 = s3 = 0;
for (i = 0; i < 4; i++) {
for (j = 0; j < 64; j++) {
if (table[i] & (1ULL << j)) {
s0 ^= ha_random_state[0];
s1 ^= ha_random_state[1];
s2 ^= ha_random_state[2];
s3 ^= ha_random_state[3];
}
ha_random64_internal();
}
}
#if defined(USE_THREAD) && defined(HA_CAS_IS_8B) && defined(HA_HAVE_CAS_DW)
do {
#endif
new[1] = old[0] ^ old[1];
new[0] = rotl64(old[0], 24) ^ new[1] ^ (new[1] << 16); // a, b
new[1] = rotl64(new[1], 37); // c
ha_random_state[0] = s0;
ha_random_state[1] = s1;
ha_random_state[2] = s2;
ha_random_state[3] = s3;
}
/* This function is equivalent to calling <dist> times 2^128 calls to
* ha_random_next(). It can be used to generate 2^128 non-overlapping
* sequences. The <dist> argument is the distance to jump to and is used
* in a loop so it rather not be too large if the processing time is a
* concern. It only applies to the current thread. Note that <dist> may
* not be zero.
*/
void ha_random_jump128(uint32_t dist)
{
static const uint64_t table[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c };
BUG_ON(!dist);
while (dist--)
_ha_random_jump(table);
}
/* This function is equivalent to calling <dist> times 2^192 calls to
* ha_random_next(). It can be used to generate 2^64 non-overlapping
* sequences. The <dist> argument is the distance to jump to and is used
* in a loop so it rather not be too large if the processing time is a
* concern. It only applies to the current thread. Note that <dist> may
* not be zero.
*/
void ha_random_jump192(uint32_t dist)
{
static const uint64_t table[] = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 };
BUG_ON(!dist);
while (dist--)
_ha_random_jump(table);
#if defined(USE_THREAD) && defined(HA_CAS_IS_8B) && defined(HA_HAVE_CAS_DW)
} while (unlikely(!_HA_ATOMIC_DWCAS(ha_random_state, old, new)));
#else
ha_random_state[0] = new[0];
ha_random_state[1] = new[1];
#if defined(USE_THREAD)
HA_SPIN_UNLOCK(OTHER_LOCK, &rand_lock);
#endif
#endif
return rotl64(old[0] * 5, 7) * 9;
}
/* seeds the random state using up to <len> bytes from <seed>, starting with
@ -6364,49 +6306,43 @@ void ha_random_seed(const unsigned char *seed, size_t len)
len = sizeof(ha_random_state);
memcpy(ha_random_state, seed, len);
/* also initialize the secret table used by XXH3 */
XXH3_generateSecret(ha_random_xxh_secret, sizeof(ha_random_xxh_secret), seed, len);
}
/* Seed the PRNG for the current thread */
void ha_random_seed_thread(void)
{
/* seed already done for first thread, but jump still necessary */
if (tid > 0)
ha_random_seed(boot_seed, sizeof(boot_seed));
ha_random_jump192(tid + 1);
}
/* Returns a uint64_t random hashed so as not to disclose the internal PRNG
* state. The function uses a local XXH secret that is created at boot, and
* now_ns as the seed to limit remote analysis.
/* This causes a jump to (dist * 2^96) places in the pseudo-random sequence,
* and is equivalent to calling ha_random64() as many times. It is used to
* provide non-overlapping sequences of 2^96 numbers (~7*10^28) to up to 2^32
* different generators (i.e. different processes after a fork). The <dist>
* argument is the distance to jump to and is used in a loop so it rather not
* be too large if the processing time is a concern.
*
* BEWARE: this function is NOT thread-safe and must not be called during
* concurrent accesses to ha_random64().
*/
uint64_t ha_random64(void)
void ha_random_jump96(uint32_t dist)
{
uint64_t ret;
while (dist--) {
uint64_t s0 = 0;
uint64_t s1 = 0;
int b;
ret = _ha_random64_internal();
return XXH3_64bits_withSecretandSeed(&ret, sizeof(ret),
ha_random_xxh_secret, sizeof(ha_random_xxh_secret),
now_ns);
}
for (b = 0; b < 64; b++) {
if ((0xd2a98b26625eee7bULL >> b) & 1) {
s0 ^= ha_random_state[0];
s1 ^= ha_random_state[1];
}
ha_random64();
}
/* Returns a pair of uint64_t randoms hashed so as not to disclose the internal
* PRNG state. This function shouldn't be used directly, better use the public
* ha_random64_pair_hashed() which calls it. The function uses a local XXH
* secret that is created at boot, and now_ns as the seed to limit remote
* analysis.
*/
struct uint64_pair _ha_random64_pair_hashed(void)
{
XXH128_hash_t ret;
ret = XXH3_128bits_withSecretandSeed(ha_random_state, 2*sizeof(uint64_t),
ha_random_xxh_secret, sizeof(ha_random_xxh_secret),
now_ns);
/* update the internal state */
_ha_random64_internal();
return (struct uint64_pair){ .l = ret.low64, .h = ret.high64 };
for (b = 0; b < 64; b++) {
if ((0xdddf9b1090aa7ac1ULL >> b) & 1) {
s0 ^= ha_random_state[0];
s1 ^= ha_random_state[1];
}
ha_random64();
}
ha_random_state[0] = s0;
ha_random_state[1] = s1;
}
}
/* Generates an RFC 9562 version 4 UUID into chunk
@ -6414,15 +6350,23 @@ struct uint64_pair _ha_random64_pair_hashed(void)
*/
void ha_generate_uuid_v4(struct buffer *output)
{
uint64_t l, h;
uint32_t rnd[4];
uint64_t last;
last = ha_random64();
rnd[0] = last;
rnd[1] = last >> 32;
last = ha_random64();
rnd[2] = last;
rnd[3] = last >> 32;
ha_random64_pair_hashed(&l, &h);
chunk_printf(output, "%8.8x-%4.4x-%4.4x-%4.4x-%12.12llx",
(uint)l,
(uint)(l >> 32) & 0xFFFF,
(uint)((l >> 48) & 0xFFF) | 0x4000, // highest 4 bits indicate the uuid version
(uint)(h & 0x3FFF) | 0x8000, // the highest 2 bits indicate the UUID variant (10),
(long long)(rotl64(h, 50) & 0xFFFFFFFFFFFFull));
rnd[0],
rnd[1] & 0xFFFF,
((rnd[1] >> 16u) & 0xFFF) | 0x4000, // highest 4 bits indicate the uuid version
(rnd[2] & 0x3FFF) | 0x8000, // the highest 2 bits indicate the UUID variant (10),
(long long)((rnd[2] >> 14u) | ((uint64_t) rnd[3] << 18u)) & 0xFFFFFFFFFFFFull);
}
/* Generates an RFC 9562 version 7 UUID into chunk
@ -6430,18 +6374,24 @@ void ha_generate_uuid_v4(struct buffer *output)
*/
void ha_generate_uuid_v7(struct buffer *output)
{
uint64_t l, h;
uint32_t rnd[3];
uint64_t last;
uint64_t time;
time = (date.tv_sec * 1000) + (date.tv_usec / 1000);
last = ha_random64();
rnd[0] = last;
rnd[1] = last >> 32;
last = ha_random64();
rnd[2] = last;
ha_random64_pair_hashed(&l, &h);
chunk_printf(output, "%8.8x-%4.4x-%4.4x-%4.4x-%12.12llx",
(uint)(time >> 16u),
(uint)(time & 0xFFFF),
(uint)((l >> 16) & 0xFFF) | 0x7000, // highest 4 bits indicate the uuid version
(uint)(h & 0x3FFF) | 0x8000, // the highest 2 bits indicate the UUID variant (10),
(long long)(rotl64(h, 50) & 0xFFFFFFFFFFFFull));
((rnd[0] >> 16u) & 0xFFF) | 0x7000, // highest 4 bits indicate the uuid version
(rnd[1] & 0x3FFF) | 0x8000, // the highest 2 bits indicate the UUID variant (10),
(long long)((rnd[1] >> 14u) | ((uint64_t) rnd[2] << 18u)) & 0xFFFFFFFFFFFFull);
}

View file

@ -12,9 +12,6 @@
#include <haproxy/quic_frame.h>
#include <haproxy/quic_tp-t.h>
/* Default protocol when not running over SSL layer. */
#define XPRT_QMUX_DEFAULT_ALPN "h3"
struct xprt_qmux_ctx {
struct connection *conn;
struct wait_event wait_event;
@ -97,10 +94,6 @@ int conn_recv_qmux(struct connection *conn, struct xprt_qmux_ctx *ctx, int flag)
ctx->rxrlen = rlen;
}
/* TODO initial max_record_size is limited to 16382 */
if (ctx->rxrlen > b_size(buf))
goto fail;
if (ctx->rxrlen > b_data(buf))
goto not_ready;
@ -110,8 +103,7 @@ int conn_recv_qmux(struct connection *conn, struct xprt_qmux_ctx *ctx, int flag)
goto fail;
/* TODO close connection with TRANSPORT_PARAMETER_ERROR if frame not present. */
if (frm.type != QUIC_FT_QX_TRANSPORT_PARAMETERS)
goto fail;
BUG_ON(frm.type != QUIC_FT_QX_TRANSPORT_PARAMETERS);
if (!qc_parse_frm_payload(&frm, &pos, end, NULL))
goto fail;
@ -215,7 +207,7 @@ struct task *xprt_qmux_io_cb(struct task *t, void *context, unsigned int state)
out:
if ((conn->flags & CO_FL_ERROR) ||
!(conn->flags & CO_FL_WAIT_XPRT_L6)) {
!(conn->flags & (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND))) {
/* XPRT should be unsubscribed when transfer done or on error. */
BUG_ON(ctx->wait_event.events);
@ -340,7 +332,7 @@ static void xprt_qmux_close(struct connection *conn, void *xprt_ctx)
if (ctx->ops_lower && ctx->ops_lower->close)
ctx->ops_lower->close(conn, ctx->ctx_lower);
conn->flags &= ~CO_FL_WAIT_XPRT_L6;
conn->flags &= ~(CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
BUG_ON(conn->xprt_ctx != ctx);
conn->xprt_ctx = ctx->ctx_lower;
@ -354,14 +346,6 @@ static int xprt_qmux_get_alpn(const struct connection *conn, void *xprt_ctx,
const char **str, int *len)
{
struct xprt_qmux_ctx *ctx = xprt_ctx;
/* Return a the default ALPN if lower layer is not able to negotiate it. */
if (!ctx->ops_lower || !ctx->ops_lower->get_alpn) {
*str = XPRT_QMUX_DEFAULT_ALPN;
*len = strlen(XPRT_QMUX_DEFAULT_ALPN);
return 1;
}
return ctx->ops_lower->get_alpn(conn, ctx->ctx_lower, str, len);
}