From 4dd9c32c254c6e071456510392eb8b764283a82c Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Fri, 25 May 2012 13:49:18 +0900 Subject: [PATCH] Added SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER option. This option sets maximum receive buffer size for incoming control frame. Basically the library checks the length field of the incoming control frame. For frames with name/value header block, the library also checks the length of inflated block is also under the limit. This is done while incrementally inflating block. If the length of frames with name/value header block exceeds the limit, the library will issue RST_STREAM with FRAME_TOO_LARGE. For other frames, it will issue GOAWAY. --- lib/includes/spdylay/spdylay.h | 27 +++++--- lib/spdylay_frame.c | 83 ++++++++++++++++++------ lib/spdylay_frame.h | 48 ++++++++++++++ lib/spdylay_int.h | 3 +- lib/spdylay_session.c | 112 +++++++++++++++++++++++++++------ lib/spdylay_session.h | 4 ++ tests/spdylay_session_test.c | 97 ++++++++++++++++++++++++++++ 7 files changed, 325 insertions(+), 49 deletions(-) diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index 0329775..50ae682 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -487,7 +487,7 @@ typedef struct { * to the name string and ``nv[2*i+1]`` contains a pointer to the * value string. The one beyond last value must be ``NULL``. That * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be - * ``NULL``. + * ``NULL``. This member may be ``NULL``. */ char **nv; } spdylay_syn_stream; @@ -510,7 +510,7 @@ typedef struct { * to the name string and ``nv[2*i+1]`` contains a pointer to the * value string. The one beyond last value must be ``NULL``. That * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be - * ``NULL``. + * ``NULL``. This member may be ``NULL``. */ char **nv; } spdylay_syn_reply; @@ -533,7 +533,7 @@ typedef struct { * to the name string and ``nv[2*i+1]`` contains a pointer to the * value string. The one beyond last value must be ``NULL``. That * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be - * ``NULL``. + * ``NULL``. This member may be ``NULL``. */ char **nv; } spdylay_headers; @@ -1261,7 +1261,12 @@ typedef enum { * responsible for sending WINDOW_UPDATE using * `spdylay_submit_window_update`. */ - SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE = 1 + SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE = 1, + /** + * This option sets maximum receive buffer size for incoming control + * frame. + */ + SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER = 2 } spdylay_opt; /** @@ -1276,10 +1281,16 @@ typedef enum { * The following |optname| are supported: * * :enum:`SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE` - * The |optval| must be ``int``. If |optval| is nonzero, the - * library will not send WINDOW_UPDATE automatically. Therefore, - * the application is responsible for sending WINDOW_UPDATE using - * `spdylay_submit_window_update`. This option defaults to 0. + * The |optval| must be a pointer to ``int``. If the |*optval| is + * nonzero, the library will not send WINDOW_UPDATE automatically. + * Therefore, the application is responsible for sending + * WINDOW_UPDATE using `spdylay_submit_window_update`. This option + * defaults to 0. + * + * :enum:`SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER` + * The |optval| must be a pointer to ``uint32_t``. The |*optval| + * must be in the range [(1 << 13), (1 << 24)-1], inclusive. This + * option defaults to (1 << 24)-1. * * This function returns 0 if it succeeds, or one of the following * negative error codes: diff --git a/lib/spdylay_frame.c b/lib/spdylay_frame.c index efefd39..df18088 100644 --- a/lib/spdylay_frame.c +++ b/lib/spdylay_frame.c @@ -678,14 +678,29 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, { int r; size_t len_size; - if(headlen + payloadlen != SPDYLAY_SYN_STREAM_NV_OFFSET) { - return SPDYLAY_ERR_INVALID_FRAME; - } - spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + r = spdylay_frame_unpack_syn_stream_without_nv(frame, head, headlen, + payload, payloadlen); len_size = spdylay_frame_get_len_size(frame->hd.version); if(len_size == 0) { return SPDYLAY_ERR_UNSUPPORTED_VERSION; } + if(r == 0) { + r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); + } + return r; +} + +int spdylay_frame_unpack_syn_stream_without_nv(spdylay_syn_stream *frame, + const uint8_t *head, + size_t headlen, + const uint8_t *payload, + size_t payloadlen) +{ + int r; + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + if(headlen + payloadlen != SPDYLAY_SYN_STREAM_NV_OFFSET) { + return SPDYLAY_ERR_INVALID_FRAME; + } frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; frame->assoc_stream_id = spdylay_get_uint32(payload+4) & SPDYLAY_STREAM_ID_MASK; @@ -695,8 +710,8 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, } else { frame->slot = 0; } - r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); - return r; + frame->nv = NULL; + return 0; } ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, @@ -735,21 +750,35 @@ int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame, spdylay_buffer *inflatebuf) { int r; - size_t len_size; + r = spdylay_frame_unpack_syn_reply_without_nv(frame, head, headlen, + payload, payloadlen); + if(r == 0) { + size_t len_size; + len_size = spdylay_frame_get_len_size(frame->hd.version); + if(len_size == 0) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); + } + return r; +} + +int spdylay_frame_unpack_syn_reply_without_nv(spdylay_syn_reply *frame, + const uint8_t *head, + size_t headlen, + const uint8_t *payload, + size_t payloadlen) +{ ssize_t nv_offset; spdylay_frame_unpack_ctrl_hd(&frame->hd, head); - len_size = spdylay_frame_get_len_size(frame->hd.version); - if(len_size == 0) { - return SPDYLAY_ERR_UNSUPPORTED_VERSION; - } nv_offset = spdylay_frame_nv_offset(SPDYLAY_SYN_REPLY, frame->hd.version); assert(nv_offset > 0); if((ssize_t)(headlen + payloadlen) != nv_offset) { return SPDYLAY_ERR_INVALID_FRAME; } frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; - r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); - return r; + frame->nv = NULL; + return 0; } ssize_t spdylay_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr, @@ -864,21 +893,35 @@ int spdylay_frame_unpack_headers(spdylay_headers *frame, spdylay_buffer *inflatebuf) { int r; - size_t len_size; + r = spdylay_frame_unpack_headers_without_nv(frame, head, headlen, + payload, payloadlen); + if(r == 0) { + size_t len_size; + len_size = spdylay_frame_get_len_size(frame->hd.version); + if(len_size == 0) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); + } + return r; +} + +int spdylay_frame_unpack_headers_without_nv(spdylay_headers *frame, + const uint8_t *head, + size_t headlen, + const uint8_t *payload, + size_t payloadlen) +{ ssize_t nv_offset; spdylay_frame_unpack_ctrl_hd(&frame->hd, head); - len_size = spdylay_frame_get_len_size(frame->hd.version); - if(len_size == 0) { - return SPDYLAY_ERR_UNSUPPORTED_VERSION; - } nv_offset = spdylay_frame_nv_offset(SPDYLAY_HEADERS, frame->hd.version); assert(nv_offset > 0); if((ssize_t)(headlen + payloadlen) != nv_offset) { return SPDYLAY_ERR_INVALID_FRAME; } frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; - r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); - return r; + frame->nv = NULL; + return 0; } ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr, diff --git a/lib/spdylay_frame.h b/lib/spdylay_frame.h index da67011..4d52e2a 100644 --- a/lib/spdylay_frame.h +++ b/lib/spdylay_frame.h @@ -164,6 +164,22 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, const uint8_t *payload, size_t payloadlen, spdylay_buffer *inflatebuf); +/* + * Unpacks SYN_STREAM frame byte sequence into |frame|. This function + * only unapcks bytes that come before name/value header block. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + */ +int spdylay_frame_unpack_syn_stream_without_nv(spdylay_syn_stream *frame, + const uint8_t *head, + size_t headlen, + const uint8_t *payload, + size_t payloadlen); + /* * Packs SYN_REPLY frame |frame| in wire frame format and store it in * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. @@ -222,6 +238,22 @@ int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame, const uint8_t *payload, size_t payloadlen, spdylay_buffer *inflatebuf); +/* + * Unpacks SYN_REPLY frame byte sequence into |frame|. This function + * only unapcks bytes that come before name/value header block. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + */ +int spdylay_frame_unpack_syn_reply_without_nv(spdylay_syn_reply *frame, + const uint8_t *head, + size_t headlen, + const uint8_t *payload, + size_t payloadlen); + /* * Packs PING frame |frame| in wire format and store it in * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| @@ -338,6 +370,22 @@ int spdylay_frame_unpack_headers(spdylay_headers *frame, const uint8_t *payload, size_t payloadlen, spdylay_buffer *inflatebuf); +/* + * Unpacks HEADERS frame byte sequence into |frame|. This function + * only unapcks bytes that come before name/value header block. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + */ +int spdylay_frame_unpack_headers_without_nv(spdylay_headers *frame, + const uint8_t *head, + size_t headlen, + const uint8_t *payload, + size_t payloadlen); + /* * Packs RST_STREAM frame |frame| in wire frame format and store it in * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| diff --git a/lib/spdylay_int.h b/lib/spdylay_int.h index 02fcd69..0113502 100644 --- a/lib/spdylay_int.h +++ b/lib/spdylay_int.h @@ -38,7 +38,8 @@ typedef int (*spdylay_compar)(const void *lhs, const void *rhs); /* Internal error code. They must be in the range [-499, -100], inclusive. */ typedef enum { - SPDYLAY_ERR_CREDENTIAL_PENDING = -101 + SPDYLAY_ERR_CREDENTIAL_PENDING = -101, + SPDYLAY_ERR_FRAME_TOO_LARGE = -102 } spdylay_internal_error; #endif /* SPDYLAY_INT_H */ diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index d34ed93..783c162 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -169,6 +169,8 @@ static int spdylay_session_new(spdylay_session **session_ptr, (*session_ptr)->goaway_flags = SPDYLAY_GOAWAY_NONE; (*session_ptr)->last_good_stream_id = 0; + (*session_ptr)->max_recv_ctrl_frame_buf = (1 << 24)-1; + r = spdylay_zlib_deflate_hd_init(&(*session_ptr)->hd_deflater, (*session_ptr)->version); if(r != 0) { @@ -2095,6 +2097,16 @@ static void spdylay_session_handle_parse_error(spdylay_session *session, } } +static int spdylay_get_status_code_from_error_code(int error_code) +{ + switch(error_code) { + case(SPDYLAY_ERR_FRAME_TOO_LARGE): + return SPDYLAY_FRAME_TOO_LARGE; + default: + return SPDYLAY_PROTOCOL_ERROR; + } +} + /* For errors, this function only returns FATAL error. */ static int spdylay_session_process_ctrl_frame(spdylay_session *session) { @@ -2111,6 +2123,14 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) session->iframe.buf, session->iframe.buflen, &session->iframe.inflatebuf); + } else if(session->iframe.error_code == SPDYLAY_ERR_FRAME_TOO_LARGE) { + r = spdylay_frame_unpack_syn_stream_without_nv + (&frame.syn_stream, + session->iframe.headbuf, sizeof(session->iframe.headbuf), + session->iframe.buf, session->iframe.buflen); + if(r == 0) { + r = session->iframe.error_code; + } } else { r = session->iframe.error_code; } @@ -2120,13 +2140,11 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) } r = spdylay_session_on_syn_stream_received(session, &frame); spdylay_frame_syn_stream_free(&frame.syn_stream); - /* 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) { + } else if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK || + r == SPDYLAY_ERR_FRAME_TOO_LARGE) { r = spdylay_session_handle_invalid_stream (session, frame.syn_stream.stream_id, SPDYLAY_SYN_STREAM, &frame, - SPDYLAY_PROTOCOL_ERROR); + spdylay_get_status_code_from_error_code(r)); spdylay_frame_syn_stream_free(&frame.syn_stream); } else if(spdylay_is_non_fatal(r)) { spdylay_session_handle_parse_error(session, type, r); @@ -2141,6 +2159,14 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) session->iframe.buf, session->iframe.buflen, &session->iframe.inflatebuf); + } else if(session->iframe.error_code == SPDYLAY_ERR_FRAME_TOO_LARGE) { + r = spdylay_frame_unpack_syn_reply_without_nv + (&frame.syn_reply, + session->iframe.headbuf, sizeof(session->iframe.headbuf), + session->iframe.buf, session->iframe.buflen); + if(r == 0) { + r = session->iframe.error_code; + } } else { r = session->iframe.error_code; } @@ -2150,10 +2176,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) { + } else if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK || + r == SPDYLAY_ERR_FRAME_TOO_LARGE) { r = spdylay_session_handle_invalid_stream (session, frame.syn_reply.stream_id, SPDYLAY_SYN_REPLY, &frame, - SPDYLAY_PROTOCOL_ERROR); + spdylay_get_status_code_from_error_code(r)); spdylay_frame_syn_reply_free(&frame.syn_reply); } else if(spdylay_is_non_fatal(r)) { spdylay_session_handle_parse_error(session, type, r); @@ -2226,6 +2253,14 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) session->iframe.buf, session->iframe.buflen, &session->iframe.inflatebuf); + } else if(session->iframe.error_code == SPDYLAY_ERR_FRAME_TOO_LARGE) { + r = spdylay_frame_unpack_headers_without_nv + (&frame.headers, + session->iframe.headbuf, sizeof(session->iframe.headbuf), + session->iframe.buf, session->iframe.buflen); + if(r == 0) { + r = session->iframe.error_code; + } } else { r = session->iframe.error_code; } @@ -2235,10 +2270,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) { + } else if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK || + r == SPDYLAY_ERR_FRAME_TOO_LARGE) { r = spdylay_session_handle_invalid_stream (session, frame.headers.stream_id, SPDYLAY_HEADERS, &frame, - SPDYLAY_PROTOCOL_ERROR); + spdylay_get_status_code_from_error_code(r)); spdylay_frame_headers_free(&frame.headers); } else if(spdylay_is_non_fatal(r)) { spdylay_session_handle_parse_error(session, type, r); @@ -2443,18 +2479,28 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session, ssize_t buflen; buflen = spdylay_inbound_frame_payload_nv_offset(&session->iframe); if(buflen == -1) { - /* TODO Check if payloadlen is small enough for buffering */ - buflen = session->iframe.payloadlen; + /* Check if payloadlen is small enough for buffering */ + if(session->iframe.payloadlen > session->max_recv_ctrl_frame_buf) { + session->iframe.error_code = SPDYLAY_ERR_FRAME_TOO_LARGE; + session->iframe.state = SPDYLAY_RECV_PAYLOAD_IGN; + buflen = 0; + } else { + buflen = session->iframe.payloadlen; + } } else if(buflen < (ssize_t)session->iframe.payloadlen) { + if(session->iframe.payloadlen > session->max_recv_ctrl_frame_buf) { + session->iframe.error_code = SPDYLAY_ERR_FRAME_TOO_LARGE; + } + /* We are going to receive payload even if the receiving + frame is too large to synchronize zlib context. For + name/value header block, we will just burn zlib cycle + and discard outputs. */ session->iframe.state = SPDYLAY_RECV_PAYLOAD_PRE_NV; } /* buflen >= session->iframe.payloadlen means frame is malformed. In this case, we just buffer these bytes and handle error later. */ session->iframe.buflen = buflen; - - /* TODO On error case, go into SPDYLAY_RECV_PAYLOAD_IGN state and - discard any input bytes. */ r = spdylay_reserve_buffer(&session->iframe.buf, &session->iframe.bufmax, buflen); @@ -2470,7 +2516,8 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session, } if(session->iframe.state == SPDYLAY_RECV_PAYLOAD || session->iframe.state == SPDYLAY_RECV_PAYLOAD_PRE_NV || - session->iframe.state == SPDYLAY_RECV_PAYLOAD_NV) { + session->iframe.state == SPDYLAY_RECV_PAYLOAD_NV || + session->iframe.state == SPDYLAY_RECV_PAYLOAD_IGN) { size_t rempayloadlen; size_t bufavail, readlen; int32_t data_stream_id = 0; @@ -2501,20 +2548,33 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session, /* For frame with name/value header block, the compressed portion of the block is incrementally decompressed. The result is stored in inflatebuf. */ - if(session->iframe.error_code == 0) { + if(session->iframe.error_code == 0 || + session->iframe.error_code == SPDYLAY_ERR_FRAME_TOO_LARGE) { ssize_t decomplen; + if(session->iframe.error_code == SPDYLAY_ERR_FRAME_TOO_LARGE) { + spdylay_buffer_reset(&session->iframe.inflatebuf); + } decomplen = spdylay_zlib_inflate_hd(&session->hd_inflater, &session->iframe.inflatebuf, inmark, readlen); - /* TODO If total length in inflatebuf exceeds certain limit, - set TOO_LARGE_FRAME to error_code and issue RST_STREAM - later. */ if(decomplen < 0) { + /* We are going to overwrite error_code here if it is + already set. But it is fine because the only possible + nonzero error code here is SPDYLAY_ERR_FRAME_TOO_LARGE + and zlib/fatal error can override it. */ session->iframe.error_code = decomplen; + } else if(spdylay_buffer_length(&session->iframe.inflatebuf) + > session->max_recv_ctrl_frame_buf) { + /* If total length in inflatebuf exceeds certain limit, + set TOO_LARGE_FRAME to error_code and issue RST_STREAM + later. */ + session->iframe.error_code = SPDYLAY_ERR_FRAME_TOO_LARGE; } } } else if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { - memcpy(session->iframe.buf+session->iframe.off, inmark, readlen); + if(session->iframe.state != SPDYLAY_RECV_PAYLOAD_IGN) { + memcpy(session->iframe.buf+session->iframe.off, inmark, readlen); + } } else { /* For data frame, We don't buffer data. Instead, just pass received data to callback function. */ @@ -2827,6 +2887,18 @@ int spdylay_session_set_option(spdylay_session *session, return SPDYLAY_ERR_INVALID_ARGUMENT; } break; + case SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER: + if(optlen == sizeof(uint32_t)) { + uint32_t intval = *(uint32_t*)optval; + if((1 << 13) <= intval && intval < (1 << 24)) { + session->max_recv_ctrl_frame_buf = intval; + } else { + return SPDYLAY_ERR_INVALID_ARGUMENT; + } + } else { + return SPDYLAY_ERR_INVALID_ARGUMENT; + } + break; default: return SPDYLAY_ERR_INVALID_ARGUMENT; } diff --git a/lib/spdylay_session.h b/lib/spdylay_session.h index 825a309..1a342c9 100644 --- a/lib/spdylay_session.h +++ b/lib/spdylay_session.h @@ -93,6 +93,8 @@ typedef enum { SPDYLAY_RECV_HEAD, /* Receiving frame payload (comes after length field) */ SPDYLAY_RECV_PAYLOAD, + /* Receiving frame payload, but the received bytes are discarded. */ + SPDYLAY_RECV_PAYLOAD_IGN, /* Receiving frame payload that comes before name/value header block. Applied only for SYN_STREAM, SYN_REPLY and HEADERS. */ SPDYLAY_RECV_PAYLOAD_PRE_NV, @@ -207,6 +209,8 @@ struct spdylay_session { /* Option flags. This is bitwise-OR of 0 or more of spdylay_optmask. */ uint32_t opt_flags; + /* Maxmum size of buffer to use when receving control frame. */ + uint32_t max_recv_ctrl_frame_buf; /* Client certificate vector */ spdylay_client_cert_vector cli_certvec; diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index 3264068..6d53274 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -242,6 +242,16 @@ void test_spdylay_session_recv(void) const char *upcase_nv[] = { "URL", "/", NULL }; + const char *mid_nv[] = { + "method", "GET", + "scheme", "https", + "url", "/", + "x-head", "foo", + "x-head", "bar", + "version", "HTTP/1.1", + "x-empty", "", + NULL + }; uint8_t *framedata = NULL, *nvbuf = NULL; size_t framedatalen = 0, nvbuflen = 0; ssize_t framelen; @@ -315,6 +325,93 @@ void test_spdylay_session_recv(void) spdylay_session_del(session); + /* Some tests for frame too large */ + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, + &user_data); + /* made max buffer small to cause error intentionally */ + /* Inflated wire format of mid_nv will be 111 in SPDY/3. So payload + length will be 121. Setting max buffer size to 110 will cause + error while inflating name/value header block. */ + session->max_recv_ctrl_frame_buf = 110; + + /* Receive SYN_STREAM with too large payload */ + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY3, + SPDYLAY_CTRL_FLAG_NONE, + 1, 0, 3, dup_nv(mid_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 == OB_CTRL_TYPE(item)); + CU_ASSERT(SPDYLAY_FRAME_TOO_LARGE == OB_CTRL(item)->rst_stream.status_code); + CU_ASSERT(1 == OB_CTRL(item)->rst_stream.stream_id); + CU_ASSERT(0 == spdylay_session_send(session)); + + /* For SYN_REPLY and SYN_HEADERS, make max buffer even smaller */ + session->max_recv_ctrl_frame_buf = 8; + + /* Receive SYN_REPLY with too large payload */ + spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY3, + SPDYLAY_CTRL_FLAG_NONE, + 1, dup_nv(mid_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 == OB_CTRL_TYPE(item)); + CU_ASSERT(SPDYLAY_FRAME_TOO_LARGE == OB_CTRL(item)->rst_stream.status_code); + CU_ASSERT(1 == OB_CTRL(item)->rst_stream.stream_id); + CU_ASSERT(0 == spdylay_session_send(session)); + + /* Receive HEADERS with too large payload */ + spdylay_frame_headers_init(&frame.headers, SPDYLAY_PROTO_SPDY3, + SPDYLAY_CTRL_FLAG_NONE, + 1, dup_nv(mid_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 == OB_CTRL_TYPE(item)); + CU_ASSERT(SPDYLAY_FRAME_TOO_LARGE == OB_CTRL(item)->rst_stream.status_code); + CU_ASSERT(1 == OB_CTRL(item)->rst_stream.stream_id); + CU_ASSERT(0 == spdylay_session_send(session)); + + /* Receive PING with too large payload */ + spdylay_frame_ping_init(&frame.ping, SPDYLAY_PROTO_SPDY3, 1); + spdylay_reserve_buffer(&framedata, &framedatalen, 77); + framelen = spdylay_frame_pack_ping(&framedata, &framedatalen, &frame.ping); + spdylay_frame_ping_free(&frame.ping); + spdylay_put_uint32be(&framedata[4], framedatalen - SPDYLAY_HEAD_LEN); + scripted_data_feed_init(&df, framedata, framedatalen); + 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_GOAWAY == OB_CTRL_TYPE(item)); + CU_ASSERT(SPDYLAY_GOAWAY_PROTOCOL_ERROR == + OB_CTRL(item)->rst_stream.status_code); + CU_ASSERT(0 == spdylay_session_send(session)); + + spdylay_session_del(session); + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, &user_data); /* Receive SYN_REPLY with invalid header block */