nghttpx: Validate received request body length against content-length
This commit is contained in:
parent
441f1cc282
commit
62b9e4bb56
|
@ -109,10 +109,10 @@ Downstream::Downstream(Upstream *upstream, int32_t stream_id, int32_t priority)
|
||||||
: request_buf_(upstream ? upstream->get_mcpool() : nullptr),
|
: request_buf_(upstream ? upstream->get_mcpool() : nullptr),
|
||||||
response_buf_(upstream ? upstream->get_mcpool() : nullptr),
|
response_buf_(upstream ? upstream->get_mcpool() : nullptr),
|
||||||
request_bodylen_(0), response_bodylen_(0), response_sent_bodylen_(0),
|
request_bodylen_(0), response_bodylen_(0), response_sent_bodylen_(0),
|
||||||
response_content_length_(-1), upstream_(upstream),
|
request_content_length_(-1), response_content_length_(-1),
|
||||||
request_headers_sum_(0), response_headers_sum_(0), request_datalen_(0),
|
upstream_(upstream), request_headers_sum_(0), response_headers_sum_(0),
|
||||||
response_datalen_(0), stream_id_(stream_id), priority_(priority),
|
request_datalen_(0), response_datalen_(0), stream_id_(stream_id),
|
||||||
downstream_stream_id_(-1),
|
priority_(priority), downstream_stream_id_(-1),
|
||||||
response_rst_stream_error_code_(NGHTTP2_NO_ERROR),
|
response_rst_stream_error_code_(NGHTTP2_NO_ERROR),
|
||||||
request_state_(INITIAL), request_major_(1), request_minor_(1),
|
request_state_(INITIAL), request_major_(1), request_minor_(1),
|
||||||
response_state_(INITIAL), response_http_status_(0), response_major_(1),
|
response_state_(INITIAL), response_http_status_(0), response_major_(1),
|
||||||
|
@ -668,18 +668,38 @@ void Downstream::set_response_content_length(int64_t len) {
|
||||||
response_content_length_ = len;
|
response_content_length_ = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Downstream::validate_request_bodylen() const {
|
||||||
|
if (request_content_length_ == -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request_content_length_ != request_bodylen_) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
DLOG(INFO, this) << "request invalid bodylen: content-length="
|
||||||
|
<< request_content_length_
|
||||||
|
<< ", received=" << request_bodylen_;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Downstream::validate_response_bodylen() const {
|
bool Downstream::validate_response_bodylen() const {
|
||||||
if (!expect_response_body() || response_content_length_ == -1) {
|
if (!expect_response_body() || response_content_length_ == -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (response_content_length_ != response_bodylen_) {
|
||||||
DLOG(INFO, this) << "response invalid bodylen: content-length="
|
if (LOG_ENABLED(INFO)) {
|
||||||
<< response_content_length_
|
DLOG(INFO, this) << "response invalid bodylen: content-length="
|
||||||
<< ", received=" << response_bodylen_;
|
<< response_content_length_
|
||||||
|
<< ", received=" << response_bodylen_;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return response_content_length_ == response_bodylen_;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Downstream::set_priority(int32_t pri) { priority_ = pri; }
|
void Downstream::set_priority(int32_t pri) { priority_ = pri; }
|
||||||
|
@ -705,6 +725,14 @@ void Downstream::inspect_http2_request() {
|
||||||
if (request_method_ == "CONNECT") {
|
if (request_method_ == "CONNECT") {
|
||||||
upgrade_request_ = true;
|
upgrade_request_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto idx = request_hdidx_[http2::HD_CONTENT_LENGTH];
|
||||||
|
if (idx != -1) {
|
||||||
|
auto len = util::parse_uint(request_headers_[idx].value);
|
||||||
|
if (len != -1) {
|
||||||
|
request_content_length_ = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Downstream::inspect_http1_request() {
|
void Downstream::inspect_http1_request() {
|
||||||
|
|
|
@ -158,6 +158,9 @@ public:
|
||||||
size_t get_request_datalen() const;
|
size_t get_request_datalen() const;
|
||||||
void dec_request_datalen(size_t len);
|
void dec_request_datalen(size_t len);
|
||||||
void reset_request_datalen();
|
void reset_request_datalen();
|
||||||
|
// Validates that received request body length and content-length
|
||||||
|
// matches.
|
||||||
|
bool validate_request_bodylen() const;
|
||||||
bool request_pseudo_header_allowed(int token) const;
|
bool request_pseudo_header_allowed(int token) const;
|
||||||
bool expect_response_body() const;
|
bool expect_response_body() const;
|
||||||
enum {
|
enum {
|
||||||
|
@ -309,6 +312,8 @@ private:
|
||||||
// the length of response body sent to upstream client
|
// the length of response body sent to upstream client
|
||||||
int64_t response_sent_bodylen_;
|
int64_t response_sent_bodylen_;
|
||||||
|
|
||||||
|
// content-length of request body, -1 if it is unknown.
|
||||||
|
int64_t request_content_length_;
|
||||||
// content-length of response body, -1 if it is unknown.
|
// content-length of response body, -1 if it is unknown.
|
||||||
int64_t response_content_length_;
|
int64_t response_content_length_;
|
||||||
|
|
||||||
|
|
|
@ -302,6 +302,11 @@ int on_request_headers(Http2Upstream *upstream, Downstream *downstream,
|
||||||
|
|
||||||
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
|
if (!downstream->validate_request_bodylen()) {
|
||||||
|
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
downstream->disable_upstream_rtimer();
|
downstream->disable_upstream_rtimer();
|
||||||
|
|
||||||
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
||||||
|
@ -381,6 +386,11 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
downstream->disable_upstream_rtimer();
|
downstream->disable_upstream_rtimer();
|
||||||
|
|
||||||
|
if (!downstream->validate_request_bodylen()) {
|
||||||
|
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
downstream->end_upload_data();
|
downstream->end_upload_data();
|
||||||
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
||||||
}
|
}
|
||||||
|
@ -400,6 +410,11 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
|
if (!downstream->validate_request_bodylen()) {
|
||||||
|
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
downstream->disable_upstream_rtimer();
|
downstream->disable_upstream_rtimer();
|
||||||
|
|
||||||
downstream->end_upload_data();
|
downstream->end_upload_data();
|
||||||
|
|
|
@ -204,6 +204,11 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
|
||||||
|
|
||||||
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
||||||
if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {
|
if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {
|
||||||
|
if (!downstream->validate_request_bodylen()) {
|
||||||
|
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
downstream->disable_upstream_rtimer();
|
downstream->disable_upstream_rtimer();
|
||||||
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
||||||
}
|
}
|
||||||
|
@ -321,6 +326,11 @@ void on_data_recv_callback(spdylay_session *session, uint8_t flags,
|
||||||
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
||||||
auto downstream = upstream->find_downstream(stream_id);
|
auto downstream = upstream->find_downstream(stream_id);
|
||||||
if (downstream && (flags & SPDYLAY_DATA_FLAG_FIN)) {
|
if (downstream && (flags & SPDYLAY_DATA_FLAG_FIN)) {
|
||||||
|
if (!downstream->validate_request_bodylen()) {
|
||||||
|
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
downstream->disable_upstream_rtimer();
|
downstream->disable_upstream_rtimer();
|
||||||
downstream->end_upload_data();
|
downstream->end_upload_data();
|
||||||
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
||||||
|
|
Loading…
Reference in New Issue