Commit 707a0b41 authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

Move name/value validation functions to src

nghttp2 library itself now accept octet header/value pairs,
completely not restricted by HTTP/1 header name/value rule.
The applications may impose restriction about them using
validators.
parent 0e4b3d43
......@@ -2143,8 +2143,6 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |nva| includes empty name, or name which contains invalid
* characters.
* :enum:`NGHTTP2_ERR_STREAM_CLOSED`
* The stream is already closed or does not exist.
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
*/
......
......@@ -550,46 +550,6 @@ nghttp2_settings_entry* nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
return iv_copy;
}
int nghttp2_nv_array_check_nocase(const nghttp2_nv *nva, size_t nvlen)
{
size_t i;
for(i = 0; i < nvlen; ++i) {
if(!nghttp2_check_header_name_nocase(nva[i].name, nva[i].namelen)) {
return 0;
}
if(!nghttp2_check_header_value(nva[i].value, nva[i].valuelen)) {
return 0;
}
}
return 1;
}
int nghttp2_nv_array_check(const nghttp2_nv *nva, size_t nvlen)
{
size_t i;
for(i = 0; i < nvlen; ++i) {
if(!nghttp2_check_header_name(nva[i].name, nva[i].namelen)) {
return 0;
}
if(!nghttp2_check_header_value(nva[i].value, nva[i].valuelen)) {
return 0;
}
}
return 1;
}
int nghttp2_nv_check(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen)
{
if(!nghttp2_check_header_name(name, namelen)) {
return 0;
}
if(!nghttp2_check_header_value(value, valuelen)) {
return 0;
}
return 1;
}
int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b)
{
return a->namelen == b->namelen && a->valuelen == b->valuelen &&
......
......@@ -558,25 +558,6 @@ int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b);
*/
void nghttp2_nv_array_del(nghttp2_nv *nva);
/*
* Checks header name/value pair is well-formed. This function allows
* captital alphabet letters in name.
*
* This function returns nonzero if it succeeds, or 0.
*/
int nghttp2_nv_array_check_nocase(const nghttp2_nv *nva, size_t nvlen);
/*
* Checks header name/value pair is well-formed. This function does
* not allow captital alphabet letters in name.
*
* This function returns nonzero if it succeeds, or 0.
*/
int nghttp2_nv_array_check(const nghttp2_nv *nva, size_t nvlen);
int nghttp2_nv_check(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen);
/*
* Checks that the |iv|, which includes |niv| entries, does not have
* invalid values. The |flow_control_opt| is current flow control
......
......@@ -154,183 +154,6 @@ int nghttp2_should_send_window_update(int32_t local_window_size,
return recv_window_size >= local_window_size / 2;
}
static int VALID_HD_NAME_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */,
0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */,
0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */,
0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */,
0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */,
0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */,
0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
};
static int check_header_name(const uint8_t *name, size_t len, int nocase)
{
const uint8_t *last;
if(len == 0) {
return 0;
}
if(*name == ':') {
if(len == 1) {
return 0;
}
++name;
--len;
}
for(last = name + len; name != last; ++name) {
if(nocase && 'A' <= *name && *name <= 'Z') continue;
if(!VALID_HD_NAME_CHARS[*name]) {
return 0;
}
}
return 1;
}
int nghttp2_check_header_name(const uint8_t *name, size_t len)
{
return check_header_name(name, len, 0);
}
int nghttp2_check_header_name_nocase(const uint8_t *name, size_t len)
{
return check_header_name(name, len, 1);
}
static int VALID_HD_VALUE_CHARS[] = {
1 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */,
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
1 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
};
int nghttp2_check_header_value(const uint8_t* value, size_t len)
{
const uint8_t *last;
for(last = value + len; value != last; ++value) {
if(!VALID_HD_VALUE_CHARS[*value]) {
return 0;
}
}
return 1;
}
const char* nghttp2_strerror(int error_code)
{
switch(error_code) {
......
......@@ -118,28 +118,6 @@ int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
int nghttp2_should_send_window_update(int32_t local_window_size,
int32_t recv_window_size);
/*
* Checks the header name in |name| with |len| bytes is well-formed.
*
* This function returns nonzero if it succeeds, or 0.
*/
int nghttp2_check_header_name(const uint8_t *name, size_t len);
/*
* Checks the header name in |name| with |len| bytes is
* well-formed. This function accepts also characters in [A-Z].
*
* This function returns nonzero if it succeeds, or 0.
*/
int nghttp2_check_header_name_nocase(const uint8_t *name, size_t len);
/*
* Checks the header value in |value| with |len| bytes is well-formed.
*
* This function returns nonzero if it succeeds, or 0.
*/
int nghttp2_check_header_value(const uint8_t* value, size_t len);
/*
* Deallocates memory space pointed by |ptr|. This function exists for
* the application to free the memory space allocated by the library
......
......@@ -110,9 +110,6 @@ static int nghttp2_submit_headers_shared_nva
{
ssize_t rv;
nghttp2_nv *nva_copy;
if(!nghttp2_nv_array_check_nocase(nva, nvlen)) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen);
if(rv < 0) {
return rv;
......@@ -195,12 +192,6 @@ int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
uint8_t flags_copy;
int rv;
if(!nghttp2_nv_array_check_nocase(nva, nvlen)) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
if(nghttp2_session_get_stream(session, stream_id) == NULL) {
return NGHTTP2_ERR_STREAM_CLOSED;
}
frame = malloc(sizeof(nghttp2_frame));
if(frame == NULL) {
return NGHTTP2_ERR_NOMEM;
......
......@@ -791,6 +791,9 @@ int on_header_callback(nghttp2_session *session,
if(!stream) {
return 0;
}
if(!http2::check_nv(name, namelen, value, valuelen)) {
return 0;
}
http2::split_add_header(stream->headers, name, namelen, value, valuelen);
return 0;
}
......
......@@ -90,7 +90,7 @@ void capitalize(std::string& s, size_t offset)
}
}
bool check_header_value(const char *value)
bool lws(const char *value)
{
for(; *value; ++value) {
switch(*value) {
......@@ -98,10 +98,10 @@ bool check_header_value(const char *value)
case ' ':
continue;
default:
return true;
return false;
}
}
return false;
return true;
}
void sanitize_header_value(std::string& s, size_t offset)
......@@ -535,6 +535,201 @@ std::string rewrite_location_uri(const std::string& uri,
return res;
}
namespace {
int VALID_HD_NAME_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */,
0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */,
0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */,
0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */,
0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */,
0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */,
0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
};
} // namespace
namespace {
int check_header_name(const uint8_t *name, size_t len, int nocase)
{
const uint8_t *last;
if(len == 0) {
return 0;
}
if(*name == ':') {
if(len == 1) {
return 0;
}
++name;
--len;
}
for(last = name + len; name != last; ++name) {
if(nocase && 'A' <= *name && *name <= 'Z') continue;
if(!VALID_HD_NAME_CHARS[*name]) {
return 0;
}
}
return 1;
}
} // namespace
int check_header_name(const uint8_t *name, size_t len)
{
return check_header_name(name, len, 0);
}
int check_header_name_nocase(const uint8_t *name, size_t len)
{
return check_header_name(name, len, 1);
}
namespace {
int VALID_HD_VALUE_CHARS[] = {
1 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */,
0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
1 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
};
} // namespace
int check_header_value(const uint8_t* value, size_t len)
{
const uint8_t *last;
for(last = value + len; value != last; ++value) {
if(!VALID_HD_VALUE_CHARS[*value]) {
return 0;
}
}
return 1;
}
int check_nv(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen)
{
if(!check_header_name(name, namelen)) {
return 0;
}
if(!check_header_value(value, valuelen)) {
return 0;
}
return 1;
}
} // namespace http2
} // namespace nghttp2
......@@ -46,8 +46,8 @@ std::string get_status_string(unsigned int status_code);
void capitalize(std::string& s, size_t offset);
// Returns false if |value| is LWS
bool check_header_value(const char *value);
// Returns true if |value| is LWS
bool lws(const char *value);
void sanitize_header_value(std::string& s, size_t offset);
......@@ -197,6 +197,30 @@ std::string rewrite_location_uri(const std::string& uri,
const std::string& upstream_scheme,
uint16_t upstream_port);
// Checks the header name in |name| with |len| bytes is well-formed.
//
// This function returns nonzero if it succeeds, or 0.
int check_header_name(const uint8_t *name, size_t len);
// Checks the header name in |name| with |len| bytes is
// well-formed. This function accepts also characters in [A-Z].
//
// This function returns nonzero if it succeeds, or 0.
int check_header_name_nocase(const uint8_t *name, size_t len);
// Checks the header value in |value| with |len| bytes is well-formed.
//
// This function returns nonzero if it succeeds, or 0.
int check_header_value(const uint8_t* value, size_t len);
// Checks the header name/value pair using check_header_name() and
// check_header_value(). If both function returns nonzero, this
// function returns nonzero.
int check_nv(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen);
} // namespace http2
} // namespace nghttp2
......
......@@ -244,11 +244,11 @@ void test_http2_build_http1_headers_from_norm_headers(void)
// CU_ASSERT(hdrs == "Alpha: bravo charlie \r\n");
}
void test_http2_check_header_value(void)
void test_http2_lws(void)
{
CU_ASSERT(http2::check_header_value("alpha"));
CU_ASSERT(!http2::check_header_value(" "));
CU_ASSERT(!http2::check_header_value(""));
CU_ASSERT(!http2::lws("alpha"));
CU_ASSERT(http2::lws(" "));
CU_ASSERT(http2::lws(""));
}
namespace {
......@@ -297,4 +297,52 @@ void test_http2_rewrite_location_uri(void)
"localhost", "https", 3000);
}
namespace {
int check_header_name(const char *s)
{
return http2::check_header_name((const uint8_t*)s, strlen(s));
}
} // namespace
namespace {
int check_header_name_nocase(const char *s)
{
return http2::check_header_name_nocase((const uint8_t*)s, strlen(s));
}
} // namespace
void test_http2_check_header_name(void)
{
CU_ASSERT(check_header_name(":path"));
CU_ASSERT(check_header_name("path"));
CU_ASSERT(check_header_name("!#$%&'*+-.^_`|~"));
CU_ASSERT(!check_header_name(":PATH"));
CU_ASSERT(!check_header_name("path:"));
CU_ASSERT(!check_header_name(""));
CU_ASSERT(!check_header_name(":"));
CU_ASSERT(check_header_name_nocase(":path"));
CU_ASSERT(check_header_name_nocase("path"));
CU_ASSERT(check_header_name_nocase("!#$%&'*+-.^_`|~"));
CU_ASSERT(check_header_name_nocase(":PATH"));
CU_ASSERT(!check_header_name_nocase("path:"));
CU_ASSERT(!check_header_name_nocase(""));
CU_ASSERT(!check_header_name_nocase(":"));
}
#define check_header_value(S) \
http2::check_header_value((const uint8_t*)S, sizeof(S) - 1)
void test_http2_check_header_value(void)
{
uint8_t goodval[] = { 'a', '\0', 'b', 0x80u, 'c', 0xffu, 'd', '\t', ' ' };
uint8_t badval1[] = { 'a', 0x1fu, 'b' };
uint8_t badval2[] = { 'a', 0x7fu, 'b' };
CU_ASSERT(check_header_value(" !|}~"));
CU_ASSERT(check_header_value(goodval));
CU_ASSERT(!check_header_value(badval1));
CU_ASSERT(!check_header_value(badval2));
}
} // namespace shrpx
......@@ -36,8 +36,10 @@ void test_http2_value_lws(void);
void test_http2_concat_norm_headers(void);
void test_http2_copy_norm_headers_to_nva(void);
void test_http2_build_http1_headers_from_norm_headers(void);
void test_http2_check_header_value(void);
void test_http2_lws(void);
void test_http2_rewrite_location_uri(void);
void test_http2_check_header_name(void);
void test_http2_check_header_value(void);
} // namespace shrpx
......
......@@ -87,10 +87,14 @@ int main(int argc, char* argv[])
shrpx::test_http2_copy_norm_headers_to_nva) ||
!CU_add_test(pSuite, "http2_build_http1_headers_from_norm_headers",
shrpx::test_http2_build_http1_headers_from_norm_headers) ||
!CU_add_test(pSuite, "http2_check_header_value",
shrpx::test_http2_check_header_value) ||
!CU_add_test(pSuite, "http2_lws",
shrpx::test_http2_lws) ||
!CU_add_test(pSuite, "http2_rewrite_location_uri",
shrpx::test_http2_rewrite_location_uri) ||
!CU_add_test(pSuite, "http2_check_header_name",
shrpx::test_http2_check_header_name) ||
!CU_add_test(pSuite, "http2_check_header_value",
shrpx::test_http2_check_header_value) ||
!CU_add_test(pSuite, "downstream_normalize_request_headers",
shrpx::test_downstream_normalize_request_headers) ||
!CU_add_test(pSuite, "downstream_normalize_response_headers",
......
......@@ -816,7 +816,9 @@ int on_header_callback(nghttp2_session *session,
if(!downstream) {
return 0;
}
// TODO Discard malformed header here
if(!http2::check_nv(name, namelen, value, valuelen)) {
return 0;
}
downstream->split_add_response_header(name, namelen, value, valuelen);
return 0;
}
......
......@@ -233,7 +233,9 @@ int on_header_callback(nghttp2_session *session,
if(!downstream) {
return 0;
}
// TODO Discard malformed header here
if(!http2::check_nv(name, namelen, value, valuelen)) {
return 0;
}
downstream->split_add_request_header(name, namelen, value, valuelen);
return 0;
}
......
......@@ -179,10 +179,8 @@ void on_ctrl_recv_callback
}
bool is_connect = method && strcmp("CONNECT", method) == 0;
if(!path || !host || !method ||
!http2::check_header_value(host) ||
!http2::check_header_value(path) ||
!http2::check_header_value(method) ||
(!is_connect && (!scheme || !http2::check_header_value(scheme)))) {
http2::lws(host) || http2::lws(path) || http2::lws(method) ||
(!is_connect && (!scheme || http2::lws(scheme)))) {
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
return;
}
......
......@@ -215,8 +215,6 @@ int main(int argc, char* argv[])
test_nghttp2_frame_pack_goaway) ||
!CU_add_test(pSuite, "frame_pack_window_update",
test_nghttp2_frame_pack_window_update) ||
!CU_add_test(pSuite, "nv_array_check",
test_nghttp2_nv_array_check) ||
!CU_add_test(pSuite, "nv_array_copy", test_nghttp2_nv_array_copy) ||
!CU_add_test(pSuite, "iv_check", test_nghttp2_iv_check) ||
!CU_add_test(pSuite, "hd_deflate", test_nghttp2_hd_deflate) ||
......@@ -248,11 +246,7 @@ int main(int argc, char* argv[])
test_nghttp2_hd_deflate_inflate) ||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
!CU_add_test(pSuite, "adjust_local_window_size",
test_nghttp2_adjust_local_window_size) ||
!CU_add_test(pSuite, "check_header_name",
test_nghttp2_check_header_name) ||
!CU_add_test(pSuite, "check_header_value",
test_nghttp2_check_header_value)
test_nghttp2_adjust_local_window_size)
) {
CU_cleanup_registry();
return CU_get_error();
......
......@@ -370,39 +370,6 @@ void test_nghttp2_frame_pack_window_update(void)
nghttp2_frame_window_update_free(&frame);
}
void test_nghttp2_nv_array_check(void)
{
nghttp2_nv nva1[] = {MAKE_NV("path", "/"),
MAKE_NV("host", "a")};
nghttp2_nv nva2[] = {MAKE_NV("", "/"),
MAKE_NV("host", "a")};
nghttp2_nv nva3[] = {MAKE_NV("path", "/"),
MAKE_NV("host\x01", "a")};
nghttp2_nv nva4[] = {MAKE_NV("PATH", "/")};
nghttp2_nv nva5[] = {MAKE_NV("path", "foo"),
MAKE_NV("host", "foo\x00")};
nghttp2_nv nva6[] = {MAKE_NV("path", "foo"),
MAKE_NV("host", "foo\x01")};
CU_ASSERT(nghttp2_nv_array_check(nva1, ARRLEN(nva1)));
CU_ASSERT(nghttp2_nv_array_check_nocase(nva1, ARRLEN(nva1)));
CU_ASSERT(0 == nghttp2_nv_array_check(nva2, ARRLEN(nva2)));
CU_ASSERT(0 == nghttp2_nv_array_check_nocase(nva2, ARRLEN(nva2)));
CU_ASSERT(0 == nghttp2_nv_array_check(nva3, ARRLEN(nva3)));
CU_ASSERT(0 == nghttp2_nv_array_check_nocase(nva3, ARRLEN(nva3)));
CU_ASSERT(0 == nghttp2_nv_array_check(nva4, ARRLEN(nva4)));
CU_ASSERT(nghttp2_nv_array_check_nocase(nva4, ARRLEN(nva4)));
CU_ASSERT(nghttp2_nv_array_check(nva5, ARRLEN(nva5)));
CU_ASSERT(nghttp2_nv_array_check_nocase(nva5, ARRLEN(nva5)));
CU_ASSERT(0 == nghttp2_nv_array_check(nva6, ARRLEN(nva6)));
CU_ASSERT(0 == nghttp2_nv_array_check_nocase(nva6, ARRLEN(nva6)));
}
void test_nghttp2_nv_array_copy(void)
{
nghttp2_nv *nva;
......
......@@ -34,7 +34,6 @@ void test_nghttp2_frame_pack_push_promise(void);
void test_nghttp2_frame_pack_ping(void);
void test_nghttp2_frame_pack_goaway(void);
void test_nghttp2_frame_pack_window_update(void);
void test_nghttp2_nv_array_check(void);
void test_nghttp2_nv_array_copy(void);
void test_nghttp2_iv_check(void);
......
......@@ -152,47 +152,3 @@ void test_nghttp2_adjust_local_window_size(void)
CU_ASSERT(0 == recv_reduction);
CU_ASSERT(INT32_MIN == delta);
}
static int check_header_name(const char *s)
{
return nghttp2_check_header_name((const uint8_t*)s, strlen(s));
}
static int check_header_name_nocase(const char *s)
{
return nghttp2_check_header_name_nocase((const uint8_t*)s, strlen(s));
}
void test_nghttp2_check_header_name(void)
{
CU_ASSERT(check_header_name(":path"));
CU_ASSERT(check_header_name("path"));
CU_ASSERT(check_header_name("!#$%&'*+-.^_`|~"));
CU_ASSERT(!check_header_name(":PATH"));
CU_ASSERT(!check_header_name("path:"));
CU_ASSERT(!check_header_name(""));
CU_ASSERT(!check_header_name(":"));
CU_ASSERT(check_header_name_nocase(":path"));
CU_ASSERT(check_header_name_nocase("path"));
CU_ASSERT(check_header_name_nocase("!#$%&'*+-.^_`|~"));
CU_ASSERT(check_header_name_nocase(":PATH"));
CU_ASSERT(!check_header_name_nocase("path:"));
CU_ASSERT(!check_header_name_nocase(""));
CU_ASSERT(!check_header_name_nocase(":"));
}
#define check_header_value(S) \
nghttp2_check_header_value((const uint8_t*)S, sizeof(S) - 1)
void test_nghttp2_check_header_value(void)
{
uint8_t goodval[] = { 'a', '\0', 'b', 0x80u, 'c', 0xffu, 'd', '\t', ' ' };
uint8_t badval1[] = { 'a', 0x1fu, 'b' };
uint8_t badval2[] = { 'a', 0x7fu, 'b' };
CU_ASSERT(check_header_value(" !|}~"));
CU_ASSERT(check_header_value(goodval));
CU_ASSERT(!check_header_value(badval1));
CU_ASSERT(!check_header_value(badval2));
}
......@@ -26,7 +26,5 @@
#define NGHTTP2_HELPER_TEST_H
void test_nghttp2_adjust_local_window_size(void);
void test_nghttp2_check_header_name(void);
void test_nghttp2_check_header_value(void);
#endif /* NGHTTP2_HELPER_TEST_H */
......@@ -2670,31 +2670,34 @@ void test_nghttp2_submit_invalid_nv(void)
MAKE_NV("", "empty name")
};
/* Now invalid header name/value pair in HTTP/1.1 is accepted in
nghttp2 */
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL));
/* nghttp2_submit_request */
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
CU_ASSERT(0 ==
nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT,
empty_name_nv, ARRLEN(empty_name_nv),
NULL, NULL));
/* nghttp2_submit_response */
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
CU_ASSERT(0 ==
nghttp2_submit_response(session, 2,
empty_name_nv, ARRLEN(empty_name_nv),
NULL));
/* nghttp2_submit_headers */
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
CU_ASSERT(0 ==
nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1,
NGHTTP2_PRI_DEFAULT,
empty_name_nv, ARRLEN(empty_name_nv),
NULL));
/* nghttp2_submit_push_promise */
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
CU_ASSERT(0 ==
nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 2,
empty_name_nv, ARRLEN(empty_name_nv)));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment