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 */