mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Several robustness improvements to protect mongrel against bad HTTP requests.
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@138 19e92222-5c0b-0410-8929-a290d50e31e9
This commit is contained in:
parent
6c8d479b38
commit
3c804d5e15
6 changed files with 201 additions and 77 deletions
|
@ -9,6 +9,7 @@
|
|||
static VALUE mMongrel;
|
||||
static VALUE cHttpParser;
|
||||
static VALUE cURIClassifier;
|
||||
static VALUE eHttpParserError;
|
||||
static int id_handler_map;
|
||||
|
||||
static VALUE global_http_prefix;
|
||||
|
@ -32,15 +33,36 @@ static VALUE global_mongrel_version;
|
|||
static VALUE global_server_software;
|
||||
static VALUE global_port_80;
|
||||
|
||||
#define DEF_GLOBAL(name, val) global_##name = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##name);
|
||||
/** Defines common length and error messages for input length validation. */
|
||||
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " # length " allowed length.";
|
||||
|
||||
/** Validates the max length of given input and throws an HttpParserError exception if over. */
|
||||
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); }
|
||||
|
||||
/** Defines global strings in the init method. */
|
||||
#define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N);
|
||||
|
||||
|
||||
/* Defines the maximum allowed lengths for various input elements.*/
|
||||
DEF_MAX_LENGTH(FIELD_NAME, 256);
|
||||
DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
|
||||
DEF_MAX_LENGTH(REQUEST_URI, 512);
|
||||
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
|
||||
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
|
||||
|
||||
|
||||
void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
|
||||
{
|
||||
char *ch, *end;
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE v = rb_str_new(value, vlen);
|
||||
VALUE f = rb_str_dup(global_http_prefix);
|
||||
VALUE v = Qnil;
|
||||
VALUE f = Qnil;
|
||||
|
||||
VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
|
||||
VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
|
||||
|
||||
v = rb_str_new(value, vlen);
|
||||
f = rb_str_dup(global_http_prefix);
|
||||
f = rb_str_buf_cat(f, field, flen);
|
||||
|
||||
for(ch = RSTRING(f)->ptr, end = ch + RSTRING(f)->len; ch < end; ch++) {
|
||||
|
@ -57,14 +79,20 @@ void http_field(void *data, const char *field, size_t flen, const char *value, s
|
|||
void request_method(void *data, const char *at, size_t length)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE val = rb_str_new(at, length);
|
||||
VALUE val = Qnil;
|
||||
|
||||
val = rb_str_new(at, length);
|
||||
rb_hash_aset(req, global_request_method, val);
|
||||
}
|
||||
|
||||
void request_uri(void *data, const char *at, size_t length)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE val = rb_str_new(at, length);
|
||||
VALUE val = Qnil;
|
||||
|
||||
VALIDATE_MAX_LENGTH(length, REQUEST_URI);
|
||||
|
||||
val = rb_str_new(at, length);
|
||||
rb_hash_aset(req, global_request_uri, val);
|
||||
}
|
||||
|
||||
|
@ -72,7 +100,11 @@ void request_uri(void *data, const char *at, size_t length)
|
|||
void query_string(void *data, const char *at, size_t length)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE val = rb_str_new(at, length);
|
||||
VALUE val = Qnil;
|
||||
|
||||
VALIDATE_MAX_LENGTH(length, QUERY_STRING);
|
||||
|
||||
val = rb_str_new(at, length);
|
||||
rb_hash_aset(req, global_query_string, val);
|
||||
}
|
||||
|
||||
|
@ -136,6 +168,7 @@ VALUE HttpParser_alloc(VALUE klass)
|
|||
hp->query_string = query_string;
|
||||
hp->http_version = http_version;
|
||||
hp->header_done = header_done;
|
||||
http_parser_init(hp);
|
||||
|
||||
obj = Data_Wrap_Struct(klass, NULL, HttpParser_free, hp);
|
||||
|
||||
|
@ -214,8 +247,10 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data)
|
|||
http->data = (void *)req_hash;
|
||||
http_parser_execute(http, RSTRING(data)->ptr, RSTRING(data)->len);
|
||||
|
||||
VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
|
||||
|
||||
if(http_parser_has_error(http)) {
|
||||
rb_raise(rb_eStandardError, "HTTP Parsing failure");
|
||||
rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
|
||||
} else {
|
||||
return INT2FIX(http_parser_nread(http));
|
||||
}
|
||||
|
@ -479,6 +514,8 @@ void Init_http11()
|
|||
DEF_GLOBAL(server_software, "SERVER_SOFTWARE");
|
||||
DEF_GLOBAL(port_80, "80");
|
||||
|
||||
eHttpParserError = rb_define_class_under(mMongrel, "HttpParserError", rb_eIOError);
|
||||
|
||||
cHttpParser = rb_define_class_under(mMongrel, "HttpParser", rb_cObject);
|
||||
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
||||
rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MARK(S,F) assert((F) - (S)->mark >= 0); (S)->mark = (F);
|
||||
#define MARK(S,F) (S)->mark = (F);
|
||||
|
||||
/** machine **/
|
||||
#line 101 "ext/http11/http11_parser.rl"
|
||||
#line 107 "ext/http11/http11_parser.rl"
|
||||
|
||||
|
||||
/** Data **/
|
||||
|
@ -21,7 +21,7 @@ static int http_parser_first_final = 53;
|
|||
|
||||
static int http_parser_error = 1;
|
||||
|
||||
#line 105 "ext/http11/http11_parser.rl"
|
||||
#line 111 "ext/http11/http11_parser.rl"
|
||||
|
||||
int http_parser_init(http_parser *parser) {
|
||||
int cs = 0;
|
||||
|
@ -30,7 +30,7 @@ int http_parser_init(http_parser *parser) {
|
|||
{
|
||||
cs = http_parser_start;
|
||||
}
|
||||
#line 109 "ext/http11/http11_parser.rl"
|
||||
#line 115 "ext/http11/http11_parser.rl"
|
||||
parser->cs = cs;
|
||||
parser->body_start = NULL;
|
||||
parser->content_len = 0;
|
||||
|
@ -71,7 +71,7 @@ st1:
|
|||
goto _out1;
|
||||
tr13:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st2;
|
||||
st2:
|
||||
if ( ++p == pe )
|
||||
|
@ -117,9 +117,10 @@ case 7:
|
|||
goto tr33;
|
||||
goto st1;
|
||||
tr33:
|
||||
#line 29 "ext/http11/http11_parser.rl"
|
||||
#line 30 "ext/http11/http11_parser.rl"
|
||||
{
|
||||
if(parser->request_method != NULL)
|
||||
assert(p - parser->mark >= 0 && "buffer overflow");
|
||||
if(parser->request_method != NULL)
|
||||
parser->request_method(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
goto st8;
|
||||
|
@ -127,7 +128,7 @@ st8:
|
|||
if ( ++p == pe )
|
||||
goto _out8;
|
||||
case 8:
|
||||
#line 131 "ext/http11/http11_parser.c"
|
||||
#line 132 "ext/http11/http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 42: goto tr27;
|
||||
case 43: goto tr28;
|
||||
|
@ -145,26 +146,28 @@ case 8:
|
|||
goto st1;
|
||||
tr27:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st9;
|
||||
st9:
|
||||
if ( ++p == pe )
|
||||
goto _out9;
|
||||
case 9:
|
||||
#line 155 "ext/http11/http11_parser.c"
|
||||
#line 156 "ext/http11/http11_parser.c"
|
||||
if ( (*p) == 32 )
|
||||
goto tr34;
|
||||
goto st1;
|
||||
tr34:
|
||||
#line 33 "ext/http11/http11_parser.rl"
|
||||
#line 35 "ext/http11/http11_parser.rl"
|
||||
{
|
||||
assert(p - parser->mark >= 0 && "buffer overflow");
|
||||
if(parser->request_uri != NULL)
|
||||
parser->request_uri(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
goto st10;
|
||||
tr46:
|
||||
#line 37 "ext/http11/http11_parser.rl"
|
||||
#line 40 "ext/http11/http11_parser.rl"
|
||||
{
|
||||
assert(p - parser->mark >= 0 && "buffer overflow");
|
||||
if(parser->query_string != NULL)
|
||||
parser->query_string(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
|
@ -173,19 +176,19 @@ st10:
|
|||
if ( ++p == pe )
|
||||
goto _out10;
|
||||
case 10:
|
||||
#line 177 "ext/http11/http11_parser.c"
|
||||
#line 180 "ext/http11/http11_parser.c"
|
||||
if ( (*p) == 72 )
|
||||
goto tr11;
|
||||
goto st1;
|
||||
tr11:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st11;
|
||||
st11:
|
||||
if ( ++p == pe )
|
||||
goto _out11;
|
||||
case 11:
|
||||
#line 189 "ext/http11/http11_parser.c"
|
||||
#line 192 "ext/http11/http11_parser.c"
|
||||
if ( (*p) == 84 )
|
||||
goto st12;
|
||||
goto st1;
|
||||
|
@ -243,8 +246,9 @@ case 18:
|
|||
goto st18;
|
||||
goto st1;
|
||||
tr37:
|
||||
#line 42 "ext/http11/http11_parser.rl"
|
||||
#line 46 "ext/http11/http11_parser.rl"
|
||||
{
|
||||
assert(p - parser->mark >= 0 && "buffer overflow");
|
||||
if(parser->http_version != NULL)
|
||||
parser->http_version(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
|
@ -252,6 +256,7 @@ tr37:
|
|||
tr49:
|
||||
#line 22 "ext/http11/http11_parser.rl"
|
||||
{
|
||||
assert(p - (parser->mark - 1) >= 0 && "buffer overflow");
|
||||
if(parser->http_field != NULL) {
|
||||
parser->http_field(parser->data,
|
||||
parser->field_start, parser->field_len,
|
||||
|
@ -263,7 +268,7 @@ st19:
|
|||
if ( ++p == pe )
|
||||
goto _out19;
|
||||
case 19:
|
||||
#line 267 "ext/http11/http11_parser.c"
|
||||
#line 272 "ext/http11/http11_parser.c"
|
||||
if ( (*p) == 10 )
|
||||
goto st20;
|
||||
goto st1;
|
||||
|
@ -303,7 +308,7 @@ case 21:
|
|||
goto tr40;
|
||||
goto st1;
|
||||
tr40:
|
||||
#line 46 "ext/http11/http11_parser.rl"
|
||||
#line 52 "ext/http11/http11_parser.rl"
|
||||
{
|
||||
parser->body_start = p+1;
|
||||
if(parser->header_done != NULL)
|
||||
|
@ -315,7 +320,7 @@ st53:
|
|||
if ( ++p == pe )
|
||||
goto _out53;
|
||||
case 53:
|
||||
#line 319 "ext/http11/http11_parser.c"
|
||||
#line 324 "ext/http11/http11_parser.c"
|
||||
goto st1;
|
||||
tr36:
|
||||
#line 16 "ext/http11/http11_parser.rl"
|
||||
|
@ -325,7 +330,7 @@ st22:
|
|||
if ( ++p == pe )
|
||||
goto _out22;
|
||||
case 22:
|
||||
#line 329 "ext/http11/http11_parser.c"
|
||||
#line 334 "ext/http11/http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 33: goto st22;
|
||||
case 58: goto tr32;
|
||||
|
@ -360,7 +365,7 @@ st23:
|
|||
if ( ++p == pe )
|
||||
goto _out23;
|
||||
case 23:
|
||||
#line 364 "ext/http11/http11_parser.c"
|
||||
#line 369 "ext/http11/http11_parser.c"
|
||||
if ( (*p) == 13 )
|
||||
goto tr49;
|
||||
goto tr52;
|
||||
|
@ -372,19 +377,19 @@ st24:
|
|||
if ( ++p == pe )
|
||||
goto _out24;
|
||||
case 24:
|
||||
#line 376 "ext/http11/http11_parser.c"
|
||||
#line 381 "ext/http11/http11_parser.c"
|
||||
if ( (*p) == 13 )
|
||||
goto tr49;
|
||||
goto st24;
|
||||
tr28:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st25;
|
||||
st25:
|
||||
if ( ++p == pe )
|
||||
goto _out25;
|
||||
case 25:
|
||||
#line 388 "ext/http11/http11_parser.c"
|
||||
#line 393 "ext/http11/http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 43: goto st25;
|
||||
case 58: goto st26;
|
||||
|
@ -403,13 +408,13 @@ case 25:
|
|||
goto st1;
|
||||
tr30:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st26;
|
||||
st26:
|
||||
if ( ++p == pe )
|
||||
goto _out26;
|
||||
case 26:
|
||||
#line 413 "ext/http11/http11_parser.c"
|
||||
#line 418 "ext/http11/http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 32: goto tr34;
|
||||
case 37: goto st27;
|
||||
|
@ -451,20 +456,20 @@ case 28:
|
|||
goto st1;
|
||||
tr29:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st29;
|
||||
st29:
|
||||
if ( ++p == pe )
|
||||
goto _out29;
|
||||
case 29:
|
||||
#line 461 "ext/http11/http11_parser.c"
|
||||
#line 466 "ext/http11/http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 32: goto tr34;
|
||||
case 37: goto st31;
|
||||
case 47: goto st1;
|
||||
case 60: goto st1;
|
||||
case 62: goto st1;
|
||||
case 63: goto tr44;
|
||||
case 63: goto tr43;
|
||||
case 127: goto st1;
|
||||
}
|
||||
if ( (*p) > 31 ) {
|
||||
|
@ -482,7 +487,7 @@ case 30:
|
|||
case 37: goto st31;
|
||||
case 60: goto st1;
|
||||
case 62: goto st1;
|
||||
case 63: goto tr44;
|
||||
case 63: goto tr43;
|
||||
case 127: goto st1;
|
||||
}
|
||||
if ( (*p) > 31 ) {
|
||||
|
@ -517,9 +522,10 @@ case 32:
|
|||
} else
|
||||
goto st30;
|
||||
goto st1;
|
||||
tr44:
|
||||
#line 33 "ext/http11/http11_parser.rl"
|
||||
tr43:
|
||||
#line 35 "ext/http11/http11_parser.rl"
|
||||
{
|
||||
assert(p - parser->mark >= 0 && "buffer overflow");
|
||||
if(parser->request_uri != NULL)
|
||||
parser->request_uri(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
|
@ -528,7 +534,7 @@ st33:
|
|||
if ( ++p == pe )
|
||||
goto _out33;
|
||||
case 33:
|
||||
#line 532 "ext/http11/http11_parser.c"
|
||||
#line 538 "ext/http11/http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 32: goto tr46;
|
||||
case 37: goto tr51;
|
||||
|
@ -544,13 +550,13 @@ case 33:
|
|||
goto tr50;
|
||||
tr50:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st34;
|
||||
st34:
|
||||
if ( ++p == pe )
|
||||
goto _out34;
|
||||
case 34:
|
||||
#line 554 "ext/http11/http11_parser.c"
|
||||
#line 560 "ext/http11/http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 32: goto tr46;
|
||||
case 37: goto st35;
|
||||
|
@ -566,13 +572,13 @@ case 34:
|
|||
goto st34;
|
||||
tr51:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st35;
|
||||
st35:
|
||||
if ( ++p == pe )
|
||||
goto _out35;
|
||||
case 35:
|
||||
#line 576 "ext/http11/http11_parser.c"
|
||||
#line 582 "ext/http11/http11_parser.c"
|
||||
if ( (*p) < 65 ) {
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto st36;
|
||||
|
@ -597,13 +603,13 @@ case 36:
|
|||
goto st1;
|
||||
tr14:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st37;
|
||||
st37:
|
||||
if ( ++p == pe )
|
||||
goto _out37;
|
||||
case 37:
|
||||
#line 607 "ext/http11/http11_parser.c"
|
||||
#line 613 "ext/http11/http11_parser.c"
|
||||
if ( (*p) == 69 )
|
||||
goto st38;
|
||||
goto st1;
|
||||
|
@ -616,13 +622,13 @@ case 38:
|
|||
goto st1;
|
||||
tr15:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st39;
|
||||
st39:
|
||||
if ( ++p == pe )
|
||||
goto _out39;
|
||||
case 39:
|
||||
#line 626 "ext/http11/http11_parser.c"
|
||||
#line 632 "ext/http11/http11_parser.c"
|
||||
if ( (*p) == 69 )
|
||||
goto st40;
|
||||
goto st1;
|
||||
|
@ -642,13 +648,13 @@ case 41:
|
|||
goto st1;
|
||||
tr16:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st42;
|
||||
st42:
|
||||
if ( ++p == pe )
|
||||
goto _out42;
|
||||
case 42:
|
||||
#line 652 "ext/http11/http11_parser.c"
|
||||
#line 658 "ext/http11/http11_parser.c"
|
||||
if ( (*p) == 80 )
|
||||
goto st43;
|
||||
goto st1;
|
||||
|
@ -689,13 +695,13 @@ case 47:
|
|||
goto st1;
|
||||
tr17:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st48;
|
||||
st48:
|
||||
if ( ++p == pe )
|
||||
goto _out48;
|
||||
case 48:
|
||||
#line 699 "ext/http11/http11_parser.c"
|
||||
#line 705 "ext/http11/http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 79: goto st49;
|
||||
case 85: goto st38;
|
||||
|
@ -710,13 +716,13 @@ case 49:
|
|||
goto st1;
|
||||
tr18:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
{MARK(parser, p); }
|
||||
goto st50;
|
||||
st50:
|
||||
if ( ++p == pe )
|
||||
goto _out50;
|
||||
case 50:
|
||||
#line 720 "ext/http11/http11_parser.c"
|
||||
#line 726 "ext/http11/http11_parser.c"
|
||||
if ( (*p) == 82 )
|
||||
goto st51;
|
||||
goto st1;
|
||||
|
@ -791,15 +797,15 @@ case 52:
|
|||
|
||||
_out: {}
|
||||
}
|
||||
#line 128 "ext/http11/http11_parser.rl"
|
||||
#line 134 "ext/http11/http11_parser.rl"
|
||||
|
||||
parser->cs = cs;
|
||||
parser->nread = p - buffer;
|
||||
if(parser->body_start) {
|
||||
/* final \r\n combo encountered so stop right here */
|
||||
|
||||
#line 802 "ext/http11/http11_parser.c"
|
||||
#line 134 "ext/http11/http11_parser.rl"
|
||||
#line 808 "ext/http11/http11_parser.c"
|
||||
#line 140 "ext/http11/http11_parser.rl"
|
||||
parser->nread++;
|
||||
}
|
||||
|
||||
|
@ -811,8 +817,8 @@ int http_parser_finish(http_parser *parser)
|
|||
int cs = parser->cs;
|
||||
|
||||
|
||||
#line 815 "ext/http11/http11_parser.c"
|
||||
#line 145 "ext/http11/http11_parser.rl"
|
||||
#line 821 "ext/http11/http11_parser.c"
|
||||
#line 151 "ext/http11/http11_parser.rl"
|
||||
|
||||
parser->cs = cs;
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MARK(S,F) assert((F) - (S)->mark >= 0); (S)->mark = (F);
|
||||
#define MARK(S,F) (S)->mark = (F);
|
||||
|
||||
/** machine **/
|
||||
%%{
|
||||
machine http_parser;
|
||||
|
||||
action mark { MARK(parser, fpc); }
|
||||
action mark {MARK(parser, fpc); }
|
||||
|
||||
action start_field { parser->field_start = fpc; }
|
||||
action write_field {
|
||||
|
@ -20,6 +20,7 @@
|
|||
|
||||
action start_value { MARK(parser, fpc); }
|
||||
action write_value {
|
||||
assert(p - (parser->mark - 1) >= 0 && "buffer overflow");
|
||||
if(parser->http_field != NULL) {
|
||||
parser->http_field(parser->data,
|
||||
parser->field_start, parser->field_len,
|
||||
|
@ -27,22 +28,27 @@
|
|||
}
|
||||
}
|
||||
action request_method {
|
||||
if(parser->request_method != NULL)
|
||||
assert(p - parser->mark >= 0 && "buffer overflow");
|
||||
if(parser->request_method != NULL)
|
||||
parser->request_method(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
action request_uri {
|
||||
assert(p - parser->mark >= 0 && "buffer overflow");
|
||||
if(parser->request_uri != NULL)
|
||||
parser->request_uri(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
action query_string {
|
||||
assert(p - parser->mark >= 0 && "buffer overflow");
|
||||
if(parser->query_string != NULL)
|
||||
parser->query_string(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
|
||||
action http_version {
|
||||
assert(p - parser->mark >= 0 && "buffer overflow");
|
||||
if(parser->http_version != NULL)
|
||||
parser->http_version(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
|
||||
action done {
|
||||
parser->body_start = p+1;
|
||||
if(parser->header_done != NULL)
|
||||
|
@ -61,7 +67,7 @@
|
|||
extra = ("!" | "*" | "'" | "(" | ")" | ",");
|
||||
reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
|
||||
unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
|
||||
national = any - (alpha | digit | reserved | extra | safe | unsafe);
|
||||
national = any -- (alpha | digit | reserved | extra | safe | unsafe);
|
||||
unreserved = (alpha | digit | safe | extra | national);
|
||||
escape = ("%" xdigit xdigit);
|
||||
uchar = (unreserved | escape);
|
||||
|
@ -69,7 +75,7 @@
|
|||
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
|
||||
|
||||
# elements
|
||||
token = (ascii - (CTL | tspecials));
|
||||
token = (ascii -- (CTL | tspecials));
|
||||
|
||||
# URI schemes and absolute paths
|
||||
scheme = ( alpha | digit | "+" | "-" | "." )* ;
|
||||
|
@ -89,11 +95,11 @@
|
|||
HTTP_Version = ("HTTP/" http_number) >mark %http_version ;
|
||||
Request_Line = (Method " " Request_URI " " HTTP_Version CRLF) ;
|
||||
|
||||
field_name = (token - ":")+ >start_field %write_field;
|
||||
field_name = (token -- ":")+ >start_field %write_field;
|
||||
|
||||
field_value = any* >start_value %write_value;
|
||||
|
||||
message_header = field_name ":" field_value $0 CRLF >1;
|
||||
message_header = field_name ":" field_value :> CRLF;
|
||||
|
||||
Request = Request_Line (message_header)* ( CRLF @done);
|
||||
|
||||
|
|
|
@ -110,7 +110,11 @@ module Mongrel
|
|||
ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
||||
|
||||
# The basic max request size we'll try to read.
|
||||
CHUNK_SIZE=(16 * 1024)
|
||||
CHUNK_SIZE=(4 * 1024)
|
||||
|
||||
# This is the maximum header that is allowed before a client is booted. The parser detects
|
||||
# this, but we'd also like to do this as well.
|
||||
MAX_HEADER=1024 * (80 + 32)
|
||||
|
||||
# Format to generate a correct RFC 1123 date. rdoc for Time is wrong, there is no httpdate function.
|
||||
RFC_1123_DATE_FORMAT="%a, %d %B %Y %H:%M:%S GMT".freeze
|
||||
|
@ -259,13 +263,12 @@ module Mongrel
|
|||
attr_reader :header_sent
|
||||
attr_reader :status_sent
|
||||
|
||||
def initialize(socket, filter = nil)
|
||||
def initialize(socket)
|
||||
@socket = socket
|
||||
@body = StringIO.new
|
||||
@status = 404
|
||||
@header = HeaderOut.new(StringIO.new)
|
||||
@header[Const::DATE] = HttpServer.httpdate(Time.now)
|
||||
@filter = filter
|
||||
@body_sent = false
|
||||
@header_sent = false
|
||||
@status_sent = false
|
||||
|
@ -343,6 +346,8 @@ module Mongrel
|
|||
end
|
||||
|
||||
|
||||
|
||||
|
||||
# This is the main driver of Mongrel, while the Mognrel::HttpParser and Mongrel::URIClassifier
|
||||
# make up the majority of how the server functions. It's a very simple class that just
|
||||
# has a thread accepting connections and a simple HttpServer.process_client function
|
||||
|
@ -432,14 +437,20 @@ module Mongrel
|
|||
else
|
||||
# gotta stream and read again until we can get the parser to be character safe
|
||||
# TODO: make this more efficient since this means we're parsing a lot repeatedly
|
||||
if data.length >= Const::MAX_HEADER
|
||||
raise HttpParserError.new("HEADER is longer than allowed, aborting client early.")
|
||||
end
|
||||
|
||||
parser.reset
|
||||
data << client.readpartial(Const::CHUNK_SIZE)
|
||||
end
|
||||
end
|
||||
rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL
|
||||
# ignored
|
||||
rescue HttpParserError
|
||||
STDERR.puts "BAD CLIENT: #$!"
|
||||
rescue => details
|
||||
STDERR.puts "ERROR(#{details.class}): #{details}"
|
||||
STDERR.puts "ERROR: #$!"
|
||||
STDERR.puts details.backtrace.join("\n")
|
||||
ensure
|
||||
client.close
|
||||
|
|
|
@ -70,11 +70,9 @@ module ObjectTracker
|
|||
# Strings can't be tracked easily and are so numerous that they drown out all else
|
||||
# so we just ignore them in the counts.
|
||||
ObjectSpace.each_object do |obj|
|
||||
if not obj.kind_of? String
|
||||
ospace << obj.object_id
|
||||
counts[obj.class] ||= 0
|
||||
counts[obj.class] += 1
|
||||
end
|
||||
ospace << obj.object_id
|
||||
counts[obj.class] ||= 0
|
||||
counts[obj.class] += 1
|
||||
end
|
||||
|
||||
dead_objects = @active_objects - ospace
|
||||
|
|
|
@ -2,6 +2,7 @@ require 'test/unit'
|
|||
require 'http11'
|
||||
require 'mongrel'
|
||||
require 'benchmark'
|
||||
require 'digest/sha1'
|
||||
|
||||
include Mongrel
|
||||
|
||||
|
@ -38,6 +39,72 @@ class HttpParserTest < Test::Unit::TestCase
|
|||
assert parser.error?, "Parser SHOULD have error"
|
||||
end
|
||||
|
||||
# lame random garbage maker
|
||||
def rand_data(min, max, readable=true)
|
||||
count = min + ((rand(max)+1) *10).to_i
|
||||
res = count.to_s + "/"
|
||||
|
||||
if readable
|
||||
res << Digest::SHA1.hexdigest(rand(count * 1000).to_s) * (count / 40)
|
||||
else
|
||||
res << Digest::SHA1.digest(rand(count * 1000).to_s) * (count / 20)
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
def test_horrible_queries
|
||||
parser = HttpParser.new
|
||||
|
||||
# first verify that large random get requests fail
|
||||
100.times do |c|
|
||||
get = "GET /#{rand_data(1024, 1024+(c*1024))} HTTP/1.1\r\n"
|
||||
assert_raises Mongrel::HttpParserError do
|
||||
parser.execute({}, get)
|
||||
parser.reset
|
||||
end
|
||||
end
|
||||
|
||||
# then that large header names are caught
|
||||
100.times do |c|
|
||||
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
|
||||
assert_raises Mongrel::HttpParserError do
|
||||
parser.execute({}, get)
|
||||
parser.reset
|
||||
end
|
||||
end
|
||||
|
||||
# then that large mangled field values are caught
|
||||
100.times do |c|
|
||||
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
||||
assert_raises Mongrel::HttpParserError do
|
||||
parser.execute({}, get)
|
||||
parser.reset
|
||||
end
|
||||
end
|
||||
|
||||
# then large headers are rejected too
|
||||
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
|
||||
get << "X-Test: test\r\n" * (80 * 1024)
|
||||
assert_raises Mongrel::HttpParserError do
|
||||
parser.execute({}, get)
|
||||
parser.reset
|
||||
end
|
||||
|
||||
# finally just that random garbage gets blocked all the time
|
||||
10.times do |c|
|
||||
get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
||||
assert_raises Mongrel::HttpParserError do
|
||||
parser.execute({}, get)
|
||||
parser.reset
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
def test_query_parse
|
||||
res = HttpRequest.query_parse("zed=1&frank=2")
|
||||
assert res["zed"], "didn't get the request right"
|
||||
|
@ -51,7 +118,6 @@ class HttpParserTest < Test::Unit::TestCase
|
|||
assert_equal 4,res["zed"].length, "wrong number for zed"
|
||||
assert_equal "11",res["frank"], "wrong number for frank"
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue