mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-26 07:30:34 +00:00
MINOR: mux-h2: add a counter of "glitches" on a connection
There are a lot of H2 events which are not invalid from a protocol perspective but which are yet anomalies, especially when repeated. They can come from bogus or really poorly implemlented clients, as well as purposely built attacks, as we've seen in the past with various waves of attempts at abusing H2 stacks. In order to better deal with such situations, it would be nice to be able to sort out what is correct and what is not. There's already the HTTP error counter that may even be updated on a tracked connection, but HTTP errors are something clearly defined while there's an entire scope of gray area around it that should not fall into it. This patch introduces the notion of "glitches", which normally correspond to unexpected and temporary malfunction. And this is exactly what we'd like to monitor. For example a peer is not misbehaving if a request it sends fails to decode because due to HPACK compression it's larger than a buffer, and for this reason such an event is reported as a stream error and not a connection error. But this causes trouble nonetheless and should be accounted for, especially to detect if it's repeated. Similarly, a truncated preamble or settings frame may very well be caused by a network hiccup but how do we know that in the logs? For such events, a glitch counter is incremented on the connection. For now a total of 41 locations were instrumented with this and the counter is reported in the traces when not null, as well as in "show sess" and "show fd". This was done using a new function, "h2c_report_glitch()" so that it becomes easier to extend to more advanced processing (applying thresholds, producing logs, escalating to connection error, tracking etc). A test with h2spec shows it reported in 8545 trace lines for 147 tests, with some reaching value 3 in a same test (e.g. HPACK errors). Some places were not instrumented, typically anything that can be triggered on perfectly valid activity (received data after RST being emitted, timeouts, etc). Some types of events were thought about, such as INITIAL_WINDOW_SIZE after the first SETTINGS frame, too small window update increments, etc. It just sounds too early to know if those are currently being triggered by perfectly legit clients. Also it's currently not incremented on timeouts so that we don't do that repeatedly on short keep-alive timeouts, though it could make sense. This may change in the future depending on how it's used. For now this is not exposed outside of traces and debugging.
This commit is contained in:
parent
87b74697cd
commit
3d4438484a
79
src/mux_h2.c
79
src/mux_h2.c
@ -78,11 +78,13 @@ struct h2c {
|
||||
int timeout; /* idle timeout duration in ticks */
|
||||
int shut_timeout; /* idle timeout duration in ticks after GOAWAY was sent */
|
||||
int idle_start; /* date of the last time the connection went idle (no stream + empty mbuf), or the start of current http req */
|
||||
/* 32-bit hole here */
|
||||
|
||||
unsigned int nb_streams; /* number of streams in the tree */
|
||||
unsigned int nb_sc; /* number of attached stream connectors */
|
||||
unsigned int nb_reserved; /* number of reserved streams */
|
||||
unsigned int stream_cnt; /* total number of streams seen */
|
||||
int glitches; /* total number of glitches on this connection */
|
||||
|
||||
struct proxy *proxy; /* the proxy this connection was created for */
|
||||
struct task *task; /* timeout management task */
|
||||
struct h2_counters *px_counters; /* h2 counters attached to proxy */
|
||||
@ -510,6 +512,9 @@ static void h2_trace(enum trace_level level, uint64_t mask, const struct trace_s
|
||||
if (h2c->errcode)
|
||||
chunk_appendf(&trace_buf, " err=%s/%02x", h2_err_str(h2c->errcode), h2c->errcode);
|
||||
|
||||
if (h2c->glitches)
|
||||
chunk_appendf(&trace_buf, " glitches=%d", h2c->glitches);
|
||||
|
||||
if (h2c->flags & H2_CF_DEM_IN_PROGRESS && // frame processing has started, type and length are valid
|
||||
(mask & (H2_EV_RX_FRAME|H2_EV_RX_FHDR)) == (H2_EV_RX_FRAME|H2_EV_RX_FHDR)) {
|
||||
chunk_appendf(&trace_buf, " dft=%s/%02x dfl=%d", h2_ft_str(h2c->dft), h2c->dff, h2c->dfl);
|
||||
@ -599,6 +604,14 @@ static inline int h2c_max_concurrent_streams(const struct h2c *h2c)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* report a glitch on the connection. That is any unexpected event that may
|
||||
* occasionally happen but if repeated a bit too much, might indicate a
|
||||
* misbehaving or completely bogus peer.
|
||||
*/
|
||||
static inline void h2c_report_glitch(struct h2c *h2c)
|
||||
{
|
||||
h2c->glitches++;
|
||||
}
|
||||
|
||||
/* update h2c timeout if needed */
|
||||
static void h2c_update_timeout(struct h2c *h2c)
|
||||
@ -1046,6 +1059,7 @@ static int h2_init(struct connection *conn, struct proxy *prx, struct session *s
|
||||
h2c->nb_sc = 0;
|
||||
h2c->nb_reserved = 0;
|
||||
h2c->stream_cnt = 0;
|
||||
h2c->glitches = 0;
|
||||
|
||||
h2c->dbuf = *input;
|
||||
h2c->dsi = -1;
|
||||
@ -1586,6 +1600,7 @@ static struct h2s *h2c_frt_stream_new(struct h2c *h2c, int id, struct buffer *in
|
||||
BUG_ON(conn_reverse_in_preconnect(h2c->conn));
|
||||
|
||||
if (h2c->nb_streams >= h2c_max_concurrent_streams(h2c)) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("HEADERS frame causing MAX_CONCURRENT_STREAMS to be exceeded", H2_EV_H2S_NEW|H2_EV_RX_FRAME|H2_EV_RX_HDR, h2c->conn);
|
||||
session_inc_http_req_ctr(sess);
|
||||
session_inc_http_err_ctr(sess);
|
||||
@ -1829,6 +1844,7 @@ static int h2c_frt_recv_preface(struct h2c *h2c)
|
||||
if (!ret1)
|
||||
h2c->flags |= H2_CF_DEM_SHORT_READ;
|
||||
if (ret1 < 0 || (h2c->flags & H2_CF_RCVD_SHUT)) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("I/O error or short read", H2_EV_RX_FRAME|H2_EV_RX_PREFACE, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
|
||||
if (b_data(&h2c->dbuf) ||
|
||||
@ -2292,12 +2308,19 @@ static int h2c_handle_settings(struct h2c *h2c)
|
||||
*/
|
||||
if (arg < 0) { // RFC7540#6.5.2
|
||||
error = H2_ERR_FLOW_CONTROL_ERROR;
|
||||
h2c_report_glitch(h2c);
|
||||
goto fail;
|
||||
}
|
||||
/* WT: maybe we should count a glitch here in case of a
|
||||
* change after H2_CS_SETTINGS1 because while it's not
|
||||
* fundamentally invalid from a protocol's perspective,
|
||||
* it's often suspicious.
|
||||
*/
|
||||
h2c->miw = arg;
|
||||
break;
|
||||
case H2_SETTINGS_MAX_FRAME_SIZE:
|
||||
if (arg < 16384 || arg > 16777215) { // RFC7540#6.5.2
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("MAX_FRAME_SIZE out of range", H2_EV_RX_FRAME|H2_EV_RX_SETTINGS, h2c->conn);
|
||||
error = H2_ERR_PROTOCOL_ERROR;
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
@ -2310,6 +2333,7 @@ static int h2c_handle_settings(struct h2c *h2c)
|
||||
break;
|
||||
case H2_SETTINGS_ENABLE_PUSH:
|
||||
if (arg < 0 || arg > 1) { // RFC7540#6.5.2
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("ENABLE_PUSH out of range", H2_EV_RX_FRAME|H2_EV_RX_SETTINGS, h2c->conn);
|
||||
error = H2_ERR_PROTOCOL_ERROR;
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
@ -2572,13 +2596,22 @@ static int h2c_handle_window_update(struct h2c *h2c, struct h2s *h2s)
|
||||
goto done;
|
||||
|
||||
if (!inc) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("stream WINDOW_UPDATE inc=0", H2_EV_RX_FRAME|H2_EV_RX_WU, h2c->conn, h2s);
|
||||
error = H2_ERR_PROTOCOL_ERROR;
|
||||
HA_ATOMIC_INC(&h2c->px_counters->strm_proto_err);
|
||||
goto strm_err;
|
||||
}
|
||||
|
||||
/* WT: it would be tempting to count a glitch here for very small
|
||||
* increments (less than a few tens of bytes), but that might be
|
||||
* perfectly valid for many short streams, so better instead
|
||||
* count the number of WU per frame maybe. That would be better
|
||||
* dealt with using scores per frame.
|
||||
*/
|
||||
|
||||
if (h2s_mws(h2s) >= 0 && h2s_mws(h2s) + inc < 0) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("stream WINDOW_UPDATE inc<0", H2_EV_RX_FRAME|H2_EV_RX_WU, h2c->conn, h2s);
|
||||
error = H2_ERR_FLOW_CONTROL_ERROR;
|
||||
HA_ATOMIC_INC(&h2c->px_counters->strm_proto_err);
|
||||
@ -2597,6 +2630,7 @@ static int h2c_handle_window_update(struct h2c *h2c, struct h2s *h2s)
|
||||
else {
|
||||
/* connection window update */
|
||||
if (!inc) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("conn WINDOW_UPDATE inc=0", H2_EV_RX_FRAME|H2_EV_RX_WU, h2c->conn);
|
||||
error = H2_ERR_PROTOCOL_ERROR;
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
@ -2604,6 +2638,7 @@ static int h2c_handle_window_update(struct h2c *h2c, struct h2s *h2s)
|
||||
}
|
||||
|
||||
if (h2c->mws >= 0 && h2c->mws + inc < 0) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("conn WINDOW_UPDATE inc<0", H2_EV_RX_FRAME|H2_EV_RX_WU, h2c->conn);
|
||||
error = H2_ERR_FLOW_CONTROL_ERROR;
|
||||
goto conn_err;
|
||||
@ -2673,6 +2708,7 @@ static int h2c_handle_priority(struct h2c *h2c)
|
||||
|
||||
if (h2_get_n32(&h2c->dbuf, 0) == h2c->dsi) {
|
||||
/* 7540#5.3 : can't depend on itself */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("PRIORITY depends on itself", H2_EV_RX_FRAME|H2_EV_RX_WU, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
@ -2786,6 +2822,7 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s)
|
||||
else if (h2c->dsi <= h2c->max_id || !(h2c->dsi & 1)) {
|
||||
/* RFC7540#5.1.1 stream id > prev ones, and must be odd here */
|
||||
error = H2_ERR_PROTOCOL_ERROR;
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("HEADERS on invalid stream ID", H2_EV_RX_FRAME|H2_EV_RX_HDR, h2c->conn);
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
sess_log(h2c->conn->owner);
|
||||
@ -2803,6 +2840,7 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s)
|
||||
* stop processing its requests for real.
|
||||
*/
|
||||
error = H2_ERR_ENHANCE_YOUR_CALM;
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_STATE("Stream limit violated", H2_EV_STRM_SHUT, h2c->conn);
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
sess_log(h2c->conn->owner);
|
||||
@ -2971,6 +3009,7 @@ static struct h2s *h2c_bck_handle_headers(struct h2c *h2c, struct h2s *h2s)
|
||||
|
||||
if (h2s->st != H2_SS_OPEN && h2s->st != H2_SS_HLOC) {
|
||||
/* RFC7540#5.1 */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("response HEADERS in invalid state", H2_EV_RX_FRAME|H2_EV_RX_HDR, h2c->conn, h2s);
|
||||
h2s_error(h2s, H2_ERR_STREAM_CLOSED);
|
||||
h2c->st0 = H2_CS_FRAME_E;
|
||||
@ -3066,6 +3105,7 @@ static int h2c_handle_data(struct h2c *h2c, struct h2s *h2s)
|
||||
|
||||
if (!(h2s->flags & H2_SF_HEADERS_RCVD)) {
|
||||
/* RFC9113#8.1: The header section must be received before the message content */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("Unexpected DATA frame before the message headers", H2_EV_RX_FRAME|H2_EV_RX_DATA, h2c->conn, h2s);
|
||||
error = H2_ERR_PROTOCOL_ERROR;
|
||||
HA_ATOMIC_INC(&h2c->px_counters->strm_proto_err);
|
||||
@ -3073,6 +3113,7 @@ static int h2c_handle_data(struct h2c *h2c, struct h2s *h2s)
|
||||
}
|
||||
if ((h2s->flags & H2_SF_DATA_CLEN) && (h2c->dfl - h2c->dpl) > h2s->body_len) {
|
||||
/* RFC7540#8.1.2 */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("DATA frame larger than content-length", H2_EV_RX_FRAME|H2_EV_RX_DATA, h2c->conn, h2s);
|
||||
error = H2_ERR_PROTOCOL_ERROR;
|
||||
HA_ATOMIC_INC(&h2c->px_counters->strm_proto_err);
|
||||
@ -3131,6 +3172,7 @@ static int h2c_handle_data(struct h2c *h2c, struct h2s *h2s)
|
||||
|
||||
if (h2s->flags & H2_SF_DATA_CLEN && h2s->body_len) {
|
||||
/* RFC7540#8.1.2 */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("ES on DATA frame before content-length", H2_EV_RX_FRAME|H2_EV_RX_DATA, h2c->conn, h2s);
|
||||
error = H2_ERR_PROTOCOL_ERROR;
|
||||
HA_ATOMIC_INC(&h2c->px_counters->strm_proto_err);
|
||||
@ -3173,6 +3215,7 @@ static int h2_frame_check_vs_state(struct h2c *h2c, struct h2s *h2s)
|
||||
/* RFC7540#5.1: any frame other than HEADERS or PRIORITY in
|
||||
* this state MUST be treated as a connection error
|
||||
*/
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("invalid frame type for IDLE state", H2_EV_RX_FRAME|H2_EV_RX_FHDR, h2c->conn, h2s);
|
||||
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
|
||||
if (!h2c->nb_streams && !(h2c->flags & H2_CF_IS_BACK)) {
|
||||
@ -3186,6 +3229,7 @@ static int h2_frame_check_vs_state(struct h2c *h2c, struct h2s *h2s)
|
||||
|
||||
if (h2s->st == H2_SS_IDLE && (h2c->flags & H2_CF_IS_BACK)) {
|
||||
/* only PUSH_PROMISE would be permitted here */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("invalid frame type for IDLE state (back)", H2_EV_RX_FRAME|H2_EV_RX_FHDR, h2c->conn, h2s);
|
||||
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
@ -3201,11 +3245,13 @@ static int h2_frame_check_vs_state(struct h2c *h2c, struct h2s *h2s)
|
||||
* PUSH_PROMISE/CONTINUATION cause connection errors.
|
||||
*/
|
||||
if (h2_ft_bit(h2c->dft) & H2_FT_HDR_MASK) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("invalid frame type for HREM state", H2_EV_RX_FRAME|H2_EV_RX_FHDR, h2c->conn, h2s);
|
||||
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
}
|
||||
else {
|
||||
h2c_report_glitch(h2c);
|
||||
h2s_error(h2s, H2_ERR_STREAM_CLOSED);
|
||||
}
|
||||
TRACE_DEVEL("leaving in error (hrem&!wu&!rst&!prio)", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_PROTO_ERR, h2c->conn, h2s);
|
||||
@ -3235,6 +3281,7 @@ static int h2_frame_check_vs_state(struct h2c *h2c, struct h2s *h2s)
|
||||
* receives an unexpected stream identifier
|
||||
* MUST respond with a connection error.
|
||||
*/
|
||||
h2c_report_glitch(h2c);
|
||||
h2c_error(h2c, H2_ERR_STREAM_CLOSED);
|
||||
TRACE_DEVEL("leaving in error (closed&hdrmask)", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_PROTO_ERR, h2c->conn, h2s);
|
||||
return 0;
|
||||
@ -3261,6 +3308,7 @@ static int h2_frame_check_vs_state(struct h2c *h2c, struct h2s *h2s)
|
||||
* RST_RCVD bit, we don't want to accidentally catch valid
|
||||
* frames for a closed stream, i.e. RST/PRIO/WU.
|
||||
*/
|
||||
h2c_report_glitch(h2c);
|
||||
h2s_error(h2s, H2_ERR_STREAM_CLOSED);
|
||||
h2c->st0 = H2_CS_FRAME_E;
|
||||
TRACE_DEVEL("leaving in error (rst_rcvd&!hdrmask)", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_PROTO_ERR, h2c->conn, h2s);
|
||||
@ -3285,6 +3333,7 @@ static int h2_frame_check_vs_state(struct h2c *h2c, struct h2s *h2s)
|
||||
if (h2c->dft != H2_FT_RST_STREAM &&
|
||||
h2c->dft != H2_FT_PRIORITY &&
|
||||
h2c->dft != H2_FT_WINDOW_UPDATE) {
|
||||
h2c_report_glitch(h2c);
|
||||
h2c_error(h2c, H2_ERR_STREAM_CLOSED);
|
||||
TRACE_DEVEL("leaving in error (rst_sent&!rst&!prio&!wu)", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_PROTO_ERR, h2c->conn, h2s);
|
||||
return 0;
|
||||
@ -3420,6 +3469,10 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
if (unlikely(h2c_frt_recv_preface(h2c) <= 0)) {
|
||||
/* RFC7540#3.5: a GOAWAY frame MAY be omitted */
|
||||
if (h2c->st0 == H2_CS_ERROR) {
|
||||
if (b_data(&h2c->dbuf) ||
|
||||
!(((const struct session *)h2c->conn->owner)->fe->options & (PR_O_NULLNOLOG|PR_O_IGNORE_PRB)))
|
||||
h2c_report_glitch(h2c);
|
||||
|
||||
TRACE_PROTO("failed to receive preface", H2_EV_RX_PREFACE|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c->st0 = H2_CS_ERROR2;
|
||||
if (b_data(&h2c->dbuf) ||
|
||||
@ -3444,6 +3497,7 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
/* RFC7540#3.5: a GOAWAY frame MAY be omitted */
|
||||
h2c->flags |= H2_CF_DEM_SHORT_READ;
|
||||
if (h2c->st0 == H2_CS_ERROR) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("failed to receive settings", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_RX_SETTINGS|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c->st0 = H2_CS_ERROR2;
|
||||
if (!(h2c->flags & H2_CF_IS_BACK))
|
||||
@ -3454,6 +3508,7 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
|
||||
if (hdr.sid || hdr.ft != H2_FT_SETTINGS || hdr.ff & H2_F_SETTINGS_ACK) {
|
||||
/* RFC7540#3.5: a GOAWAY frame MAY be omitted */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("unexpected frame type or flags", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_RX_SETTINGS|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
|
||||
h2c->st0 = H2_CS_ERROR2;
|
||||
@ -3465,6 +3520,7 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
|
||||
if ((int)hdr.len < 0 || (int)hdr.len > global.tune.bufsize) {
|
||||
/* RFC7540#3.5: a GOAWAY frame MAY be omitted */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("invalid settings frame length", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_RX_SETTINGS|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
|
||||
h2c->st0 = H2_CS_ERROR2;
|
||||
@ -3506,6 +3562,7 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
}
|
||||
|
||||
if ((int)hdr.len < 0 || (int)hdr.len > global.tune.bufsize) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("invalid H2 frame length", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
|
||||
if (!h2c->nb_streams && !(h2c->flags & H2_CF_IS_BACK)) {
|
||||
@ -3536,6 +3593,7 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
* padlen in the flow control, so it must be adjusted.
|
||||
*/
|
||||
if (hdr.len < 1) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("invalid H2 padded frame length", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
|
||||
if (!(h2c->flags & H2_CF_IS_BACK))
|
||||
@ -3553,6 +3611,7 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
padlen = *(uint8_t *)b_peek(&h2c->dbuf, 9);
|
||||
|
||||
if (padlen > hdr.len) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("invalid H2 padding length", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
/* RFC7540#6.1 : pad length = length of
|
||||
* frame payload or greater => error.
|
||||
@ -3585,6 +3644,7 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
/* check for minimum basic frame format validity */
|
||||
ret = h2_frame_check(h2c->dft, 1, h2c->dsi, h2c->dfl, global.tune.bufsize);
|
||||
if (ret != H2_ERR_NO_ERROR) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("received invalid H2 frame header", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c_error(h2c, ret);
|
||||
if (!(h2c->flags & H2_CF_IS_BACK))
|
||||
@ -3673,6 +3733,7 @@ static void h2_process_demux(struct h2c *h2c)
|
||||
* frames' parsers consume all following CONTINUATION
|
||||
* frames so this one is out of sequence.
|
||||
*/
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_ERROR("received unexpected H2 CONTINUATION frame", H2_EV_RX_FRAME|H2_EV_RX_CONT|H2_EV_H2C_ERR, h2c->conn, h2s);
|
||||
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
|
||||
if (!(h2c->flags & H2_CF_IS_BACK))
|
||||
@ -4998,7 +5059,8 @@ static void h2_shutw(struct stconn *sc, enum co_shw_mode mode)
|
||||
*
|
||||
* The H2_SF_HEADERS_RCVD flag is also looked at in the <flags> field prior to
|
||||
* decoding, in order to detect if we're dealing with a headers or a trailers
|
||||
* block (the trailers block appears after H2_SF_HEADERS_RCVD was seen).
|
||||
* block (the trailers block appears after H2_SF_HEADERS_RCVD was seen). The
|
||||
* function takes care of counting glitches.
|
||||
*/
|
||||
static int h2c_dec_hdrs(struct h2c *h2c, struct buffer *rxbuf, uint32_t *flags, unsigned long long *body_len, char *upgrade_protocol)
|
||||
{
|
||||
@ -5042,6 +5104,7 @@ next_frame:
|
||||
|
||||
if (hdr.ft != H2_FT_CONTINUATION) {
|
||||
/* RFC7540#6.10: frame of unexpected type */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_STATE("not continuation!", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_RX_HDR|H2_EV_RX_CONT|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
@ -5050,6 +5113,7 @@ next_frame:
|
||||
|
||||
if (hdr.sid != h2c->dsi) {
|
||||
/* RFC7540#6.10: frame of different stream */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_STATE("different stream ID!", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_RX_HDR|H2_EV_RX_CONT|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
@ -5058,6 +5122,7 @@ next_frame:
|
||||
|
||||
if ((unsigned)hdr.len > (unsigned)global.tune.bufsize) {
|
||||
/* RFC7540#4.2: invalid frame length */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_STATE("too large frame!", H2_EV_RX_FRAME|H2_EV_RX_FHDR|H2_EV_RX_HDR|H2_EV_RX_CONT|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_FRAME_SIZE_ERROR);
|
||||
goto fail;
|
||||
@ -5103,6 +5168,7 @@ next_frame:
|
||||
if (h2c->dff & H2_F_HEADERS_PRIORITY) {
|
||||
if (read_n32(hdrs) == h2c->dsi) {
|
||||
/* RFC7540#5.3.1 : stream dep may not depend on itself */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_STATE("invalid stream dependency!", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
@ -5110,6 +5176,7 @@ next_frame:
|
||||
}
|
||||
|
||||
if (flen < 5) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_STATE("frame too short for priority!", 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;
|
||||
@ -5161,6 +5228,7 @@ next_frame:
|
||||
}
|
||||
|
||||
if (outlen < 0) {
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_STATE("failed to decompress HPACK", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_COMPRESSION_ERROR);
|
||||
goto fail;
|
||||
@ -5193,6 +5261,7 @@ next_frame:
|
||||
|
||||
if (outlen < 0 || htx_free_space(htx) < global.tune.maxrewrite) {
|
||||
/* too large headers? this is a stream error only */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_STATE("message headers too large or invalid", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_H2S_ERR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
htx->flags |= HTX_FL_PARSING_ERROR;
|
||||
goto fail;
|
||||
@ -5228,6 +5297,7 @@ next_frame:
|
||||
if (h2c->dff & H2_F_HEADERS_END_STREAM) {
|
||||
if (msgf & H2_MSGF_RSP_1XX) {
|
||||
/* RFC9113#8.1 : HEADERS frame with the ES flag set that carries an informational status code is malformed */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_STATE("invalid interim response with ES flag!", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
goto fail;
|
||||
}
|
||||
@ -5273,6 +5343,7 @@ next_frame:
|
||||
/* This is the last HEADERS frame hence a trailer */
|
||||
if (!(h2c->dff & H2_F_HEADERS_END_STREAM)) {
|
||||
/* It's a trailer but it's missing ES flag */
|
||||
h2c_report_glitch(h2c);
|
||||
TRACE_STATE("missing EH on trailers frame", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_H2C_ERR|H2_EV_PROTO_ERR, h2c->conn);
|
||||
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
|
||||
HA_ATOMIC_INC(&h2c->px_counters->conn_proto_err);
|
||||
@ -7248,9 +7319,9 @@ static int h2_dump_h2c_info(struct buffer *msg, struct h2c *h2c, const char *pfx
|
||||
hmbuf = br_head(h2c->mbuf);
|
||||
tmbuf = br_tail(h2c->mbuf);
|
||||
chunk_appendf(msg, " h2c.st0=%s .err=%d .maxid=%d .lastid=%d .flg=0x%04x"
|
||||
" .nbst=%u .nbsc=%u",
|
||||
" .nbst=%u .nbsc=%u, .glitches=%d",
|
||||
h2c_st_to_str(h2c->st0), h2c->errcode, h2c->max_id, h2c->last_sid, h2c->flags,
|
||||
h2c->nb_streams, h2c->nb_sc);
|
||||
h2c->nb_streams, h2c->nb_sc, h2c->glitches);
|
||||
|
||||
if (pfx)
|
||||
chunk_appendf(msg, "\n%s", pfx);
|
||||
|
Loading…
Reference in New Issue
Block a user