Compare commits

...

8 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa 5563a0bea6 Update man pages 2016-02-11 23:13:41 +09:00
Tatsuhiro Tsujikawa 5952f071da Bump up version number to 1.7.1 2016-02-11 23:10:34 +09:00
Tatsuhiro Tsujikawa 5a7486164f Fix configure script for non-gcc, clang build 2016-02-11 23:09:11 +09:00
Tatsuhiro Tsujikawa c8395edfbe asio: client: Limit incoming response header field buffer size 2016-02-10 22:36:01 +09:00
Tatsuhiro Tsujikawa 3bff503a16 Add warning 2016-02-04 23:38:46 +09:00
Tatsuhiro Tsujikawa 7a0ab83107 nghttp: Limit incoming header field buffer 2016-02-04 23:38:46 +09:00
Tatsuhiro Tsujikawa 026919b7ea asio: server: Limit incoming request header field buffer size 2016-02-04 23:38:46 +09:00
Tatsuhiro Tsujikawa 64d7288428 nghttpd: Limit request header buffer 2016-02-04 23:38:46 +09:00
18 changed files with 104 additions and 13 deletions

View File

@ -25,7 +25,7 @@ dnl Do not change user variables!
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61) AC_PREREQ(2.61)
AC_INIT([nghttp2], [1.7.0], [t-tujikawa@users.sourceforge.net]) AC_INIT([nghttp2], [1.7.1], [t-tujikawa@users.sourceforge.net])
AC_CONFIG_AUX_DIR([.]) AC_CONFIG_AUX_DIR([.])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
@ -185,8 +185,8 @@ if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then
AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used]) AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used])
AC_DEFINE([NGHTTP2_NORETURN], [__attribute__((noreturn))], [Hint to the compiler that a function never return]) AC_DEFINE([NGHTTP2_NORETURN], [__attribute__((noreturn))], [Hint to the compiler that a function never return])
else else
AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not use AC_DEFINE([NGHTTP2_NORETURN], , [Hint to the compiler that a function never return]) AC_DEFINE([_U_], , [Hint to the compiler that a function parameter is not used])
d]) AC_DEFINE([NGHTTP2_NORETURN], , [Hint to the compiler that a function never return])
fi fi
AX_CXX_COMPILE_STDCXX_11([noext], [optional]) AX_CXX_COMPILE_STDCXX_11([noext], [optional])

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "H2LOAD" "1" "January 25, 2016" "1.7.0" "nghttp2" .TH "H2LOAD" "1" "February 11, 2016" "1.7.1" "nghttp2"
.SH NAME .SH NAME
h2load \- HTTP/2 benchmarking tool h2load \- HTTP/2 benchmarking tool
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTP" "1" "January 25, 2016" "1.7.0" "nghttp2" .TH "NGHTTP" "1" "February 11, 2016" "1.7.1" "nghttp2"
.SH NAME .SH NAME
nghttp \- HTTP/2 client nghttp \- HTTP/2 client
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTPD" "1" "January 25, 2016" "1.7.0" "nghttp2" .TH "NGHTTPD" "1" "February 11, 2016" "1.7.1" "nghttp2"
.SH NAME .SH NAME
nghttpd \- HTTP/2 server nghttpd \- HTTP/2 server
. .

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTPX" "1" "January 25, 2016" "1.7.0" "nghttp2" .TH "NGHTTPX" "1" "February 11, 2016" "1.7.1" "nghttp2"
.SH NAME .SH NAME
nghttpx \- HTTP/2 proxy nghttpx \- HTTP/2 proxy
. .

View File

@ -1608,6 +1608,14 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
* *
* To set this callback to :type:`nghttp2_session_callbacks`, use * To set this callback to :type:`nghttp2_session_callbacks`, use
* `nghttp2_session_callbacks_set_on_header_callback()`. * `nghttp2_session_callbacks_set_on_header_callback()`.
*
* .. warning::
*
* Application should properly limit the total buffer size to store
* incoming header fields. Without it, peer may send large number
* of header fields or large header fields to cause out of memory in
* local endpoint. Due to how HPACK works, peer can do this
* effectively without using much memory on their own.
*/ */
typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, typedef int (*nghttp2_on_header_callback)(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame,

View File

@ -425,7 +425,7 @@ void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents) {
Stream::Stream(Http2Handler *handler, int32_t stream_id) Stream::Stream(Http2Handler *handler, int32_t stream_id)
: handler(handler), file_ent(nullptr), body_length(0), body_offset(0), : handler(handler), file_ent(nullptr), body_length(0), body_offset(0),
stream_id(stream_id), echo_upload(false) { header_buffer_size(0), stream_id(stream_id), echo_upload(false) {
auto config = handler->get_config(); auto config = handler->get_config();
ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout); ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout); ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
@ -1316,6 +1316,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return 0; return 0;
} }
if (stream->header_buffer_size + namelen + valuelen > 64_k) {
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
return 0;
}
stream->header_buffer_size += namelen + valuelen;
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
http2::index_header(stream->hdidx, token, stream->headers.size()); http2::index_header(stream->hdidx, token, stream->headers.size());

