diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 2d95796..cf7aad7 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -275,6 +275,7 @@ ngx_http_v2_init(ngx_event_t *rev) h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module); h2c->concurrent_pushes = h2scf->concurrent_pushes; + h2c->priority_limit = h2scf->concurrent_streams; h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log); if (h2c->pool == NULL) { @@ -1548,6 +1549,14 @@ ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos, header->name.len = h2c->state.field_end - h2c->state.field_start; header->name.data = h2c->state.field_start; + if (header->name.len == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent zero header name length"); + + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_PROTOCOL_ERROR); + } + return ngx_http_v2_state_field_len(h2c, pos, end); } @@ -1798,6 +1807,13 @@ ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, u_char *pos, return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } + if (--h2c->priority_limit == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent too many PRIORITY frames"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM); + } + if (end - pos < NGX_HTTP_V2_PRIORITY_SIZE) { return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_priority); @@ -3122,6 +3138,8 @@ ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t push) h2c->processing++; } + h2c->priority_limit += h2scf->concurrent_streams; + return stream; } @@ -3259,10 +3277,6 @@ ngx_http_v2_validate_header(ngx_http_request_t *r, ngx_http_v2_header_t *header) ngx_uint_t i; ngx_http_core_srv_conf_t *cscf; - if (header->name.len == 0) { - return NGX_ERROR; - } - r->invalid_header = 0; cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); @@ -4375,6 +4389,8 @@ ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc) */ pool = stream->pool; + h2c->frames -= stream->frames; + ngx_http_free_request(stream->request, rc); if (pool != h2c->state.pool) { diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h index 09b2c1d..7d36173 100644 --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -170,6 +170,7 @@ struct ngx_http_v2_connection_s { ngx_uint_t processing; ngx_uint_t frames; ngx_uint_t idle; + ngx_uint_t priority_limit; ngx_uint_t pushing; ngx_uint_t concurrent_pushes; @@ -247,6 +248,8 @@ struct ngx_http_v2_stream_s { ngx_buf_t *preread; + ngx_uint_t frames; + ngx_http_v2_out_frame_t *free_frames; ngx_chain_t *free_frame_headers; ngx_chain_t *free_bufs; diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c index 6ba234e..968254e 100644 --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -1645,22 +1645,34 @@ static ngx_http_v2_out_frame_t * ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream, size_t len, ngx_chain_t *first, ngx_chain_t *last) { - u_char flags; - ngx_buf_t *buf; - ngx_chain_t *cl; - ngx_http_v2_out_frame_t *frame; + u_char flags; + ngx_buf_t *buf; + ngx_chain_t *cl; + ngx_http_v2_out_frame_t *frame; + ngx_http_v2_connection_t *h2c; frame = stream->free_frames; + h2c = stream->connection; if (frame) { stream->free_frames = frame->next; - } else { + } else if (h2c->frames < 10000) { frame = ngx_palloc(stream->request->pool, sizeof(ngx_http_v2_out_frame_t)); if (frame == NULL) { return NULL; } + + stream->frames++; + h2c->frames++; + + } else { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "http2 flood detected"); + + h2c->connection->error = 1; + return NULL; } flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0;