mirror of
https://github.com/moparisthebest/spdylay
synced 2024-12-22 07:38:52 -05:00
Issue RST_STREAM with PROTOCOL_ERROR if invalid header block is received.
We say the header block is invalid if at least one of the following condition is true: There are duplicate header names; or the header names are not encoded in US-ASCII character set and not lower cased; or the header name is zero-length string; or the header value contains multiple in-sequence NUL bytes. spdylay_frame_unpack_nv() returns SPDYLAY_ERR_INVALID_HEADER_BLOCK if the unpacking suceeded but it found the header block is invalid. This means that caller treats it as success, but do additional processing for invalid header block if it wants. The functions calling spdylay_frame_unpack_nv() also return SPDYLAY_ERR_INVALID_HEADER_BLOCK.
This commit is contained in:
parent
5deef03687
commit
262cda86e8
@ -143,6 +143,14 @@ typedef enum {
|
||||
* GOAWAY has already been sent.
|
||||
*/
|
||||
SPDYLAY_ERR_GOAWAY_ALREADY_SENT = -517,
|
||||
/**
|
||||
* The received frame contains the invalid header block. (e.g.,
|
||||
* There are duplicate header names; or the header names are not
|
||||
* encoded in US-ASCII character set and not lower cased; or the
|
||||
* header name is zero-length string; or the header value contains
|
||||
* multiple in-sequence NUL bytes).
|
||||
*/
|
||||
SPDYLAY_ERR_INVALID_HEADER_BLOCK = -518,
|
||||
/**
|
||||
* The errors < :enum:`SPDYLAY_ERR_FATAL` mean that the library is
|
||||
* under unexpected condition and cannot process any further data
|
||||
|
@ -31,17 +31,7 @@
|
||||
#include "spdylay_helper.h"
|
||||
#include "spdylay_net.h"
|
||||
|
||||
#define spdylay_frame_get_nv_len(IN, LEN_SIZE) \
|
||||
(LEN_SIZE == 2 ? spdylay_get_uint16(IN) : spdylay_get_uint32(IN))
|
||||
|
||||
#define spdylay_frame_put_nv_len(OUT, VAL, LEN_SIZE) \
|
||||
(LEN_SIZE == 2 ? \
|
||||
spdylay_put_uint16be(OUT, VAL) : spdylay_put_uint32be(OUT, VAL))
|
||||
|
||||
/* Returns the number of bytes in length of name/value pair for the
|
||||
given protocol version |version|. If |version| is not supported,
|
||||
returns 0. */
|
||||
static size_t spdylay_frame_get_len_size(uint16_t version)
|
||||
size_t spdylay_frame_get_len_size(uint16_t version)
|
||||
{
|
||||
if(SPDYLAY_PROTO_SPDY2 == version) {
|
||||
return 2;
|
||||
@ -158,6 +148,7 @@ int spdylay_frame_count_unpack_nv_space
|
||||
}
|
||||
if(inlen == off) {
|
||||
*nvlen_ptr = nvlen;
|
||||
|
||||
*buflen_ptr = buflen+(nvlen*2+1)*sizeof(char*);
|
||||
return 0;
|
||||
} else {
|
||||
@ -165,6 +156,83 @@ int spdylay_frame_count_unpack_nv_space
|
||||
}
|
||||
}
|
||||
|
||||
static int spdylay_length_prefix_str_compar2(const void *lhs, const void *rhs)
|
||||
{
|
||||
ssize_t lhslen, rhslen, complen;
|
||||
int r;
|
||||
lhslen = spdylay_get_uint16(*(uint8_t**)lhs);
|
||||
rhslen = spdylay_get_uint16(*(uint8_t**)rhs);
|
||||
complen = spdylay_min(lhslen, rhslen);
|
||||
r = memcmp(*(uint8_t**)lhs+2, *(uint8_t**)rhs+2, complen);
|
||||
if(r == 0) {
|
||||
return lhslen-rhslen;
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
static int spdylay_length_prefix_str_compar4(const void *lhs, const void *rhs)
|
||||
{
|
||||
ssize_t lhslen, rhslen, complen;
|
||||
int r;
|
||||
/* Assuming the returned value does not exceed the maximum value of
|
||||
ssize_t */
|
||||
lhslen = spdylay_get_uint32(*(uint8_t**)lhs);
|
||||
rhslen = spdylay_get_uint32(*(uint8_t**)rhs);
|
||||
complen = spdylay_min(lhslen, rhslen);
|
||||
r = memcmp(*(uint8_t**)lhs+4, *(uint8_t**)rhs+4, complen);
|
||||
if(r == 0) {
|
||||
return lhslen-rhslen;
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
int spdylay_frame_unpack_nv_check_name(uint8_t *buf, size_t buflen,
|
||||
const uint8_t *in, size_t inlen,
|
||||
size_t len_size)
|
||||
{
|
||||
uint32_t n;
|
||||
size_t i;
|
||||
const uint8_t **index;
|
||||
n = spdylay_frame_get_nv_len(in, len_size);
|
||||
assert(n*sizeof(char*) <= buflen);
|
||||
in += len_size;
|
||||
index = (const uint8_t**)buf;
|
||||
for(i = 0; i < n; ++i) {
|
||||
uint32_t len;
|
||||
size_t j;
|
||||
len = spdylay_frame_get_nv_len(in, len_size);
|
||||
if(len == 0) {
|
||||
return SPDYLAY_ERR_INVALID_HEADER_BLOCK;
|
||||
}
|
||||
*index++ = in;
|
||||
in += len_size;
|
||||
for(j = 0; j < len; ++j) {
|
||||
unsigned char c = in[j];
|
||||
if(c < 0x20 || c > 0x7e || ('A' <= c && c <= 'Z')) {
|
||||
return SPDYLAY_ERR_INVALID_HEADER_BLOCK;
|
||||
}
|
||||
}
|
||||
in += len;
|
||||
len = spdylay_frame_get_nv_len(in, len_size);
|
||||
in += len_size+len;
|
||||
}
|
||||
qsort(buf, n, sizeof(uint8_t*),
|
||||
len_size == 2 ?
|
||||
spdylay_length_prefix_str_compar2 : spdylay_length_prefix_str_compar4);
|
||||
index = (const uint8_t**)buf;
|
||||
for(i = 1; i < n; ++i) {
|
||||
uint32_t len1 = spdylay_frame_get_nv_len(*(index+i-1), len_size);
|
||||
uint32_t len2 = spdylay_frame_get_nv_len(*(index+i), len_size);
|
||||
if(len1 == len2 && memcmp(*(index+i-1)+len_size, *(index+i)+len_size,
|
||||
len_size) == 0) {
|
||||
return SPDYLAY_ERR_INVALID_HEADER_BLOCK;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen,
|
||||
size_t len_size)
|
||||
{
|
||||
@ -173,6 +241,7 @@ int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen,
|
||||
size_t i;
|
||||
char *buf, **index, *data;
|
||||
uint32_t n;
|
||||
int invalid_header_block = 0;
|
||||
r = spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, in, inlen, len_size);
|
||||
if(r != 0) {
|
||||
return r;
|
||||
@ -181,6 +250,15 @@ int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen,
|
||||
if(buf == NULL) {
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
r = spdylay_frame_unpack_nv_check_name((uint8_t*)buf, buflen, in, inlen,
|
||||
len_size);
|
||||
if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK) {
|
||||
invalid_header_block = 1;
|
||||
r = 0;
|
||||
} else if(r != 0) {
|
||||
free(buf);
|
||||
return r;
|
||||
}
|
||||
index = (char**)buf;
|
||||
data = buf+(nvlen*2+1)*sizeof(char*);
|
||||
n = spdylay_frame_get_nv_len(in, len_size);
|
||||
@ -207,6 +285,9 @@ int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen,
|
||||
if(*data == '\0') {
|
||||
*index++ = name;
|
||||
*index++ = val;
|
||||
if(val == data) {
|
||||
invalid_header_block = 1;
|
||||
}
|
||||
val = data+1;
|
||||
}
|
||||
}
|
||||
@ -220,7 +301,7 @@ int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen,
|
||||
*index = NULL;
|
||||
assert((size_t)((char*)index - buf) == (nvlen*2)*sizeof(char*));
|
||||
*nv_ptr = (char**)buf;
|
||||
return 0;
|
||||
return invalid_header_block ? SPDYLAY_ERR_INVALID_HEADER_BLOCK : 0;
|
||||
}
|
||||
|
||||
int spdylay_frame_alloc_unpack_nv(char ***nv_ptr,
|
||||
|
@ -45,6 +45,20 @@
|
||||
/* The number of bytes of frame header. */
|
||||
#define SPDYLAY_FRAME_HEAD_LENGTH 8
|
||||
|
||||
#define spdylay_frame_get_nv_len(IN, LEN_SIZE) \
|
||||
(LEN_SIZE == 2 ? spdylay_get_uint16(IN) : spdylay_get_uint32(IN))
|
||||
|
||||
#define spdylay_frame_put_nv_len(OUT, VAL, LEN_SIZE) \
|
||||
(LEN_SIZE == 2 ? \
|
||||
spdylay_put_uint16be(OUT, VAL) : spdylay_put_uint32be(OUT, VAL))
|
||||
|
||||
/*
|
||||
* Returns the number of bytes in length of name/value pair for the
|
||||
* given protocol version |version|. If |version| is not supported,
|
||||
* returns 0.
|
||||
*/
|
||||
size_t spdylay_frame_get_len_size(uint16_t version);
|
||||
|
||||
/*
|
||||
* Packs SYN_STREAM frame |frame| in wire format and store it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes.
|
||||
@ -87,9 +101,15 @@ ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr,
|
||||
* inflated name/value pairs. This function expands |*nvbuf_ptr| as
|
||||
* necessary and updates these variables.
|
||||
*
|
||||
* This function also validates the name/value pairs. If unpacking
|
||||
* succeeds but validation fails, it is indicated by returning
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK.
|
||||
*
|
||||
* This function returns 0 if it succeeds or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK
|
||||
* Unpacking succeeds but the header block is invalid.
|
||||
* SPDYLAY_ERR_INVALID_FRAME
|
||||
* The input data are invalid.
|
||||
* SPDYLAY_ERR_UNSUPPORTED_VERSION
|
||||
@ -147,9 +167,15 @@ ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr,
|
||||
* inflated name/value pairs. This function expands |*nvbuf_ptr| as
|
||||
* necessary and updates these variables.
|
||||
*
|
||||
* This function also validates the name/value pairs. If unpacking
|
||||
* succeeds but validation fails, it is indicated by returning
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK.
|
||||
*
|
||||
* This function returns 0 if it succeeds or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK
|
||||
* Unpacking succeeds but the header block is invalid.
|
||||
* SPDYLAY_ERR_UNSUPPORTED_VERSION
|
||||
* The version is not supported.
|
||||
* SPDYLAY_ERR_INVALID_FRAME
|
||||
@ -265,9 +291,15 @@ ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
* inflated name/value pairs. This function expands |*nvbuf_ptr| as
|
||||
* necessary and updates these variables.
|
||||
*
|
||||
* This function also validates the name/value pairs. If unpacking
|
||||
* succeeds but validation fails, it is indicated by returning
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK.
|
||||
*
|
||||
* This function returns 0 if it succeeds or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK
|
||||
* Unpacking succeeds but the header block is invalid.
|
||||
* SPDYLAY_ERR_UNSUPPORTED_VERSION
|
||||
* The version is not supported.
|
||||
* SPDYLAY_ERR_INVALID_FRAME
|
||||
@ -453,15 +485,46 @@ int spdylay_frame_count_unpack_nv_space
|
||||
(size_t *num_nv_ptr, size_t *buf_size_ptr, const uint8_t *in, size_t inlen,
|
||||
size_t len_size);
|
||||
|
||||
/*
|
||||
* Validates name of Name/Value header Block. The |buf| is the
|
||||
* allocated buffer with the length at least |buflen| bytes. The
|
||||
* |buflen| must be at least the number of Name/Value pairs in the
|
||||
* packed name/value header block |in|. The length of |in| is given in
|
||||
* |inlen|. The |buf| is used as a work memory to validate header
|
||||
* names and the caller must not use its content on return.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK
|
||||
* There are duplicate header names; or the header names are not
|
||||
* encoded in US-ASCII character set and not lower cased; or the
|
||||
* header name is zero-length string.
|
||||
*/
|
||||
int spdylay_frame_unpack_nv_check_name(uint8_t *buf, size_t buflen,
|
||||
const uint8_t *in, size_t inlen,
|
||||
size_t len_size);
|
||||
|
||||
/*
|
||||
* Unpacks name/value pairs in wire format |in| with length |inlen|
|
||||
* and stores them in |*nv_ptr|. Thif function allocates enough
|
||||
* memory to store name/value pairs in |*nv_ptr|. |len_size| is the
|
||||
* number of bytes in length of name/value pair and it must be 2 or 4.
|
||||
*
|
||||
* This function also validates the name/value pairs. If unpacking
|
||||
* succeeds but validation fails, it is indicated by returning
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK.
|
||||
*
|
||||
* If error other than SPDYLAY_ERR_INVALID_HEADER_BLOCK is returned,
|
||||
* the |nv_ptr| is not assigned. In other words,
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK means unpacking succeeded, but
|
||||
* header block validation failed.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK
|
||||
* Unpacking succeeds but the header block is invalid.
|
||||
* SPDYLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
@ -477,9 +540,20 @@ int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen,
|
||||
* |len_size| is the number of bytes used in name/value length. It
|
||||
* must be either 2 or 4.
|
||||
*
|
||||
* This function also validates the name/value pairs. If unpacking
|
||||
* succeeds but validation fails, it is indicated by returning
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK.
|
||||
*
|
||||
* If error other than SPDYLAY_ERR_INVALID_HEADER_BLOCK is returned,
|
||||
* the |nv_ptr| is not assigned. In other words,
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK means unpacking succeeded, but
|
||||
* header block validation failed.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* SPDYLAY_ERR_INVALID_HEADER_BLOCK
|
||||
* Unpacking succeeds but the header block is invalid.
|
||||
* SPDYLAY_ERR_ZLIB
|
||||
* The inflate operation failed.
|
||||
* SPDYLAY_ERR_NOMEM
|
||||
|
@ -1345,25 +1345,6 @@ static int spdylay_session_check_version(spdylay_session *session,
|
||||
return session->version == version;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns non-zero iff name/value pairs |nv| are good shape.
|
||||
* Currently, we only checks whether names are lower cased. The spdy/2
|
||||
* spec requires that names must be lower cased.
|
||||
*/
|
||||
static int spdylay_session_check_nv(char **nv)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; nv[i]; i += 2) {
|
||||
int j;
|
||||
for(j = 0; nv[i][j] != '\0'; ++j) {
|
||||
if('A' <= nv[i][j] && nv[i][j] <= 'Z') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates SYN_STREAM frame |frame|. This function returns 0 if it
|
||||
* succeeds, or non-zero spdylay_status_code.
|
||||
@ -1400,9 +1381,6 @@ static int spdylay_session_validate_syn_stream(spdylay_session *session,
|
||||
follow it. */
|
||||
return SPDYLAY_REFUSED_STREAM;
|
||||
}
|
||||
if(!spdylay_session_check_nv(frame->nv)) {
|
||||
return SPDYLAY_PROTOCOL_ERROR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1519,8 +1497,7 @@ int spdylay_session_on_syn_reply_received(spdylay_session *session,
|
||||
}
|
||||
if((stream = spdylay_session_get_stream(session,
|
||||
frame->syn_reply.stream_id)) &&
|
||||
(stream->shut_flags & SPDYLAY_SHUT_RD) == 0 &&
|
||||
spdylay_session_check_nv(frame->syn_reply.nv)) {
|
||||
(stream->shut_flags & SPDYLAY_SHUT_RD) == 0) {
|
||||
if(spdylay_session_is_my_stream_id(session, frame->syn_reply.stream_id)) {
|
||||
if(stream->state == SPDYLAY_STREAM_OPENING) {
|
||||
valid = 1;
|
||||
@ -1728,8 +1705,7 @@ int spdylay_session_on_headers_received(spdylay_session *session,
|
||||
}
|
||||
if((stream = spdylay_session_get_stream(session,
|
||||
frame->headers.stream_id)) &&
|
||||
(stream->shut_flags & SPDYLAY_SHUT_RD) == 0 &&
|
||||
spdylay_session_check_nv(frame->headers.nv)) {
|
||||
(stream->shut_flags & SPDYLAY_SHUT_RD) == 0) {
|
||||
if(spdylay_session_is_my_stream_id(session, frame->headers.stream_id)) {
|
||||
if(stream->state == SPDYLAY_STREAM_OPENED) {
|
||||
valid = 1;
|
||||
@ -1800,6 +1776,11 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
|
||||
/* TODO if r indicates mulformed NV pairs (multiple nulls) or
|
||||
invalid frame, send RST_STREAM with PROTOCOL_ERROR. Same for
|
||||
other control frames. */
|
||||
} else if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK) {
|
||||
r = spdylay_session_handle_invalid_stream
|
||||
(session, frame.syn_stream.stream_id, SPDYLAY_SYN_STREAM, &frame,
|
||||
SPDYLAY_PROTOCOL_ERROR);
|
||||
spdylay_frame_syn_stream_free(&frame.syn_stream);
|
||||
} else if(spdylay_is_non_fatal(r)) {
|
||||
r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
|
||||
}
|
||||
@ -1821,6 +1802,11 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
|
||||
}
|
||||
r = spdylay_session_on_syn_reply_received(session, &frame);
|
||||
spdylay_frame_syn_reply_free(&frame.syn_reply);
|
||||
} else if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK) {
|
||||
r = spdylay_session_handle_invalid_stream
|
||||
(session, frame.syn_reply.stream_id, SPDYLAY_SYN_REPLY, &frame,
|
||||
SPDYLAY_PROTOCOL_ERROR);
|
||||
spdylay_frame_syn_reply_free(&frame.syn_reply);
|
||||
} else if(spdylay_is_non_fatal(r)) {
|
||||
r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
|
||||
}
|
||||
@ -1896,6 +1882,11 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
|
||||
}
|
||||
r = spdylay_session_on_headers_received(session, &frame);
|
||||
spdylay_frame_headers_free(&frame.headers);
|
||||
} else if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK) {
|
||||
r = spdylay_session_handle_invalid_stream
|
||||
(session, frame.headers.stream_id, SPDYLAY_HEADERS, &frame,
|
||||
SPDYLAY_PROTOCOL_ERROR);
|
||||
spdylay_frame_headers_free(&frame.headers);
|
||||
} else if(spdylay_is_non_fatal(r)) {
|
||||
r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
|
||||
}
|
||||
|
@ -182,6 +182,10 @@ int main(int argc, char* argv[])
|
||||
test_spdylay_frame_pack_nv_duplicate_keys) ||
|
||||
!CU_add_test(pSuite, "frame_nv_2to3", test_spdylay_frame_nv_2to3) ||
|
||||
!CU_add_test(pSuite, "frame_nv_3to2", test_spdylay_frame_nv_3to2) ||
|
||||
!CU_add_test(pSuite, "frame_unpack_nv_check_name_spdy2",
|
||||
test_spdylay_frame_unpack_nv_check_name_spdy2) ||
|
||||
!CU_add_test(pSuite, "frame_unpack_nv_check_name_spdy3",
|
||||
test_spdylay_frame_unpack_nv_check_name_spdy3) ||
|
||||
!CU_add_test(pSuite, "stream_add_pushed_stream",
|
||||
test_spdylay_stream_add_pushed_stream)) {
|
||||
CU_cleanup_registry();
|
||||
|
@ -36,6 +36,7 @@ static const char *headers[] = {
|
||||
"x-head", "foo",
|
||||
"x-head", "bar",
|
||||
"version", "HTTP/1.1",
|
||||
"empty", "",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -57,17 +58,28 @@ static void test_spdylay_frame_unpack_nv_with(size_t len_size)
|
||||
CU_ASSERT(strcmp("bar", nv[9]) == 0);
|
||||
CU_ASSERT(strcmp("version", nv[10]) == 0);
|
||||
CU_ASSERT(strcmp("HTTP/1.1", nv[11]) == 0);
|
||||
CU_ASSERT(strcmp("empty", nv[12]) == 0);
|
||||
CU_ASSERT(strcmp("", nv[13]) == 0);
|
||||
spdylay_frame_nv_del(nv);
|
||||
|
||||
/* Create in-sequence NUL bytes */
|
||||
memcpy(&out[len_size+len_size+strlen(headers[0])+len_size+
|
||||
strlen(headers[1])-2],
|
||||
"\0\0", 2);
|
||||
CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK ==
|
||||
spdylay_frame_unpack_nv(&nv, out, inlen, len_size));
|
||||
}
|
||||
|
||||
void test_spdylay_frame_unpack_nv_spdy2(void)
|
||||
{
|
||||
test_spdylay_frame_unpack_nv_with(2);
|
||||
test_spdylay_frame_unpack_nv_with
|
||||
(spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY2));
|
||||
}
|
||||
|
||||
void test_spdylay_frame_unpack_nv_spdy3(void)
|
||||
{
|
||||
test_spdylay_frame_unpack_nv_with(4);
|
||||
test_spdylay_frame_unpack_nv_with
|
||||
(spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY2));
|
||||
}
|
||||
|
||||
void test_spdylay_frame_pack_nv_duplicate_keys(void)
|
||||
@ -160,9 +172,9 @@ void test_spdylay_frame_pack_nv_duplicate_keys(void)
|
||||
void test_spdylay_frame_count_nv_space(void)
|
||||
{
|
||||
size_t len_size = 2;
|
||||
CU_ASSERT(74 == spdylay_frame_count_nv_space((char**)headers, len_size));
|
||||
CU_ASSERT(83 == spdylay_frame_count_nv_space((char**)headers, len_size));
|
||||
len_size = 4;
|
||||
CU_ASSERT(96 == spdylay_frame_count_nv_space((char**)headers, len_size));
|
||||
CU_ASSERT(109 == spdylay_frame_count_nv_space((char**)headers, len_size));
|
||||
}
|
||||
|
||||
void test_spdylay_frame_count_unpack_nv_space(void)
|
||||
@ -172,10 +184,12 @@ void test_spdylay_frame_count_unpack_nv_space(void)
|
||||
size_t len_size = 2;
|
||||
size_t inlen = spdylay_frame_pack_nv(out, (char**)headers, len_size);
|
||||
uint16_t temp;
|
||||
size_t expected_buflen;
|
||||
CU_ASSERT(0 == spdylay_frame_count_unpack_nv_space(&nvlen, &buflen,
|
||||
out, inlen, len_size));
|
||||
CU_ASSERT(6 == nvlen);
|
||||
CU_ASSERT(166 == buflen);
|
||||
CU_ASSERT(7 == nvlen);
|
||||
expected_buflen = 69+(nvlen*2+1)*sizeof(char*);
|
||||
CU_ASSERT(expected_buflen == buflen);
|
||||
|
||||
/* Trailing garbage */
|
||||
CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME ==
|
||||
@ -294,7 +308,7 @@ static void test_spdylay_frame_pack_syn_stream_version(uint16_t version)
|
||||
CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length);
|
||||
CU_ASSERT(strcmp("method", oframe.syn_stream.nv[0]) == 0);
|
||||
CU_ASSERT(strcmp("GET", oframe.syn_stream.nv[1]) == 0);
|
||||
CU_ASSERT(NULL == oframe.syn_stream.nv[12]);
|
||||
CU_ASSERT(NULL == oframe.syn_stream.nv[14]);
|
||||
free(buf);
|
||||
free(nvbuf);
|
||||
spdylay_frame_syn_stream_free(&oframe.syn_stream);
|
||||
@ -346,7 +360,7 @@ static void test_spdylay_frame_pack_syn_reply_version(uint16_t version)
|
||||
CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length);
|
||||
CU_ASSERT(strcmp("method", oframe.syn_reply.nv[0]) == 0);
|
||||
CU_ASSERT(strcmp("GET", oframe.syn_reply.nv[1]) == 0);
|
||||
CU_ASSERT(NULL == oframe.syn_reply.nv[12]);
|
||||
CU_ASSERT(NULL == oframe.syn_reply.nv[14]);
|
||||
free(buf);
|
||||
free(nvbuf);
|
||||
spdylay_frame_syn_reply_free(&oframe.syn_reply);
|
||||
@ -398,7 +412,7 @@ static void test_spdylay_frame_pack_headers_version(uint16_t version)
|
||||
CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length);
|
||||
CU_ASSERT(strcmp("method", oframe.headers.nv[0]) == 0);
|
||||
CU_ASSERT(strcmp("GET", oframe.headers.nv[1]) == 0);
|
||||
CU_ASSERT(NULL == oframe.headers.nv[12]);
|
||||
CU_ASSERT(NULL == oframe.headers.nv[14]);
|
||||
free(buf);
|
||||
free(nvbuf);
|
||||
spdylay_frame_headers_free(&oframe.headers);
|
||||
@ -587,3 +601,71 @@ void test_spdylay_frame_nv_3to2(void)
|
||||
CU_ASSERT(0 == strcmp("version", nv[12]));
|
||||
spdylay_frame_nv_del(nv);
|
||||
}
|
||||
|
||||
static size_t spdylay_pack_nv(uint8_t *buf, size_t len, const char **nv,
|
||||
size_t len_size)
|
||||
{
|
||||
size_t i, n;
|
||||
uint8_t *buf_ptr;
|
||||
buf_ptr = buf;
|
||||
for(n = 0; nv[n]; ++n);
|
||||
spdylay_frame_put_nv_len(buf_ptr, n/2, len_size);
|
||||
buf_ptr += len_size;
|
||||
for(i = 0; i < n; ++i) {
|
||||
size_t len = strlen(nv[i]);
|
||||
spdylay_frame_put_nv_len(buf_ptr, len, len_size);
|
||||
buf_ptr += len_size;
|
||||
memcpy(buf_ptr, nv[i], len);
|
||||
buf_ptr += len;
|
||||
}
|
||||
return buf_ptr-buf;
|
||||
}
|
||||
|
||||
static const char *empty_name_headers[] = {
|
||||
"method", "GET",
|
||||
"", "https",
|
||||
"url", "/",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char non_ascii_header_name[] = { (char)0xff };
|
||||
|
||||
static const char *non_ascii_headers[] = {
|
||||
non_ascii_header_name, "foo",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void test_spdylay_frame_unpack_nv_check_name_with(size_t len_size)
|
||||
{
|
||||
uint8_t nvbuf[1024], buf[1024];
|
||||
size_t nvbuflen;
|
||||
|
||||
nvbuflen = spdylay_pack_nv(nvbuf, sizeof(nvbuf), headers, len_size);
|
||||
CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK ==
|
||||
spdylay_frame_unpack_nv_check_name(buf, sizeof(buf),
|
||||
nvbuf, nvbuflen, len_size));
|
||||
|
||||
nvbuflen = spdylay_pack_nv(nvbuf, sizeof(nvbuf), empty_name_headers,
|
||||
len_size);
|
||||
CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK ==
|
||||
spdylay_frame_unpack_nv_check_name(buf, sizeof(buf),
|
||||
nvbuf, nvbuflen, len_size));
|
||||
|
||||
nvbuflen = spdylay_pack_nv(nvbuf, sizeof(nvbuf), non_ascii_headers,
|
||||
len_size);
|
||||
CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK ==
|
||||
spdylay_frame_unpack_nv_check_name(buf, sizeof(buf),
|
||||
nvbuf, nvbuflen, len_size));
|
||||
}
|
||||
|
||||
void test_spdylay_frame_unpack_nv_check_name_spdy2(void)
|
||||
{
|
||||
test_spdylay_frame_unpack_nv_check_name_with
|
||||
(spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY2));
|
||||
}
|
||||
|
||||
void test_spdylay_frame_unpack_nv_check_name_spdy3(void)
|
||||
{
|
||||
test_spdylay_frame_unpack_nv_check_name_with
|
||||
(spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY3));
|
||||
}
|
||||
|
@ -46,5 +46,7 @@ void test_spdylay_frame_nv_sort(void);
|
||||
void test_spdylay_frame_nv_downcase(void);
|
||||
void test_spdylay_frame_nv_2to3(void);
|
||||
void test_spdylay_frame_nv_3to2(void);
|
||||
void test_spdylay_frame_unpack_nv_check_name_spdy2(void);
|
||||
void test_spdylay_frame_unpack_nv_check_name_spdy3(void);
|
||||
|
||||
#endif /* SPDYLAY_FRAME_TEST_H */
|
||||
|
@ -209,13 +209,18 @@ void test_spdylay_session_recv(void)
|
||||
const char *nv[] = {
|
||||
"url", "/", NULL
|
||||
};
|
||||
const char *upcase_nv[] = {
|
||||
"URL", "/", NULL
|
||||
};
|
||||
uint8_t *framedata = NULL, *nvbuf = NULL;
|
||||
size_t framedatalen = 0, nvbuflen = 0;
|
||||
ssize_t framelen;
|
||||
spdylay_frame frame;
|
||||
int i;
|
||||
spdylay_outbound_item *item;
|
||||
|
||||
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
|
||||
callbacks.send_callback = null_send_callback;
|
||||
callbacks.recv_callback = scripted_recv_callback;
|
||||
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;
|
||||
user_data.df = &df;
|
||||
@ -233,8 +238,6 @@ void test_spdylay_session_recv(void)
|
||||
for(i = 0; i < framelen; ++i) {
|
||||
df.feedseq[i] = 1;
|
||||
}
|
||||
free(framedata);
|
||||
free(nvbuf);
|
||||
spdylay_frame_syn_stream_free(&frame.syn_stream);
|
||||
|
||||
user_data.ctrl_recv_cb_called = 0;
|
||||
@ -242,6 +245,68 @@ void test_spdylay_session_recv(void)
|
||||
CU_ASSERT(0 == spdylay_session_recv(session));
|
||||
}
|
||||
CU_ASSERT(1 == user_data.ctrl_recv_cb_called);
|
||||
|
||||
/* Receive SYN_STREAM with invalid header block */
|
||||
spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,
|
||||
SPDYLAY_CTRL_FLAG_NONE,
|
||||
3, 0, 3, dup_nv(upcase_nv));
|
||||
framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen,
|
||||
&nvbuf, &nvbuflen,
|
||||
&frame.syn_stream,
|
||||
&session->hd_deflater);
|
||||
spdylay_frame_syn_stream_free(&frame.syn_stream);
|
||||
scripted_data_feed_init(&df, framedata, framelen);
|
||||
user_data.ctrl_recv_cb_called = 0;
|
||||
CU_ASSERT(0 == spdylay_session_recv(session));
|
||||
CU_ASSERT(0 == user_data.ctrl_recv_cb_called);
|
||||
item = spdylay_session_get_next_ob_item(session);
|
||||
CU_ASSERT(SPDYLAY_RST_STREAM == item->frame_type);
|
||||
CU_ASSERT(SPDYLAY_PROTOCOL_ERROR == item->frame->rst_stream.status_code);
|
||||
|
||||
spdylay_session_del(session);
|
||||
|
||||
spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,
|
||||
&user_data);
|
||||
/* Receive SYN_REPLY with invalid header block */
|
||||
spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3,
|
||||
SPDYLAY_STREAM_OPENING, NULL);
|
||||
spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY2,
|
||||
SPDYLAY_CTRL_FLAG_NONE, 1, dup_nv(upcase_nv));
|
||||
framelen = spdylay_frame_pack_syn_reply(&framedata, &framedatalen,
|
||||
&nvbuf, &nvbuflen,
|
||||
&frame.syn_reply,
|
||||
&session->hd_deflater);
|
||||
spdylay_frame_syn_reply_free(&frame.syn_reply);
|
||||
scripted_data_feed_init(&df, framedata, framelen);
|
||||
user_data.ctrl_recv_cb_called = 0;
|
||||
CU_ASSERT(0 == spdylay_session_recv(session));
|
||||
CU_ASSERT(0 == user_data.ctrl_recv_cb_called);
|
||||
item = spdylay_session_get_next_ob_item(session);
|
||||
CU_ASSERT(SPDYLAY_RST_STREAM == item->frame_type);
|
||||
CU_ASSERT(SPDYLAY_PROTOCOL_ERROR == item->frame->rst_stream.status_code);
|
||||
|
||||
CU_ASSERT(0 == spdylay_session_send(session));
|
||||
|
||||
/* Receive HEADERS with invalid header block */
|
||||
spdylay_session_open_stream(session, 3, SPDYLAY_CTRL_FLAG_NONE, 3,
|
||||
SPDYLAY_STREAM_OPENED, NULL);
|
||||
spdylay_frame_headers_init(&frame.headers, SPDYLAY_PROTO_SPDY2,
|
||||
SPDYLAY_CTRL_FLAG_NONE, 3, dup_nv(upcase_nv));
|
||||
framelen = spdylay_frame_pack_headers(&framedata, &framedatalen,
|
||||
&nvbuf, &nvbuflen,
|
||||
&frame.headers,
|
||||
&session->hd_deflater);
|
||||
spdylay_frame_headers_free(&frame.headers);
|
||||
scripted_data_feed_init(&df, framedata, framelen);
|
||||
user_data.ctrl_recv_cb_called = 0;
|
||||
CU_ASSERT(0 == spdylay_session_recv(session));
|
||||
CU_ASSERT(0 == user_data.ctrl_recv_cb_called);
|
||||
item = spdylay_session_get_next_ob_item(session);
|
||||
CU_ASSERT(SPDYLAY_RST_STREAM == item->frame_type);
|
||||
CU_ASSERT(SPDYLAY_PROTOCOL_ERROR == item->frame->rst_stream.status_code);
|
||||
|
||||
free(framedata);
|
||||
free(nvbuf);
|
||||
spdylay_session_del(session);
|
||||
}
|
||||
|
||||
@ -351,7 +416,6 @@ void test_spdylay_session_on_syn_stream_received(void)
|
||||
spdylay_session_callbacks callbacks;
|
||||
my_user_data user_data;
|
||||
const char *nv[] = { NULL };
|
||||
const char *upcase_nv[] = { "version", "http/1.1", "methoD", "get", NULL };
|
||||
spdylay_frame frame;
|
||||
spdylay_stream *stream;
|
||||
int32_t stream_id = 1;
|
||||
@ -389,21 +453,11 @@ void test_spdylay_session_on_syn_stream_received(void)
|
||||
|
||||
spdylay_frame_syn_stream_free(&frame.syn_stream);
|
||||
|
||||
/* Upper cased name/value pairs */
|
||||
spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,
|
||||
SPDYLAY_CTRL_FLAG_NONE,
|
||||
5, 0, 3, dup_nv(upcase_nv));
|
||||
user_data.invalid_ctrl_recv_cb_called = 0;
|
||||
CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));
|
||||
CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);
|
||||
|
||||
spdylay_frame_syn_stream_free(&frame.syn_stream);
|
||||
|
||||
/* Stream ID less than previouly received SYN_STREAM leads session
|
||||
error */
|
||||
spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,
|
||||
SPDYLAY_CTRL_FLAG_NONE,
|
||||
3, 0, 3, dup_nv(nv));
|
||||
1, 0, 3, dup_nv(nv));
|
||||
user_data.invalid_ctrl_recv_cb_called = 0;
|
||||
CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));
|
||||
CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);
|
||||
@ -473,7 +527,6 @@ void test_spdylay_session_on_syn_reply_received(void)
|
||||
spdylay_session_callbacks callbacks;
|
||||
my_user_data user_data;
|
||||
const char *nv[] = { NULL };
|
||||
const char *upcase_nv[] = { "version", "http/1.1", "methoD", "get", NULL };
|
||||
spdylay_frame frame;
|
||||
spdylay_stream *stream;
|
||||
spdylay_outbound_item *item;
|
||||
@ -512,16 +565,6 @@ void test_spdylay_session_on_syn_reply_received(void)
|
||||
|
||||
spdylay_frame_syn_reply_free(&frame.syn_reply);
|
||||
|
||||
/* Upper cased name/value pairs */
|
||||
spdylay_session_open_stream(session, 5, SPDYLAY_CTRL_FLAG_NONE, 0,
|
||||
SPDYLAY_STREAM_OPENING, NULL);
|
||||
spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY2,
|
||||
SPDYLAY_CTRL_FLAG_NONE, 5, dup_nv(upcase_nv));
|
||||
CU_ASSERT(0 == spdylay_session_on_syn_reply_received(session, &frame));
|
||||
CU_ASSERT(3 == user_data.invalid_ctrl_recv_cb_called);
|
||||
|
||||
spdylay_frame_syn_reply_free(&frame.syn_reply);
|
||||
|
||||
spdylay_session_del(session);
|
||||
|
||||
spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks,
|
||||
@ -877,7 +920,6 @@ void test_spdylay_session_on_headers_received(void)
|
||||
spdylay_session_callbacks callbacks;
|
||||
my_user_data user_data;
|
||||
const char *nv[] = { NULL };
|
||||
const char *upcase_nv[] = { "version", "http/1.1", "methoD", "get", NULL };
|
||||
spdylay_frame frame;
|
||||
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
|
||||
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;
|
||||
@ -937,17 +979,6 @@ void test_spdylay_session_on_headers_received(void)
|
||||
|
||||
spdylay_frame_headers_free(&frame.headers);
|
||||
|
||||
/* Upper cased name/value pairs */
|
||||
spdylay_session_open_stream(session, 5, SPDYLAY_CTRL_FLAG_NONE, 0,
|
||||
SPDYLAY_STREAM_OPENED, NULL);
|
||||
spdylay_frame_headers_init(&frame.headers, SPDYLAY_PROTO_SPDY2,
|
||||
SPDYLAY_CTRL_FLAG_NONE, 5, dup_nv(upcase_nv));
|
||||
|
||||
CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame));
|
||||
CU_ASSERT(3 == user_data.invalid_ctrl_recv_cb_called);
|
||||
|
||||
spdylay_frame_headers_free(&frame.headers);
|
||||
|
||||
spdylay_session_del(session);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user