View File

@ -110,6 +110,9 @@ struct Stream {
ev_timer wtimer; ev_timer wtimer;
int64_t body_length; int64_t body_length;
int64_t body_offset; int64_t body_offset;
// Total amount of bytes (sum of name and value length) used in
// headers.
size_t header_buffer_size;
int32_t stream_id; int32_t stream_id;
http2::HeaderIndex hdidx; http2::HeaderIndex hdidx;
bool echo_upload; bool echo_upload;

View File

@ -32,7 +32,7 @@ namespace nghttp2 {
namespace asio_http2 { namespace asio_http2 {
namespace client { namespace client {
request_impl::request_impl() : strm_(nullptr) {} request_impl::request_impl() : strm_(nullptr), header_buffer_size_(0) {}
void request_impl::write_trailer(header_map h) { void request_impl::write_trailer(header_map h) {
auto sess = strm_->session(); auto sess = strm_->session();
@ -105,6 +105,12 @@ void request_impl::method(std::string s) { method_ = std::move(s); }
const std::string &request_impl::method() const { return method_; } const std::string &request_impl::method() const { return method_; }
size_t request_impl::header_buffer_size() const { return header_buffer_size_; }
void request_impl::update_header_buffer_size(size_t len) {
header_buffer_size_ += len;
}
} // namespace client } // namespace client
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2

View File

@ -75,6 +75,9 @@ public:
void method(std::string s); void method(std::string s);
const std::string &method() const; const std::string &method() const;
size_t header_buffer_size() const;
void update_header_buffer_size(size_t len);
private: private:
header_map header_; header_map header_;
response_cb response_cb_; response_cb response_cb_;
@ -84,6 +87,7 @@ private:
class stream *strm_; class stream *strm_;
uri_ref uri_; uri_ref uri_;
std::string method_; std::string method_;
size_t header_buffer_size_;
}; };
} // namespace client } // namespace client

View File

@ -30,7 +30,8 @@ namespace nghttp2 {
namespace asio_http2 { namespace asio_http2 {
namespace client { namespace client {
response_impl::response_impl() : content_length_(-1), status_code_(0) {} response_impl::response_impl()
: content_length_(-1), header_buffer_size_(0), status_code_(0) {}
void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); } void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); }
@ -52,6 +53,12 @@ header_map &response_impl::header() { return header_; }
const header_map &response_impl::header() const { return header_; } const header_map &response_impl::header() const { return header_; }
size_t response_impl::header_buffer_size() const { return header_buffer_size_; }
void response_impl::update_header_buffer_size(size_t len) {
header_buffer_size_ += len;
}
} // namespace client } // namespace client
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2

View File

@ -53,12 +53,16 @@ public:
header_map &header(); header_map &header();
const header_map &header() const; const header_map &header() const;
size_t header_buffer_size() const;
void update_header_buffer_size(size_t len);
private: private:
data_cb data_cb_; data_cb data_cb_;
header_map header_; header_map header_;
int64_t content_length_; int64_t content_length_;
size_t header_buffer_size_;
int status_code_; int status_code_;
}; };

View File

@ -174,6 +174,12 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (token == http2::HD__STATUS) { if (token == http2::HD__STATUS) {
res.status_code(util::parse_uint(value, valuelen)); res.status_code(util::parse_uint(value, valuelen));
} else { } else {
if (res.header_buffer_size() + namelen + valuelen > 64_k) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR);
break;
}
res.update_header_buffer_size(namelen + valuelen);
if (token == http2::HD_CONTENT_LENGTH) { if (token == http2::HD_CONTENT_LENGTH) {
res.content_length(util::parse_uint(value, valuelen)); res.content_length(util::parse_uint(value, valuelen));
@ -214,6 +220,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
} }
// fall through // fall through
default: default:
if (req.header_buffer_size() + namelen + valuelen > 64_k) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR);
break;
}
req.update_header_buffer_size(namelen + valuelen);
req.header().emplace( req.header().emplace(
std::string(name, name + namelen), std::string(name, name + namelen),
header_value{std::string(value, value + valuelen), header_value{std::string(value, value + valuelen),

View File

@ -105,6 +105,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
} }
// fall through // fall through
default: default:
if (req.header_buffer_size() + namelen + valuelen > 64_k) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
break;
}
req.update_header_buffer_size(namelen + valuelen);
req.header().emplace(std::string(name, name + namelen), req.header().emplace(std::string(name, name + namelen),
header_value{std::string(value, value + valuelen), header_value{std::string(value, value + valuelen),
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0}); (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});

View File

@ -28,7 +28,7 @@ namespace nghttp2 {
namespace asio_http2 { namespace asio_http2 {
namespace server { namespace server {
request_impl::request_impl() : strm_(nullptr) {} request_impl::request_impl() : strm_(nullptr), header_buffer_size_(0) {}
const header_map &request_impl::header() const { return header_; } const header_map &request_impl::header() const { return header_; }
@ -62,6 +62,12 @@ void request_impl::remote_endpoint(boost::asio::ip::tcp::endpoint ep) {
remote_ep_ = std::move(ep); remote_ep_ = std::move(ep);
} }
size_t request_impl::header_buffer_size() const { return header_buffer_size_; }
void request_impl::update_header_buffer_size(size_t len) {
header_buffer_size_ += len;
}
} // namespace server } // namespace server
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2

View File

@ -58,6 +58,9 @@ public:
const boost::asio::ip::tcp::endpoint &remote_endpoint() const; const boost::asio::ip::tcp::endpoint &remote_endpoint() const;
void remote_endpoint(boost::asio::ip::tcp::endpoint ep); void remote_endpoint(boost::asio::ip::tcp::endpoint ep);
size_t header_buffer_size() const;
void update_header_buffer_size(size_t len);
private: private:
class stream *strm_; class stream *strm_;
header_map header_; header_map header_;
@ -65,6 +68,7 @@ private:
uri_ref uri_; uri_ref uri_;
data_cb on_data_cb_; data_cb on_data_cb_;
boost::asio::ip::tcp::endpoint remote_ep_; boost::asio::ip::tcp::endpoint remote_ep_;
size_t header_buffer_size_;
}; };
} // namespace server } // namespace server

View File

@ -135,8 +135,8 @@ Request::Request(const std::string &uri, const http_parser_url &u,
const nghttp2_priority_spec &pri_spec, int level) const nghttp2_priority_spec &pri_spec, int level)
: uri(uri), u(u), pri_spec(pri_spec), data_length(data_length), : uri(uri), u(u), pri_spec(pri_spec), data_length(data_length),
data_offset(0), response_len(0), inflater(nullptr), html_parser(nullptr), data_offset(0), response_len(0), inflater(nullptr), html_parser(nullptr),
data_prd(data_prd), stream_id(-1), status(0), level(level), data_prd(data_prd), header_buffer_size(0), stream_id(-1), status(0),
expect_final_response(false) { level(level), expect_final_response(false) {
http2::init_hdidx(res_hdidx); http2::init_hdidx(res_hdidx);
http2::init_hdidx(req_hdidx); http2::init_hdidx(req_hdidx);
} }
@ -1704,6 +1704,14 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
break; break;
} }
if (req->header_buffer_size + namelen + valuelen > 64_k) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
return 0;
}
req->header_buffer_size += namelen + valuelen;
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
http2::index_header(req->res_hdidx, token, req->res_nva.size()); http2::index_header(req->res_hdidx, token, req->res_nva.size());
@ -1719,6 +1727,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
break; break;
} }
if (req->header_buffer_size + namelen + valuelen > 64_k) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->push_promise.promised_stream_id,
NGHTTP2_INTERNAL_ERROR);
return 0;
}
req->header_buffer_size += namelen + valuelen;
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
http2::index_header(req->req_hdidx, token, req->req_nva.size()); http2::index_header(req->req_hdidx, token, req->req_nva.size());
@ -1806,6 +1823,10 @@ int on_frame_recv_callback2(nghttp2_session *session,
if (!req) { if (!req) {
break; break;
} }
// Reset for response header field reception
req->header_buffer_size = 0;
auto scheme = req->get_req_header(http2::HD__SCHEME); auto scheme = req->get_req_header(http2::HD__SCHEME);
auto authority = req->get_req_header(http2::HD__AUTHORITY); auto authority = req->get_req_header(http2::HD__AUTHORITY);
auto path = req->get_req_header(http2::HD__PATH); auto path = req->get_req_header(http2::HD__PATH);

View File

@ -150,6 +150,7 @@ struct Request {
nghttp2_gzip *inflater; nghttp2_gzip *inflater;
HtmlParser *html_parser; HtmlParser *html_parser;
const nghttp2_data_provider *data_prd; const nghttp2_data_provider *data_prd;
size_t header_buffer_size;
int32_t stream_id; int32_t stream_id;
int status; int status;
// Recursion level: 0: first entity, 1: entity linked from first entity // Recursion level: 0: first entity, 1: entity linked from first entity