mirror of
https://github.com/haproxy/haproxy.git
synced 2026-05-28 04:12:17 -04:00
Compare commits
No commits in common. "master" and "v3.4-dev13" have entirely different histories.
master
...
v3.4-dev13
71 changed files with 346 additions and 955 deletions
83
CHANGELOG
83
CHANGELOG
|
|
@ -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
|
||||
|
|
|
|||
2
VERDATE
2
VERDATE
|
|
@ -1,2 +1,2 @@
|
|||
$Format:%ci$
|
||||
2026/05/26
|
||||
2026/05/20
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
3.4-dev14
|
||||
3.4-dev13
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -179,8 +179,6 @@ enum {
|
|||
/* below we have all handshake flags grouped into one */
|
||||
CO_FL_HANDSHAKE = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
|
||||
CO_FL_WAIT_XPRT = CO_FL_WAIT_L4_CONN | CO_FL_HANDSHAKE | CO_FL_WAIT_L6_CONN,
|
||||
/* handshake running on top of a layer6 */
|
||||
CO_FL_WAIT_XPRT_L6 = CO_FL_QMUX_SEND | CO_FL_QMUX_RECV,
|
||||
|
||||
CO_FL_SSL_WAIT_HS = 0x08000000, /* wait for an SSL handshake to complete */
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,6 @@ int conn_reverse(struct connection *conn);
|
|||
const char *conn_err_code_name(struct connection *c);
|
||||
const char *conn_err_code_str(struct connection *c);
|
||||
int xprt_add_hs(struct connection *conn);
|
||||
int xprt_add_l6hs(struct connection *conn, int xprt);
|
||||
void register_mux_proto(struct mux_proto_list *list);
|
||||
|
||||
static inline void conn_report_term_evt(struct connection *conn, enum term_event_loc loc, unsigned char type);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -141,7 +141,6 @@
|
|||
#define HTX_SL_F_CONN_UPG 0x00001000 /* The message contains "connection: upgrade" header */
|
||||
#define HTX_SL_F_BODYLESS_RESP 0x00002000 /* The response to this message is bodyless (only for request) */
|
||||
#define HTX_SL_F_NOT_HTTP 0x00004000 /* Not an HTTP message (e.g "RTSP", only possible if invalid message are accepted) */
|
||||
#define HTX_SL_F_UPG_HDR 0x00008000 /* non-empty Upgrapde header found */
|
||||
|
||||
/* This function is used to report flags in debugging tools. Please reflect
|
||||
* below any single-bit flag addition above in the same order via the
|
||||
|
|
@ -158,8 +157,7 @@ static forceinline char *hsl_show_flags(char *buf, size_t len, const char *delim
|
|||
_(HTX_SL_F_CLEN, _(HTX_SL_F_CHNK, _(HTX_SL_F_VER_11,
|
||||
_(HTX_SL_F_BODYLESS, _(HTX_SL_F_HAS_SCHM, _(HTX_SL_F_SCHM_HTTP,
|
||||
_(HTX_SL_F_SCHM_HTTPS, _(HTX_SL_F_HAS_AUTHORITY,
|
||||
_(HTX_SL_F_NORMALIZED_URI, _(HTX_SL_F_CONN_UPG, _(HTX_SL_F_BODYLESS_RESP,
|
||||
_(HTX_SL_F_NOT_HTTP, _(HTX_SL_F_UPG_HDR))))))))))))))));
|
||||
_(HTX_SL_F_NORMALIZED_URI, _(HTX_SL_F_CONN_UPG)))))))))))));
|
||||
/* epilogue */
|
||||
_(~0U);
|
||||
return buf;
|
||||
|
|
|
|||
|
|
@ -121,8 +121,8 @@ static inline size_t array_size_or_fail(size_t m, size_t n)
|
|||
{
|
||||
size_t size;
|
||||
|
||||
if (unlikely(mulsz_overflow(m, n, &size)))
|
||||
return DISGUISE(~(size_t)0);
|
||||
if (mulsz_overflow(m, n, &size))
|
||||
return ~(size_t)0;
|
||||
return size;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,14 +43,6 @@
|
|||
#define QPACK_DEC_INST_SCCL 0x40 // Stream Cancellation
|
||||
#define QPACK_DEC_INST_SACK 0x80 // Section Acknowledgment
|
||||
|
||||
/* Encoded field line bitmasks (shared between encoder and decoder) */
|
||||
#define QPACK_EFL_BITMASK 0xf0
|
||||
#define QPACK_LFL_WPBNM 0x00 // Literal field line with post-base name reference
|
||||
#define QPACK_IFL_WPBI 0x10 // Indexed field line with post-based index
|
||||
#define QPACK_LFL_WLN_BIT 0x20 // Literal field line with literal name
|
||||
#define QPACK_LFL_WNR_BIT 0x40 // Literal field line with name reference
|
||||
#define QPACK_IFL_BIT 0x80 // Indexed field line
|
||||
|
||||
/* RFC 9204 6. Error Handling */
|
||||
enum qpack_err {
|
||||
QPACK_ERR_DECOMPRESSION_FAILED = 0x200,
|
||||
|
|
|
|||
|
|
@ -93,30 +93,24 @@ static inline struct ist qpack_get_value(const struct qpack_dht *dht, const stru
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* takes an absolute idx (including static table offset), returns the associated name */
|
||||
/* takes an idx, returns the associated name */
|
||||
static inline struct ist qpack_idx_to_name(const struct qpack_dht *dht, uint32_t idx)
|
||||
{
|
||||
const struct qpack_dte *dte;
|
||||
|
||||
if (idx < QPACK_SHT_SIZE)
|
||||
return ist("### ERR ###"); /* static table entries not accessible via dht */
|
||||
|
||||
dte = qpack_get_dte(dht, idx - QPACK_SHT_SIZE);
|
||||
dte = qpack_get_dte(dht, idx);
|
||||
if (!dte)
|
||||
return ist("### ERR ###"); // error
|
||||
|
||||
return qpack_get_name(dht, dte);
|
||||
}
|
||||
|
||||
/* takes an absolute idx (including static table offset), returns the associated value */
|
||||
/* takes an idx, returns the associated value */
|
||||
static inline struct ist qpack_idx_to_value(const struct qpack_dht *dht, uint32_t idx)
|
||||
{
|
||||
const struct qpack_dte *dte;
|
||||
|
||||
if (idx < QPACK_SHT_SIZE)
|
||||
return ist("### ERR ###"); /* static table entries not accessible via dht */
|
||||
|
||||
dte = qpack_get_dte(dht, idx - QPACK_SHT_SIZE);
|
||||
dte = qpack_get_dte(dht, idx);
|
||||
if (!dte)
|
||||
return ist("### ERR ###"); // error
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* include/haproxy/resolvers-t.h
|
||||
* include/haproxy/dns-t.h
|
||||
* This file provides structures and types for DNS.
|
||||
*
|
||||
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
|
||||
|
|
@ -114,7 +114,7 @@ struct resolv_answer_item {
|
|||
char name[DNS_MAX_NAME_SIZE+1]; /* answer name */
|
||||
int16_t type; /* question type */
|
||||
int16_t class; /* query class */
|
||||
uint32_t ttl; /* response TTL */
|
||||
int32_t ttl; /* response TTL */
|
||||
int16_t priority; /* SRV type priority */
|
||||
uint16_t weight; /* SRV type weight */
|
||||
uint16_t port; /* SRV type port */
|
||||
|
|
@ -281,7 +281,7 @@ enum {
|
|||
* matching preference was found.
|
||||
*/
|
||||
RSLV_UPD_SRVIP_NOT_FOUND, /* provided IP not found
|
||||
* OR provided IP found and preference is not matched and an IP
|
||||
* OR provided IP found and preference is not match and an IP
|
||||
* matching preference was found.
|
||||
*/
|
||||
RSLV_UPD_NO_IP_FOUND, /* no IP could be found in the response */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* include/haproxy/resolvers.h
|
||||
* include/haproxy/dns.h
|
||||
* This file provides functions related to DNS protocol
|
||||
*
|
||||
* Copyright (C) 2014 Baptiste Assmann <bedis9@gmail.com>
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ struct certificate_ocsp {
|
|||
int refcount_store; /* Number of ckch_store that reference this certificate_ocsp */
|
||||
int refcount; /* Number of actual references to this certificate_ocsp (SSL_CTXs mostly) */
|
||||
struct buffer response;
|
||||
unsigned long expire;
|
||||
long expire;
|
||||
X509 *issuer;
|
||||
STACK_OF(X509) *chain;
|
||||
struct eb64_node next_update; /* Key of items inserted in ocsp_update_tree (sorted by absolute date) */
|
||||
|
|
|
|||
|
|
@ -188,12 +188,4 @@ struct file_name_node {
|
|||
char name[VAR_ARRAY]; /* storage, used with cebus_*() */
|
||||
};
|
||||
|
||||
/* a pair of uint64_t. It's purposely arranged in little endian to help
|
||||
* being vectorized on modern processors.
|
||||
*/
|
||||
struct uint64_pair {
|
||||
uint64_t l;
|
||||
uint64_t h;
|
||||
};
|
||||
|
||||
#endif /* _HAPROXY_TOOLS_T_H */
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
24
src/acme.c
24
src/acme.c
|
|
@ -1532,7 +1532,7 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||
|
||||
hdrs = hc->res.hdrs;
|
||||
|
||||
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||
istfree(&ctx->nonce);
|
||||
ctx->nonce = istdup(hdr->v);
|
||||
|
|
@ -1562,16 +1562,6 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||
key = ctx->store->data->key;
|
||||
ctx->store->data->key = NULL;
|
||||
|
||||
/* OpenSSL's BIO_new_mem_buf() expects a NUL-terminated string when
|
||||
* passed -1. The httpclient buffer lacks this, so manually terminate
|
||||
* it here to prevent an out-of-bounds heap read during PEM parsing.
|
||||
*/
|
||||
if (b_room(&hc->res.buf) < 1) {
|
||||
memprintf(errmsg, "ACME certificate response has no room for NUL terminator");
|
||||
goto error;
|
||||
}
|
||||
hc->res.buf.area[hc->res.buf.data] = '\0';
|
||||
|
||||
/* XXX: might need a function dedicated to this, which does not read a private key */
|
||||
if (ssl_sock_load_pem_into_ckch(ctx->store->path, hc->res.buf.area, ctx->store->data , errmsg) != 0)
|
||||
goto error;
|
||||
|
|
@ -1745,7 +1735,7 @@ int acme_res_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||
|
||||
hdrs = hc->res.hdrs;
|
||||
|
||||
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||
istfree(&ctx->nonce);
|
||||
ctx->nonce = istdup(hdr->v);
|
||||
|
|
@ -1846,7 +1836,7 @@ enum acme_ret acme_res_challenge(struct task *task, struct acme_ctx *ctx, struct
|
|||
|
||||
TRACE_DATA(__FUNCTION__, ACME_EV_RES, ctx, NULL, &hc->res.buf);
|
||||
|
||||
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||
istfree(&ctx->nonce);
|
||||
ctx->nonce = istdup(hdr->v);
|
||||
|
|
@ -1973,7 +1963,7 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||
|
||||
hdrs = hc->res.hdrs;
|
||||
|
||||
for (hdr = hdrs; hdrs && isttest(hdr->v); hdr++) {
|
||||
for (hdr = hdrs; isttest(hdr->v); hdr++) {
|
||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||
istfree(&ctx->nonce);
|
||||
ctx->nonce = istdup(hdr->v);
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ check_user(struct userlist *ul, const char *user, const char *pass)
|
|||
fprintf(stderr, ", crypt=%s\n", ((ep) ? ep : ""));
|
||||
#endif
|
||||
|
||||
if (ep && u->pass && strcmp(ep, u->pass) == 0)
|
||||
if (ep && strcmp(ep, u->pass) == 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1818,7 +1818,7 @@ int connect_server(struct stream *s)
|
|||
{
|
||||
struct connection *cli_conn = objt_conn(strm_orig(s));
|
||||
struct connection *srv_conn = NULL;
|
||||
const struct mux_proto_list *mux_proto = NULL;
|
||||
const struct mux_proto_list *mux_proto;
|
||||
struct server *srv;
|
||||
struct ist name = IST_NULL;
|
||||
struct sample *name_smp;
|
||||
|
|
@ -2139,10 +2139,12 @@ int connect_server(struct stream *s)
|
|||
}
|
||||
|
||||
if (may_start_mux_now) {
|
||||
/* Delay MUX init if an XPRT handshake is required prior. */
|
||||
/* Delay QMux MUX init to let xprt_qmux handshake runs first. */
|
||||
mux_proto = conn_select_mux_be(srv_conn);
|
||||
if (mux_proto && mux_proto->init_xprt)
|
||||
if (mux_proto && mux_proto->init_xprt == XPRT_QMUX) {
|
||||
srv_conn->flags |= (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND);
|
||||
may_start_mux_now = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
||||
|
|
@ -2252,13 +2254,6 @@ int connect_server(struct stream *s)
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (mux_proto && mux_proto->init_xprt) {
|
||||
/* Add handshake layer prior to MUX init if required. Does nothing if SSL layer is active though. */
|
||||
if (xprt_add_l6hs(srv_conn, mux_proto->init_xprt)) {
|
||||
conn_full_close(srv_conn);
|
||||
return SF_ERR_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that the mux may have been created, we can start the xprt.
|
||||
|
|
|
|||
|
|
@ -125,9 +125,6 @@ int base64dec(const char *in, size_t ilen, char *out, size_t olen) {
|
|||
signed char b;
|
||||
int convlen = 0, i = 0, pad = 0;
|
||||
|
||||
if (!ilen)
|
||||
return 0;
|
||||
|
||||
if (ilen % 4)
|
||||
return -1;
|
||||
|
||||
|
|
|
|||
23
src/cache.c
23
src/cache.c
|
|
@ -374,7 +374,7 @@ static int secondary_key_cmp(const char *ref_key, const char *new_key)
|
|||
* delete_expired==0, write otherwise.
|
||||
*/
|
||||
struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_entry *entry,
|
||||
const char *primary_hash, const char *secondary_key, int delete_expired)
|
||||
const char *secondary_key, int delete_expired)
|
||||
{
|
||||
struct eb32_node *node = &entry->eb;
|
||||
|
||||
|
|
@ -395,12 +395,6 @@ struct cache_entry *get_secondary_entry(struct cache_tree *cache, struct cache_e
|
|||
entry = node ? eb32_entry(node, struct cache_entry, eb) : NULL;
|
||||
}
|
||||
|
||||
/* Now verify the full primary hash matches: eb32 only compares 32 bits so
|
||||
* we could have ended up on a different, unrelated entry.
|
||||
*/
|
||||
if (entry && primary_hash && memcmp(entry->hash, primary_hash, sizeof(entry->hash)))
|
||||
entry = NULL;
|
||||
|
||||
/* Expired entry */
|
||||
if (entry && entry->expire <= date.tv_sec) {
|
||||
if (delete_expired) {
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1151,13 +1151,8 @@ int cli_parse_cmdline(struct appctx *appctx)
|
|||
*/
|
||||
if (len-1 == strlen(appctx->cli_ctx.payload_pat)) {
|
||||
if (strncmp(str, appctx->cli_ctx.payload_pat, len-1) == 0) {
|
||||
/* end of payload was reached, rewind before the previous \n, if any, and replace it by a \0
|
||||
* Otherwise, the payload is empty, just
|
||||
*/
|
||||
if (b_data(buf) > len)
|
||||
b_sub(buf, len+1);
|
||||
else
|
||||
b_sub(buf, len);
|
||||
/* end of payload was reached, rewind before the previous \n and replace it by a \0 */
|
||||
b_sub(buf, strlen(appctx->cli_ctx.payload_pat) + 2);
|
||||
*b_tail(buf) = '\0';
|
||||
appctx->st1 &= ~APPCTX_CLI_ST1_PAYLOAD;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake)
|
|||
* information to create one, typically from the ALPN. If we're
|
||||
* done with the handshake, attempt to create one.
|
||||
*/
|
||||
if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_WAIT_XPRT_L6))) {
|
||||
if (unlikely(!conn->mux) && !(conn->flags & (CO_FL_WAIT_XPRT|CO_FL_QMUX_RECV|CO_FL_QMUX_SEND))) {
|
||||
ret = conn_create_mux(conn, NULL);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
|
@ -847,43 +847,6 @@ int xprt_add_hs(struct connection *conn)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Activates an <xprt> layer on top of <conn> connection. This handshake layer
|
||||
* should be designed to work on top of the layer 6. If SSL is active and its
|
||||
* handshake still in progress, this function does nothing.
|
||||
*
|
||||
* Returns 0 on success else a negative error code.
|
||||
*/
|
||||
int xprt_add_l6hs(struct connection *conn, int xprt)
|
||||
{
|
||||
const struct xprt_ops *ops = xprt_get(xprt);
|
||||
void *ops_ctx = NULL;
|
||||
|
||||
/* Only QMux is supported as handshake on top of layer6 for now. */
|
||||
BUG_ON(xprt != XPRT_QMUX);
|
||||
|
||||
if (conn->flags & CO_FL_ERROR)
|
||||
return -1;
|
||||
|
||||
/* Do nothing if SSL is in used but handshake still in progress. In
|
||||
* this case, xprt layer will be added on handshake completion.
|
||||
*/
|
||||
if (conn->xprt == xprt_get(XPRT_SSL) &&
|
||||
(conn->flags & CO_FL_WAIT_L6_CONN)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ops->init(conn, &ops_ctx))
|
||||
return -1;
|
||||
|
||||
ops->add_xprt(conn, ops_ctx, conn->xprt_ctx, conn->xprt, NULL, NULL);
|
||||
conn->xprt = ops;
|
||||
conn->xprt_ctx = ops_ctx;
|
||||
/* Reset XPRT READY flag before the next conn_xprt_start(). */
|
||||
conn->flags &= ~CO_FL_XPRT_READY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns a short name for an error, typically the same as the enum name
|
||||
* without the "CO_ER_" prefix, or an empty string for no error (better eye
|
||||
* catching in logs). This is more compact for some debug cases.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
24
src/dict.c
24
src/dict.c
|
|
@ -79,7 +79,7 @@ static struct dict_entry *__dict_lookup(struct dict *d, const char *s)
|
|||
*/
|
||||
struct dict_entry *dict_insert(struct dict *d, char *s)
|
||||
{
|
||||
struct dict_entry *de, *tree_de;
|
||||
struct dict_entry *de;
|
||||
struct ebpt_node *n;
|
||||
|
||||
HA_RWLOCK_RDLOCK(DICT_LOCK, &d->rwlock);
|
||||
|
|
@ -97,18 +97,13 @@ struct dict_entry *dict_insert(struct dict *d, char *s)
|
|||
|
||||
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
||||
n = ebis_insert(&d->values, &de->value);
|
||||
tree_de = container_of(n, struct dict_entry, value);
|
||||
if (tree_de == de)
|
||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||
else {
|
||||
/* another entry was already there, we'll return it, kill
|
||||
* ours and bump the other's refcount before returning it.
|
||||
*/
|
||||
HA_ATOMIC_INC(&tree_de->refcount);
|
||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||
if (n != &de->value) {
|
||||
free_dict_entry(de);
|
||||
de = container_of(n, struct dict_entry, value);
|
||||
}
|
||||
return tree_de;
|
||||
|
||||
return de;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -122,11 +117,10 @@ void dict_entry_unref(struct dict *d, struct dict_entry *de)
|
|||
if (!de)
|
||||
return;
|
||||
|
||||
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
||||
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0) {
|
||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||
if (HA_ATOMIC_SUB_FETCH(&de->refcount, 1) != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
|
||||
ebpt_delete(&de->value);
|
||||
HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
|
||||
|
||||
|
|
|
|||
30
src/h1.c
30
src/h1.c
|
|
@ -316,9 +316,6 @@ void h1_parse_upgrade_header(struct h1m *h1m, struct ist *value)
|
|||
skip_val:
|
||||
word.ptr = p = n;
|
||||
}
|
||||
|
||||
if (istlen(*value))
|
||||
h1m->flags |= H1_MF_UPG_HDR;
|
||||
}
|
||||
|
||||
/* Macros used in the HTTP/1 parser, to check for the expected presence of
|
||||
|
|
@ -713,16 +710,6 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
|||
case H1_MSG_RPCODE:
|
||||
http_msg_rpcode:
|
||||
if (likely(HTTP_IS_DIGIT(*ptr))) {
|
||||
if (ptr - sl.st.c.ptr >= 3) {
|
||||
/* more than 3 digits */
|
||||
if (h1m->err_pos == -1) /* only capture the error pointer */
|
||||
h1m->err_pos = ptr - start + skip;
|
||||
else if (h1m->err_pos < -1 || sl.st.status >= ((uint16_t)~0 - 9) / 10) {
|
||||
/* strict checks or risk of overflow */
|
||||
state = H1_MSG_RPCODE;
|
||||
goto http_msg_invalid;
|
||||
}
|
||||
}
|
||||
sl.st.status = sl.st.status * 10 + *ptr - '0';
|
||||
EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpcode, http_msg_ood, state, H1_MSG_RPCODE);
|
||||
}
|
||||
|
|
@ -965,20 +952,6 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
|||
goto http_output_full;
|
||||
}
|
||||
|
||||
/* Skip headers whose names contain forbidden
|
||||
* chars. When any is detected, h1m->err_pos >= 0,
|
||||
* so we recheck the name only when an error was
|
||||
* detected.
|
||||
*/
|
||||
if (unlikely(h1m->err_pos >= 0)) {
|
||||
size_t i = 0;
|
||||
while (i < n.len && HTTP_IS_TOKEN(n.ptr[i]))
|
||||
i++;
|
||||
|
||||
if (i < n.len)
|
||||
break;
|
||||
}
|
||||
|
||||
if (isteqi(n, ist("transfer-encoding"))) {
|
||||
ret = h1_parse_xfer_enc_header(h1m, v);
|
||||
if (ret < 0) {
|
||||
|
|
@ -1275,10 +1248,9 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
|||
void h1_generate_random_ws_input_key(char key_out[25])
|
||||
{
|
||||
/* generate a random websocket key */
|
||||
uint64_t rand1, rand2;
|
||||
const uint64_t rand1 = ha_random64(), rand2 = ha_random64();
|
||||
char key[16];
|
||||
|
||||
ha_random64_pair_hashed(&rand1, &rand2);
|
||||
memcpy(key, &rand1, 8);
|
||||
memcpy(&key[8], &rand2, 8);
|
||||
a2base64(key, 16, key_out, 25);
|
||||
|
|
|
|||
27
src/h1_htx.c
27
src/h1_htx.c
|
|
@ -162,8 +162,6 @@ static unsigned int h1m_htx_sl_flags(struct h1m *h1m)
|
|||
}
|
||||
if (h1m->flags & H1_MF_CONN_UPG)
|
||||
flags |= HTX_SL_F_CONN_UPG;
|
||||
if (h1m->flags & H1_MF_UPG_HDR)
|
||||
flags |= HTX_SL_F_UPG_HDR;
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
|
@ -215,31 +213,6 @@ static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx
|
|||
}
|
||||
}
|
||||
|
||||
/* Remove Upgrade header if no 'connection: upgrade' found */
|
||||
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_HDR)) == H1_MF_UPG_HDR) {
|
||||
int i;
|
||||
|
||||
for (i = 0; hdrs[i].n.len; i++) {
|
||||
if (isteqi(hdrs[i].n, ist("upgrade")))
|
||||
hdrs[i].v = IST_NULL;
|
||||
}
|
||||
h1m->flags &=~ (H1_MF_CONN_UPG|H1_MF_UPG_HDR);
|
||||
}
|
||||
|
||||
/* Remove 'Upgrade' value from connection header if not Upgrade header found */
|
||||
if ((h1m->flags & (H1_MF_CONN_UPG|H1_MF_UPG_HDR)) == H1_MF_CONN_UPG) {
|
||||
int i;
|
||||
|
||||
for (i = 0; hdrs[i].n.len; i++) {
|
||||
if (isteqi(hdrs[i].n, ist("connection"))) {
|
||||
http_remove_header_value(&hdrs[i].v, ist("upgrade"));
|
||||
if (!istlen(hdrs[i].v))
|
||||
hdrs[i].v = IST_NULL;
|
||||
}
|
||||
}
|
||||
h1m->flags &=~ (H1_MF_CONN_UPG|H1_MF_UPG_HDR);
|
||||
}
|
||||
|
||||
flags |= h1m_htx_sl_flags(h1m);
|
||||
|
||||
sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);
|
||||
|
|
|
|||
97
src/h3.c
97
src/h3.c
|
|
@ -212,33 +212,8 @@ static ssize_t h3_init_uni_stream(struct h3c *h3c, struct qcs *qcs,
|
|||
break;
|
||||
|
||||
case H3_UNI_S_T_PUSH:
|
||||
if (!conn_is_back(qcs->qcc->conn)) {
|
||||
/* RFC 9114 6.2.2. Push Streams
|
||||
*
|
||||
* Only servers can push; if a server receives a client-initiated push
|
||||
* stream, this MUST be treated as a connection error of type
|
||||
* H3_STREAM_CREATION_ERROR.
|
||||
*/
|
||||
TRACE_ERROR("reject push from client", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
|
||||
qcc_set_error(qcs->qcc, H3_ERR_STREAM_CREATION_ERROR, 1,
|
||||
muxc_tevt_type_proto_err);
|
||||
qcc_report_glitch(qcs->qcc, 1);
|
||||
goto err;
|
||||
}
|
||||
else {
|
||||
/* RFC 9114 4.6. Server Push
|
||||
*
|
||||
* A client MUST treat receipt of a push stream as a connection
|
||||
* error of type H3_ID_ERROR when no MAX_PUSH_ID frame has been sent or
|
||||
* when the stream references a push ID that is greater than the maximum
|
||||
* push ID.
|
||||
*/
|
||||
TRACE_ERROR("reject push from server outside of MAX_PUSH_ID", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
|
||||
qcc_set_error(qcs->qcc, H3_ERR_ID_ERROR, 1,
|
||||
muxc_tevt_type_proto_err);
|
||||
qcc_report_glitch(qcs->qcc, 1);
|
||||
goto err;
|
||||
}
|
||||
/* TODO not supported for the moment */
|
||||
h3s->type = H3S_T_PUSH;
|
||||
break;
|
||||
|
||||
case H3_UNI_S_T_QPACK_DEC:
|
||||
|
|
@ -390,6 +365,7 @@ static int h3_check_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype
|
|||
|
||||
case H3_FT_CANCEL_PUSH:
|
||||
case H3_FT_GOAWAY:
|
||||
case H3_FT_MAX_PUSH_ID:
|
||||
/* RFC 9114 7.2.3. CANCEL_PUSH
|
||||
*
|
||||
* A CANCEL_PUSH frame is sent on the control stream. Receiving a
|
||||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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':
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
22
src/hlua.c
22
src/hlua.c
|
|
@ -2949,20 +2949,20 @@ __LJMP static int hlua_socket_receive_yield(struct lua_State *L, int status, lua
|
|||
|
||||
/* remove final \r\n. */
|
||||
if (nblk == 1) {
|
||||
if (len1 && blk1[len1-1] == '\n') {
|
||||
if (blk1[len1-1] == '\n') {
|
||||
len1--;
|
||||
skip_at_end++;
|
||||
if (len1 && blk1[len1-1] == '\r') {
|
||||
if (blk1[len1-1] == '\r') {
|
||||
len1--;
|
||||
skip_at_end++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (len2 && blk2[len2-1] == '\n') {
|
||||
if (blk2[len2-1] == '\n') {
|
||||
len2--;
|
||||
skip_at_end++;
|
||||
if (len2 && blk2[len2-1] == '\r') {
|
||||
if (blk2[len2-1] == '\r') {
|
||||
len2--;
|
||||
skip_at_end++;
|
||||
}
|
||||
|
|
@ -6709,20 +6709,6 @@ __LJMP static inline int hlua_http_add_hdr(lua_State *L, struct http_msg *msg)
|
|||
size_t value_len;
|
||||
const char *value = MAY_LJMP(luaL_checklstring(L, 3, &value_len));
|
||||
struct htx *htx = htxbuf(&msg->chn->buf);
|
||||
size_t i;
|
||||
|
||||
/* Reject header values containing CR/LF/NUL to prevent HTTP header
|
||||
* injection on HTTP/1 output.
|
||||
*/
|
||||
for (i = 0; i < name_len; i++) {
|
||||
if (name[i] == 0 || name[i] == '\r' || name[i] == '\n')
|
||||
WILL_LJMP(lua_error(L));
|
||||
}
|
||||
|
||||
for (i = 0; i < value_len; i++) {
|
||||
if (value[i] == 0 || value[i] == '\r' || value[i] == '\n')
|
||||
WILL_LJMP(lua_error(L));
|
||||
}
|
||||
|
||||
lua_pushboolean(L, http_add_header(htx, ist2(name, name_len),
|
||||
ist2(value, value_len), 1));
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
48
src/jwe.c
48
src/jwe.c
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
14
src/mux_h2.c
14
src/mux_h2.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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().
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
230
src/tools.c
230
src/tools.c
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue