diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index 9758e5ad..f1003435 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -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 diff --git a/lib/spdylay_frame.c b/lib/spdylay_frame.c index ab9ab176..767f44d9 100644 --- a/lib/spdylay_frame.c +++ b/lib/spdylay_frame.c @@ -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, diff --git a/lib/spdylay_frame.h b/lib/spdylay_frame.h index 03e0dc56..b03467a5 100644 --- a/lib/spdylay_frame.h +++ b/lib/spdylay_frame.h @@ -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 diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index 471edb9f..f4640289 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -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); } diff --git a/tests/main.c b/tests/main.c index 924f9029..2162192c 100644 --- a/tests/main.c +++ b/tests/main.c @@ -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(); diff --git a/tests/spdylay_frame_test.c b/tests/spdylay_frame_test.c index 66063954..874fa76e 100644 --- a/tests/spdylay_frame_test.c +++ b/tests/spdylay_frame_test.c @@ -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)); +} diff --git a/tests/spdylay_frame_test.h b/tests/spdylay_frame_test.h index da8fb1fb..10a402a4 100644 --- a/tests/spdylay_frame_test.h +++ b/tests/spdylay_frame_test.h @@ -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 */ diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index 83f060ae..88c1898f 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -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); }