Return fatal error if flooding is detected to close session immediately
This change adds new return error code from nghttp2_session_mem_recv and nghttp2_session_recv functions, namely NGHTTP2_ERR_FLOODED. It is fatal error, and is returned when flooding was detected.
This commit is contained in:
parent
0cb8c82125
commit
d22ced77c0
|
@ -399,7 +399,16 @@ typedef enum {
|
||||||
* Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was
|
* Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was
|
||||||
* received and further processing is not possible.
|
* received and further processing is not possible.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903
|
NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903,
|
||||||
|
/**
|
||||||
|
* Possible flooding by peer was detected in this HTTP/2 session.
|
||||||
|
* Flooding is measured by how many PING and SETTINGS frames with
|
||||||
|
* ACK flag set are queued for transmission. These frames are
|
||||||
|
* response for the peer initiated frames, and peer can cause memory
|
||||||
|
* exhaustion on server side to send these frames forever and does
|
||||||
|
* not read network.
|
||||||
|
*/
|
||||||
|
NGHTTP2_ERR_FLOODED = -904
|
||||||
} nghttp2_error;
|
} nghttp2_error;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2366,6 +2375,9 @@ NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session,
|
||||||
* when |session| was configured as server and
|
* when |session| was configured as server and
|
||||||
* `nghttp2_option_set_no_recv_client_magic()` is not used with
|
* `nghttp2_option_set_no_recv_client_magic()` is not used with
|
||||||
* nonzero value.
|
* nonzero value.
|
||||||
|
* :enum:`NGHTTP2_ERR_FLOODED`
|
||||||
|
* Flooding was detected in this HTTP/2 session, and it must be
|
||||||
|
* closed. This is most likely caused by misbehaviour of peer.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
|
NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
|
||||||
|
|
||||||
|
@ -2402,6 +2414,9 @@ NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
|
||||||
* when |session| was configured as server and
|
* when |session| was configured as server and
|
||||||
* `nghttp2_option_set_no_recv_client_magic()` is not used with
|
* `nghttp2_option_set_no_recv_client_magic()` is not used with
|
||||||
* nonzero value.
|
* nonzero value.
|
||||||
|
* :enum:`NGHTTP2_ERR_FLOODED`
|
||||||
|
* Flooding was detected in this HTTP/2 session, and it must be
|
||||||
|
* closed. This is most likely caused by misbehaviour of peer.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
||||||
const uint8_t *in,
|
const uint8_t *in,
|
||||||
|
|
|
@ -294,6 +294,9 @@ const char *nghttp2_strerror(int error_code) {
|
||||||
return "The user callback function failed";
|
return "The user callback function failed";
|
||||||
case NGHTTP2_ERR_BAD_CLIENT_MAGIC:
|
case NGHTTP2_ERR_BAD_CLIENT_MAGIC:
|
||||||
return "Received bad client magic byte string";
|
return "Received bad client magic byte string";
|
||||||
|
case NGHTTP2_ERR_FLOODED:
|
||||||
|
return "Flooding was detected in this HTTP/2 session, and it must be "
|
||||||
|
"closed";
|
||||||
default:
|
default:
|
||||||
return "Unknown error code";
|
return "Unknown error code";
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,8 +52,7 @@ typedef enum {
|
||||||
* Invalid HTTP header field was received but it can be treated as
|
* Invalid HTTP header field was received but it can be treated as
|
||||||
* if it was not received because of compatibility reasons.
|
* if it was not received because of compatibility reasons.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_ERR_IGN_HTTP_HEADER = -105,
|
NGHTTP2_ERR_IGN_HTTP_HEADER = -105
|
||||||
NGHTTP2_ERR_FLOODING_DETECTED = -106
|
|
||||||
} nghttp2_internal_error;
|
} nghttp2_internal_error;
|
||||||
|
|
||||||
#endif /* NGHTTP2_INT_H */
|
#endif /* NGHTTP2_INT_H */
|
||||||
|
|
|
@ -141,15 +141,6 @@ static int session_detect_idle_stream(nghttp2_session *session,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void session_on_flooding_detected(nghttp2_session *session) {
|
|
||||||
/* If we found flooding, we might not send GOAWAY since peer might
|
|
||||||
not read at all. So we just set these flags to pretend that
|
|
||||||
GOAWAY is sent, so that nghttp2_session_want_read() and
|
|
||||||
nghttp2_session_want_write() return 0. */
|
|
||||||
session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND |
|
|
||||||
NGHTTP2_GOAWAY_TERM_SENT | NGHTTP2_GOAWAY_SENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int session_terminate_session(nghttp2_session *session,
|
static int session_terminate_session(nghttp2_session *session,
|
||||||
int32_t last_stream_id,
|
int32_t last_stream_id,
|
||||||
uint32_t error_code, const char *reason) {
|
uint32_t error_code, const char *reason) {
|
||||||
|
@ -5930,8 +5921,7 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
|
||||||
|
|
||||||
if ((flags & NGHTTP2_FLAG_ACK) &&
|
if ((flags & NGHTTP2_FLAG_ACK) &&
|
||||||
session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
|
session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
|
||||||
session_on_flooding_detected(session);
|
return NGHTTP2_ERR_FLOODED;
|
||||||
return NGHTTP2_ERR_FLOODING_DETECTED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
|
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
|
||||||
|
@ -6081,8 +6071,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
|
if (session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
|
||||||
session_on_flooding_detected(session);
|
return NGHTTP2_ERR_FLOODED;
|
||||||
return NGHTTP2_ERR_FLOODING_DETECTED;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -367,7 +367,7 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
||||||
*
|
*
|
||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
* NGHTTP2_ERR_FLOODING_DETECTED
|
* NGHTTP2_ERR_FLOODED
|
||||||
* There are too many items in outbound queue; this only happens
|
* There are too many items in outbound queue; this only happens
|
||||||
* if NGHTTP2_FLAG_ACK is set in |flags|
|
* if NGHTTP2_FLAG_ACK is set in |flags|
|
||||||
*/
|
*/
|
||||||
|
@ -417,7 +417,7 @@ int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
|
||||||
*
|
*
|
||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
* NGHTTP2_ERR_FLOODING_DETECTED
|
* NGHTTP2_ERR_FLOODED
|
||||||
* There are too many items in outbound queue; this only happens
|
* There are too many items in outbound queue; this only happens
|
||||||
* if NGHTTP2_FLAG_ACK is set in |flags|
|
* if NGHTTP2_FLAG_ACK is set in |flags|
|
||||||
*/
|
*/
|
||||||
|
@ -643,6 +643,9 @@ int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
|
||||||
* Out of memory
|
* Out of memory
|
||||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||||
* The read_callback failed
|
* The read_callback failed
|
||||||
|
* NGHTTP2_ERR_FLOODED
|
||||||
|
* There are too many items in outbound queue, and this is most
|
||||||
|
* likely caused by misbehaviour of peer.
|
||||||
*/
|
*/
|
||||||
int nghttp2_session_on_settings_received(nghttp2_session *session,
|
int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||||
nghttp2_frame *frame, int noack);
|
nghttp2_frame *frame, int noack);
|
||||||
|
@ -676,6 +679,9 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
||||||
* Out of memory.
|
* Out of memory.
|
||||||
* NGHTTP2_ERR_CALLBACK_FAILURE
|
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||||
* The callback function failed.
|
* The callback function failed.
|
||||||
|
* NGHTTP2_ERR_FLOODED
|
||||||
|
* There are too many items in outbound queue, and this is most
|
||||||
|
* likely caused by misbehaviour of peer.
|
||||||
*/
|
*/
|
||||||
int nghttp2_session_on_ping_received(nghttp2_session *session,
|
int nghttp2_session_on_ping_received(nghttp2_session *session,
|
||||||
nghttp2_frame *frame);
|
nghttp2_frame *frame);
|
||||||
|
|
|
@ -8097,6 +8097,7 @@ void test_nghttp2_session_flooding(void) {
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
nghttp2_frame frame;
|
nghttp2_frame frame;
|
||||||
nghttp2_mem *mem;
|
nghttp2_mem *mem;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
mem = nghttp2_mem_default();
|
mem = nghttp2_mem_default();
|
||||||
|
|
||||||
|
@ -8113,21 +8114,15 @@ void test_nghttp2_session_flooding(void) {
|
||||||
|
|
||||||
buf = &bufs.head->buf;
|
buf = &bufs.head->buf;
|
||||||
|
|
||||||
for (int i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
|
for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
|
||||||
CU_ASSERT(
|
CU_ASSERT(
|
||||||
(ssize_t)nghttp2_buf_len(buf) ==
|
(ssize_t)nghttp2_buf_len(buf) ==
|
||||||
nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
|
nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
|
||||||
}
|
}
|
||||||
|
|
||||||
CU_ASSERT(1 == nghttp2_session_want_read(session));
|
CU_ASSERT(NGHTTP2_ERR_FLOODED ==
|
||||||
CU_ASSERT(1 == nghttp2_session_want_write(session));
|
|
||||||
|
|
||||||
CU_ASSERT((ssize_t)nghttp2_buf_len(buf) ==
|
|
||||||
nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
|
nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
|
||||||
|
|
||||||
CU_ASSERT(0 == nghttp2_session_want_read(session));
|
|
||||||
CU_ASSERT(0 == nghttp2_session_want_write(session));
|
|
||||||
|
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
/* SETTINGS ACK */
|
/* SETTINGS ACK */
|
||||||
|
@ -8141,21 +8136,15 @@ void test_nghttp2_session_flooding(void) {
|
||||||
|
|
||||||
buf = &bufs.head->buf;
|
buf = &bufs.head->buf;
|
||||||
|
|
||||||
for (int i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
|
for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
|
||||||
CU_ASSERT(
|
CU_ASSERT(
|
||||||
(ssize_t)nghttp2_buf_len(buf) ==
|
(ssize_t)nghttp2_buf_len(buf) ==
|
||||||
nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
|
nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
|
||||||
}
|
}
|
||||||
|
|
||||||
CU_ASSERT(1 == nghttp2_session_want_read(session));
|
CU_ASSERT(NGHTTP2_ERR_FLOODED ==
|
||||||
CU_ASSERT(1 == nghttp2_session_want_write(session));
|
|
||||||
|
|
||||||
CU_ASSERT((ssize_t)nghttp2_buf_len(buf) ==
|
|
||||||
nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
|
nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
|
||||||
|
|
||||||
CU_ASSERT(0 == nghttp2_session_want_read(session));
|
|
||||||
CU_ASSERT(0 == nghttp2_session_want_write(session));
|
|
||||||
|
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
nghttp2_bufs_free(&bufs);
|
nghttp2_bufs_free(&bufs);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue