From 77a951c5da518dda471282635c98f3b572ca15db Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 5 May 2011 16:42:26 -0700 Subject: http_parser: add max_header_len accessor Rainbows! wants to be able to lower this eventually... --- ext/unicorn_http/ext_help.h | 16 +++++++++++++ ext/unicorn_http/extconf.rb | 1 + ext/unicorn_http/global_variables.h | 11 ++++----- ext/unicorn_http/unicorn_http.rl | 48 +++++++++++++++++-------------------- 4 files changed, 44 insertions(+), 32 deletions(-) (limited to 'ext') diff --git a/ext/unicorn_http/ext_help.h b/ext/unicorn_http/ext_help.h index 1f76f54..0968080 100644 --- a/ext/unicorn_http/ext_help.h +++ b/ext/unicorn_http/ext_help.h @@ -36,6 +36,22 @@ static void rb_18_str_set_len(VALUE str, long len) # endif #endif /* ! defined(OFFT2NUM) */ +#if !defined(SIZET2NUM) +# if SIZEOF_SIZE_T == SIZEOF_LONG +# define SIZET2NUM(n) ULONG2NUM(n) +# else +# define SIZET2NUM(n) ULL2NUM(n) +# endif +#endif /* ! defined(SIZET2NUM) */ + +#if !defined(NUM2SIZET) +# if SIZEOF_SIZE_T == SIZEOF_LONG +# define NUM2SIZET(n) ((size_t)NUM2ULONG(n)) +# else +# define NUM2SIZET(n) ((size_t)NUM2ULL(n)) +# endif +#endif /* ! defined(NUM2SIZET) */ + #ifndef HAVE_RB_STR_MODIFY # define rb_str_modify(x) do {} while (0) #endif /* ! defined(HAVE_RB_STR_MODIFY) */ diff --git a/ext/unicorn_http/extconf.rb b/ext/unicorn_http/extconf.rb index 7da82e7..7a1b0cd 100644 --- a/ext/unicorn_http/extconf.rb +++ b/ext/unicorn_http/extconf.rb @@ -2,6 +2,7 @@ require 'mkmf' have_macro("SIZEOF_OFF_T", "ruby.h") or check_sizeof("off_t", "sys/types.h") +have_macro("SIZEOF_SIZE_T", "ruby.h") or check_sizeof("size_t", "sys/types.h") have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h") have_func("rb_str_set_len", "ruby.h") have_func("gmtime_r", "time.h") diff --git a/ext/unicorn_http/global_variables.h b/ext/unicorn_http/global_variables.h index cda7f24..aa0d777 100644 --- a/ext/unicorn_http/global_variables.h +++ b/ext/unicorn_http/global_variables.h @@ -1,7 +1,8 @@ #ifndef global_variables_h #define global_variables_h static VALUE eHttpParserError; -static VALUE eRequestURITooLongError; +static VALUE e413; +static VALUE e414; static VALUE g_rack_url_scheme; static VALUE g_request_method; @@ -36,8 +37,7 @@ static VALUE g_http_11; static const char * const MAX_##N##_LENGTH_ERR = \ "HTTP element " # N " is longer than the " # length " allowed length." -NORETURN(static void parser_error(const char *)); -NORETURN(static void raise_414(const char *)); +NORETURN(static void parser_raise(VALUE klass, const char *)); /** * Validates the max length of given input and throws an HttpParserError @@ -45,12 +45,12 @@ NORETURN(static void raise_414(const char *)); */ #define VALIDATE_MAX_LENGTH(len, N) do { \ if (len > MAX_##N##_LENGTH) \ - parser_error(MAX_##N##_LENGTH_ERR); \ + parser_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); \ } while (0) #define VALIDATE_MAX_URI_LENGTH(len, N) do { \ if (len > MAX_##N##_LENGTH) \ - raise_414(MAX_##N##_LENGTH_ERR); \ + parser_raise(e414, MAX_##N##_LENGTH_ERR); \ } while (0) /** Defines global strings in the init method. */ @@ -66,7 +66,6 @@ DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12); DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */ DEF_MAX_LENGTH(REQUEST_PATH, 1024); DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10)); -DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32))); static void init_globals(void) { diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl index 1d2705c..7a6e031 100644 --- a/ext/unicorn_http/unicorn_http.rl +++ b/ext/unicorn_http/unicorn_http.rl @@ -82,6 +82,14 @@ static VALUE xftrust(VALUE self) return trust_x_forward; } +static size_t MAX_HEADER_LEN = 1024 * (80 + 32); /* same as Mongrel */ + +/* this is only intended for use with Rainbows! */ +static VALUE set_maxhdrlen(VALUE self, VALUE len) +{ + return SIZET2NUM(MAX_HEADER_LEN = NUM2SIZET(len)); +} + /* keep this small for Rainbows! since every client has one */ struct http_parser { int cs; /* Ragel internal state */ @@ -110,30 +118,15 @@ static ID id_clear, id_set_backtrace; static void finalize_header(struct http_parser *hp); -NORETURN(static void raise_with_empty_bt(VALUE)); - -static void raise_with_empty_bt(VALUE exc) +static void parser_raise(VALUE klass, const char *msg) { + VALUE exc = rb_exc_new2(klass, msg); VALUE bt = rb_ary_new(); rb_funcall(exc, id_set_backtrace, 1, bt); rb_exc_raise(exc); } -static void parser_error(const char *msg) -{ - VALUE exc = rb_exc_new2(eHttpParserError, msg); - - raise_with_empty_bt(exc); -} - -static void raise_414(const char *msg) -{ - VALUE exc = rb_exc_new2(eRequestURITooLongError, msg); - - raise_with_empty_bt(exc); -} - #define REMAINING (unsigned long)(pe - p) #define LEN(AT, FPC) (FPC - buffer - hp->AT) #define MARK(M,FPC) (hp->M = (FPC) - buffer) @@ -201,7 +194,7 @@ http_version(struct http_parser *hp, const char *ptr, size_t len) static inline void hp_invalid_if_trailer(struct http_parser *hp) { if (HP_FL_TEST(hp, INTRAILER)) - parser_error("invalid Trailer"); + parser_raise(eHttpParserError, "invalid Trailer"); } static void write_cont_value(struct http_parser *hp, @@ -210,7 +203,7 @@ static void write_cont_value(struct http_parser *hp, char *vptr; if (hp->cont == Qfalse) - parser_error("invalid continuation line"); + parser_raise(eHttpParserError, "invalid continuation line"); if (NIL_P(hp->cont)) return; /* we're ignoring this header (probably Host:) */ @@ -261,7 +254,7 @@ static void write_value(struct http_parser *hp, } else if (f == g_content_length) { hp->len.content = parse_length(RSTRING_PTR(v), RSTRING_LEN(v)); if (hp->len.content < 0) - parser_error("invalid Content-Length"); + parser_raise(eHttpParserError, "invalid Content-Length"); if (hp->len.content != 0) HP_FL_SET(hp, HASBODY); hp_invalid_if_trailer(hp); @@ -351,7 +344,7 @@ static void write_value(struct http_parser *hp, action add_to_chunk_size { hp->len.chunk = step_incr(hp->len.chunk, fc, 16); if (hp->len.chunk < 0) - parser_error("invalid chunk size"); + parser_raise(eHttpParserError, "invalid chunk size"); } action header_done { finalize_header(hp); @@ -692,7 +685,8 @@ static VALUE HttpParser_parse(VALUE self) } http_parser_execute(hp, RSTRING_PTR(data), RSTRING_LEN(data)); - VALIDATE_MAX_LENGTH(hp->offset, HEADER); + if (hp->offset > MAX_HEADER_LEN) + parser_raise(e413, "HTTP header is too large"); if (hp->cs == http_parser_first_final || hp->cs == http_parser_en_ChunkedBody) { @@ -705,7 +699,7 @@ static VALUE HttpParser_parse(VALUE self) } if (hp->cs == http_parser_error) - parser_error("Invalid HTTP format, parsing fails."); + parser_raise(eHttpParserError, "Invalid HTTP format, parsing fails."); return Qnil; } @@ -871,7 +865,7 @@ static VALUE HttpParser_filter_body(VALUE self, VALUE buf, VALUE data) hp->buf = data; http_parser_execute(hp, dptr, dlen); if (hp->cs == http_parser_error) - parser_error("Invalid HTTP format, parsing fails."); + parser_raise(eHttpParserError, "Invalid HTTP format, parsing fails."); assert(hp->s.dest_offset <= hp->offset && "destination buffer overflow"); @@ -919,8 +913,9 @@ void Init_unicorn_http(void) cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject); eHttpParserError = rb_define_class_under(mUnicorn, "HttpParserError", rb_eIOError); - eRequestURITooLongError = - rb_define_class_under(mUnicorn, "RequestURITooLongError", + e413 = rb_define_class_under(mUnicorn, "RequestEntityTooLargeError", + eHttpParserError); + e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError", eHttpParserError); init_globals(); @@ -964,6 +959,7 @@ void Init_unicorn_http(void) rb_define_singleton_method(cHttpParser, "keepalive_requests=", set_ka_req, 1); rb_define_singleton_method(cHttpParser, "trust_x_forwarded=", set_xftrust, 1); rb_define_singleton_method(cHttpParser, "trust_x_forwarded?", xftrust, 0); + rb_define_singleton_method(cHttpParser, "max_header_len=", set_maxhdrlen, 1); init_common_fields(); SET_GLOBAL(g_http_host, "HOST"); -- cgit v1.2.3-24-ge0c7