@ -31,10 +31,24 @@
# endif
# if HTTP_PARSER_DEBUG
# define SET_ERRNO(e) \
do { \
parser - > state = 0x80 | ( e ) ; \
parser - > error_lineno = __LINE__ ; \
} while ( 0 )
# else
# define SET_ERRNO(e) do { parser->state = 0x80 | (e); } while(0)
# endif
# define CALLBACK2(FOR) \
do { \
if ( settings - > on_ # # FOR ) { \
if ( 0 ! = settings - > on_ # # FOR ( parser ) ) return ( p - data ) ; \
if ( 0 ! = settings - > on_ # # FOR ( parser ) ) { \
SET_ERRNO ( HPE_CB_ # # FOR ) ; \
return ( p - data ) ; \
} \
} \
} while ( 0 )
@ -52,6 +66,7 @@ do { \
FOR # # _mark , \
p - FOR # # _mark ) ) \
{ \
SET_ERRNO ( HPE_CB_ # # FOR ) ; \
return ( p - data ) ; \
} \
} \
@ -319,7 +334,13 @@ enum header_states
# if HTTP_PARSER_STRICT
# define STRICT_CHECK(cond) if (cond) goto error
# define STRICT_CHECK(cond) \
do { \
if ( cond ) { \
SET_ERRNO ( HPE_STRICT ) ; \
goto error ; \
} \
} while ( 0 )
# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
# else
# define STRICT_CHECK(cond)
@ -327,6 +348,17 @@ enum header_states
# endif
/* Map errno values to strings for human-readable output */
# define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
static struct {
const char * name ;
const char * description ;
} http_strerror_tab [ ] = {
HTTP_ERRNO_MAP ( HTTP_STRERROR_GEN )
} ;
# undef HTTP_STRERROR_GEN
size_t http_parser_execute ( http_parser * parser ,
const http_parser_settings * settings ,
const char * data ,
@ -336,12 +368,21 @@ size_t http_parser_execute (http_parser *parser,
int8_t unhex_val ;
const char * p = data , * pe ;
int64_t to_read ;
enum state state = ( enum state ) parser - > state ;
enum header_states header_state = ( enum header_states ) parser - > header_state ;
enum state state ;
enum header_states header_state ;
uint64_t index = parser - > index ;
uint64_t nread = parser - > nread ;
/* We're in an error state. Don't attempt to do anything lest we overwrite
* the error information that landed us here .
*/
if ( HTTP_PARSER_ERRNO ( parser ) ! = HPE_OK ) {
return 0 ;
}
state = ( enum state ) parser - > state ;
header_state = ( enum header_states ) parser - > header_state ;
if ( len = = 0 ) {
switch ( state ) {
case s_body_identity_eof :
@ -355,7 +396,8 @@ size_t http_parser_execute (http_parser *parser,
return 0 ;
default :
return 1 ; // error
SET_ERRNO ( HPE_INVALID_EOF_STATE ) ;
return 1 ;
}
}
@ -392,7 +434,10 @@ size_t http_parser_execute (http_parser *parser,
if ( PARSING_HEADER ( state ) ) {
+ + nread ;
/* Buffer overflow attack */
if ( nread > HTTP_MAX_HEADER_SIZE ) goto error ;
if ( nread > HTTP_MAX_HEADER_SIZE ) {
SET_ERRNO ( HPE_HEADER_OVERFLOW ) ;
goto error ;
}
}
switch ( state ) {
@ -401,6 +446,7 @@ size_t http_parser_execute (http_parser *parser,
/* this state is used after a 'Connection: close' message
* the parser will error out if it reads another message
*/
SET_ERRNO ( HPE_CLOSED_CONNECTION ) ;
goto error ;
case s_start_req_or_res :
@ -426,7 +472,11 @@ size_t http_parser_execute (http_parser *parser,
parser - > type = HTTP_RESPONSE ;
state = s_res_HT ;
} else {
if ( ch ! = ' E ' ) goto error ;
if ( ch ! = ' E ' ) {
SET_ERRNO ( HPE_INVALID_CONSTANT ) ;
goto error ;
}
parser - > type = HTTP_REQUEST ;
parser - > method = HTTP_HEAD ;
index = 2 ;
@ -451,6 +501,7 @@ size_t http_parser_execute (http_parser *parser,
break ;
default :
SET_ERRNO ( HPE_INVALID_CONSTANT ) ;
goto error ;
}
break ;
@ -477,7 +528,11 @@ size_t http_parser_execute (http_parser *parser,
break ;
case s_res_first_http_major :
if ( ch < ' 1 ' | | ch > ' 9 ' ) goto error ;
if ( ch < ' 1 ' | | ch > ' 9 ' ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
parser - > http_major = ch - ' 0 ' ;
state = s_res_http_major ;
break ;
@ -490,18 +545,29 @@ size_t http_parser_execute (http_parser *parser,
break ;
}
if ( ! IS_NUM ( ch ) ) goto error ;
if ( ! IS_NUM ( ch ) ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
parser - > http_major * = 10 ;
parser - > http_major + = ch - ' 0 ' ;
if ( parser - > http_major > 999 ) goto error ;
if ( parser - > http_major > 999 ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
break ;
}
/* first digit of minor HTTP version */
case s_res_first_http_minor :
if ( ! IS_NUM ( ch ) ) goto error ;
if ( ! IS_NUM ( ch ) ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
parser - > http_minor = ch - ' 0 ' ;
state = s_res_http_minor ;
break ;
@ -514,12 +580,19 @@ size_t http_parser_execute (http_parser *parser,
break ;
}
if ( ! IS_NUM ( ch ) ) goto error ;
if ( ! IS_NUM ( ch ) ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
parser - > http_minor * = 10 ;
parser - > http_minor + = ch - ' 0 ' ;
if ( parser - > http_minor > 999 ) goto error ;
if ( parser - > http_minor > 999 ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
break ;
}
@ -529,6 +602,8 @@ size_t http_parser_execute (http_parser *parser,
if ( ch = = ' ' ) {
break ;
}
SET_ERRNO ( HPE_INVALID_STATUS ) ;
goto error ;
}
parser - > status_code = ch - ' 0 ' ;
@ -550,6 +625,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_header_field_start ;
break ;
default :
SET_ERRNO ( HPE_INVALID_STATUS ) ;
goto error ;
}
break ;
@ -558,7 +634,11 @@ size_t http_parser_execute (http_parser *parser,
parser - > status_code * = 10 ;
parser - > status_code + = ch - ' 0 ' ;
if ( parser - > status_code > 999 ) goto error ;
if ( parser - > status_code > 999 ) {
SET_ERRNO ( HPE_INVALID_STATUS ) ;
goto error ;
}
break ;
}
@ -590,7 +670,10 @@ size_t http_parser_execute (http_parser *parser,
CALLBACK2 ( message_begin ) ;
if ( ! IS_ALPHA ( LOWER ( ch ) ) ) goto error ;
if ( ! IS_ALPHA ( LOWER ( ch ) ) ) {
SET_ERRNO ( HPE_INVALID_METHOD ) ;
goto error ;
}
start_req_method_assign :
parser - > method = ( enum http_method ) 0 ;
@ -611,7 +694,9 @@ size_t http_parser_execute (http_parser *parser,
case ' S ' : parser - > method = HTTP_SUBSCRIBE ; break ;
case ' T ' : parser - > method = HTTP_TRACE ; break ;
case ' U ' : parser - > method = HTTP_UNLOCK ; /* or UNSUBSCRIBE */ break ;
default : goto error ;
default :
SET_ERRNO ( HPE_INVALID_METHOD ) ;
goto error ;
}
state = s_req_method ;
break ;
@ -619,8 +704,10 @@ size_t http_parser_execute (http_parser *parser,
case s_req_method :
{
if ( ch = = ' \0 ' )
if ( ch = = ' \0 ' ) {
SET_ERRNO ( HPE_INVALID_METHOD ) ;
goto error ;
}
const char * matcher = method_strings [ parser - > method ] ;
if ( ch = = ' ' & & matcher [ index ] = = ' \0 ' ) {
@ -658,6 +745,7 @@ size_t http_parser_execute (http_parser *parser,
} else if ( index = = 4 & & parser - > method = = HTTP_PROPFIND & & ch = = ' P ' ) {
parser - > method = HTTP_PROPPATCH ;
} else {
SET_ERRNO ( HPE_INVALID_METHOD ) ;
goto error ;
}
@ -687,6 +775,7 @@ size_t http_parser_execute (http_parser *parser,
break ;
}
SET_ERRNO ( HPE_INVALID_URL ) ;
goto error ;
}
@ -701,6 +790,7 @@ size_t http_parser_execute (http_parser *parser,
break ;
}
SET_ERRNO ( HPE_INVALID_URL ) ;
goto error ;
}
@ -737,6 +827,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_req_query_string_start ;
break ;
default :
SET_ERRNO ( HPE_INVALID_HOST ) ;
goto error ;
}
break ;
@ -762,6 +853,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_req_query_string_start ;
break ;
default :
SET_ERRNO ( HPE_INVALID_PORT ) ;
goto error ;
}
break ;
@ -800,6 +892,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_req_fragment_start ;
break ;
default :
SET_ERRNO ( HPE_INVALID_PATH ) ;
goto error ;
}
break ;
@ -836,6 +929,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_req_fragment_start ;
break ;
default :
SET_ERRNO ( HPE_INVALID_QUERY_STRING ) ;
goto error ;
}
break ;
@ -873,6 +967,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_req_fragment_start ;
break ;
default :
SET_ERRNO ( HPE_INVALID_QUERY_STRING ) ;
goto error ;
}
break ;
@ -910,6 +1005,7 @@ size_t http_parser_execute (http_parser *parser,
case ' # ' :
break ;
default :
SET_ERRNO ( HPE_INVALID_FRAGMENT ) ;
goto error ;
}
break ;
@ -943,6 +1039,7 @@ size_t http_parser_execute (http_parser *parser,
case ' # ' :
break ;
default :
SET_ERRNO ( HPE_INVALID_FRAGMENT ) ;
goto error ;
}
break ;
@ -956,6 +1053,7 @@ size_t http_parser_execute (http_parser *parser,
case ' ' :
break ;
default :
SET_ERRNO ( HPE_INVALID_CONSTANT ) ;
goto error ;
}
break ;
@ -982,7 +1080,11 @@ size_t http_parser_execute (http_parser *parser,
/* first digit of major HTTP version */
case s_req_first_http_major :
if ( ch < ' 1 ' | | ch > ' 9 ' ) goto error ;
if ( ch < ' 1 ' | | ch > ' 9 ' ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
parser - > http_major = ch - ' 0 ' ;
state = s_req_http_major ;
break ;
@ -995,18 +1097,29 @@ size_t http_parser_execute (http_parser *parser,
break ;
}
if ( ! IS_NUM ( ch ) ) goto error ;
if ( ! IS_NUM ( ch ) ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
parser - > http_major * = 10 ;
parser - > http_major + = ch - ' 0 ' ;
if ( parser - > http_major > 999 ) goto error ;
if ( parser - > http_major > 999 ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
break ;
}
/* first digit of minor HTTP version */
case s_req_first_http_minor :
if ( ! IS_NUM ( ch ) ) goto error ;
if ( ! IS_NUM ( ch ) ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
parser - > http_minor = ch - ' 0 ' ;
state = s_req_http_minor ;
break ;
@ -1026,19 +1139,30 @@ size_t http_parser_execute (http_parser *parser,
/* XXX allow spaces after digit? */
if ( ! IS_NUM ( ch ) ) goto error ;
if ( ! IS_NUM ( ch ) ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
parser - > http_minor * = 10 ;
parser - > http_minor + = ch - ' 0 ' ;
if ( parser - > http_minor > 999 ) goto error ;
if ( parser - > http_minor > 999 ) {
SET_ERRNO ( HPE_INVALID_VERSION ) ;
goto error ;
}
break ;
}
/* end of request line */
case s_req_line_almost_done :
{
if ( ch ! = LF ) goto error ;
if ( ch ! = LF ) {
SET_ERRNO ( HPE_LF_EXPECTED ) ;
goto error ;
}
state = s_header_field_start ;
break ;
}
@ -1060,7 +1184,10 @@ size_t http_parser_execute (http_parser *parser,
c = TOKEN ( ch ) ;
if ( ! c ) goto error ;
if ( ! c ) {
SET_ERRNO ( HPE_INVALID_HEADER_TOKEN ) ;
goto error ;
}
MARK ( header_field ) ;
@ -1217,6 +1344,7 @@ size_t http_parser_execute (http_parser *parser,
break ;
}
SET_ERRNO ( HPE_INVALID_HEADER_TOKEN ) ;
goto error ;
}
@ -1260,7 +1388,11 @@ size_t http_parser_execute (http_parser *parser,
break ;
case h_content_length :
if ( ! IS_NUM ( ch ) ) goto error ;
if ( ! IS_NUM ( ch ) ) {
SET_ERRNO ( HPE_INVALID_CONTENT_LENGTH ) ;
goto error ;
}
parser - > content_length = ch - ' 0 ' ;
break ;
@ -1310,7 +1442,11 @@ size_t http_parser_execute (http_parser *parser,
case h_content_length :
if ( ch = = ' ' ) break ;
if ( ! IS_NUM ( ch ) ) goto error ;
if ( ! IS_NUM ( ch ) ) {
SET_ERRNO ( HPE_INVALID_CONTENT_LENGTH ) ;
goto error ;
}
parser - > content_length * = 10 ;
parser - > content_length + = ch - ' 0 ' ;
break ;
@ -1431,6 +1567,7 @@ size_t http_parser_execute (http_parser *parser,
default :
parser - > state = state ;
SET_ERRNO ( HPE_CB_headers_complete ) ;
return p - data ; /* Error */
}
}
@ -1498,7 +1635,11 @@ size_t http_parser_execute (http_parser *parser,
assert ( parser - > flags & F_CHUNKED ) ;
unhex_val = unhex [ ( unsigned char ) ch ] ;
if ( unhex_val = = - 1 ) goto error ;
if ( unhex_val = = - 1 ) {
SET_ERRNO ( HPE_INVALID_CHUNK_SIZE ) ;
goto error ;
}
parser - > content_length = unhex_val ;
state = s_chunk_size ;
break ;
@ -1520,6 +1661,8 @@ size_t http_parser_execute (http_parser *parser,
state = s_chunk_parameters ;
break ;
}
SET_ERRNO ( HPE_INVALID_CHUNK_SIZE ) ;
goto error ;
}
@ -1588,6 +1731,7 @@ size_t http_parser_execute (http_parser *parser,
default :
assert ( 0 & & " unhandled state " ) ;
SET_ERRNO ( HPE_INVALID_INTERNAL_STATE ) ;
goto error ;
}
}
@ -1607,7 +1751,10 @@ size_t http_parser_execute (http_parser *parser,
return len ;
error :
parser - > state = s_dead ;
if ( HTTP_PARSER_ERRNO ( parser ) = = HPE_OK ) {
SET_ERRNO ( HPE_UNKNOWN ) ;
}
return ( p - data ) ;
}
@ -1649,3 +1796,15 @@ http_parser_init (http_parser *parser, enum http_parser_type t)
parser - > flags = 0 ;
parser - > method = 0 ;
}
const char *
http_errno_name ( enum http_errno err ) {
assert ( err < ( sizeof ( http_strerror_tab ) / sizeof ( http_strerror_tab [ 0 ] ) ) ) ;
return http_strerror_tab [ err ] . name ;
}
const char *
http_errno_description ( enum http_errno err ) {
assert ( err < ( sizeof ( http_strerror_tab ) / sizeof ( http_strerror_tab [ 0 ] ) ) ) ;
return http_strerror_tab [ err ] . description ;
}