1
0
mirror of https://github.com/moparisthebest/spdylay synced 2024-12-22 07:38:52 -05:00

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.
This commit is contained in:
Tatsuhiro Tsujikawa 2012-05-25 13:49:18 +09:00
parent a6ae4fc72c
commit 4dd9c32c25
7 changed files with 325 additions and 49 deletions

View File

@ -487,7 +487,7 @@ typedef struct {
* to the name string and ``nv[2*i+1]`` contains a pointer to the * 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 * 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 * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be
* ``NULL``. * ``NULL``. This member may be ``NULL``.
*/ */
char **nv; char **nv;
} spdylay_syn_stream; } spdylay_syn_stream;
@ -510,7 +510,7 @@ typedef struct {
* to the name string and ``nv[2*i+1]`` contains a pointer to the * 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 * 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 * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be
* ``NULL``. * ``NULL``. This member may be ``NULL``.
*/ */
char **nv; char **nv;
} spdylay_syn_reply; } spdylay_syn_reply;
@ -533,7 +533,7 @@ typedef struct {
* to the name string and ``nv[2*i+1]`` contains a pointer to the * 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 * 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 * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be
* ``NULL``. * ``NULL``. This member may be ``NULL``.
*/ */
char **nv; char **nv;
} spdylay_headers; } spdylay_headers;
@ -1261,7 +1261,12 @@ typedef enum {
* responsible for sending WINDOW_UPDATE using * responsible for sending WINDOW_UPDATE using
* `spdylay_submit_window_update`. * `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; } spdylay_opt;
/** /**
@ -1276,10 +1281,16 @@ typedef enum {
* The following |optname| are supported: * The following |optname| are supported:
* *
* :enum:`SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE` * :enum:`SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE`
* The |optval| must be ``int``. If |optval| is nonzero, the * The |optval| must be a pointer to ``int``. If the |*optval| is
* library will not send WINDOW_UPDATE automatically. Therefore, * nonzero, the library will not send WINDOW_UPDATE automatically.
* the application is responsible for sending WINDOW_UPDATE using * Therefore, the application is responsible for sending
* `spdylay_submit_window_update`. This option defaults to 0. * 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 * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:

View File

@ -678,14 +678,29 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame,
{ {
int r; int r;
size_t len_size; size_t len_size;
if(headlen + payloadlen != SPDYLAY_SYN_STREAM_NV_OFFSET) { r = spdylay_frame_unpack_syn_stream_without_nv(frame, head, headlen,
return SPDYLAY_ERR_INVALID_FRAME; payload, payloadlen);
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
len_size = spdylay_frame_get_len_size(frame->hd.version); len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) { if(len_size == 0) {
return SPDYLAY_ERR_UNSUPPORTED_VERSION; 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->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;
frame->assoc_stream_id = frame->assoc_stream_id =
spdylay_get_uint32(payload+4) & SPDYLAY_STREAM_ID_MASK; spdylay_get_uint32(payload+4) & SPDYLAY_STREAM_ID_MASK;
@ -695,8 +710,8 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame,
} else { } else {
frame->slot = 0; frame->slot = 0;
} }
r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); frame->nv = NULL;
return r; return 0;
} }
ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, 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) spdylay_buffer *inflatebuf)
{ {
int r; int r;
r = spdylay_frame_unpack_syn_reply_without_nv(frame, head, headlen,
payload, payloadlen);
if(r == 0) {
size_t len_size; size_t len_size;
ssize_t nv_offset;
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
len_size = spdylay_frame_get_len_size(frame->hd.version); len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) { if(len_size == 0) {
return SPDYLAY_ERR_UNSUPPORTED_VERSION; 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);
nv_offset = spdylay_frame_nv_offset(SPDYLAY_SYN_REPLY, frame->hd.version); nv_offset = spdylay_frame_nv_offset(SPDYLAY_SYN_REPLY, frame->hd.version);
assert(nv_offset > 0); assert(nv_offset > 0);
if((ssize_t)(headlen + payloadlen) != nv_offset) { if((ssize_t)(headlen + payloadlen) != nv_offset) {
return SPDYLAY_ERR_INVALID_FRAME; return SPDYLAY_ERR_INVALID_FRAME;
} }
frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;
r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); frame->nv = NULL;
return r; return 0;
} }
ssize_t spdylay_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr, 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) spdylay_buffer *inflatebuf)
{ {
int r; int r;
r = spdylay_frame_unpack_headers_without_nv(frame, head, headlen,
payload, payloadlen);
if(r == 0) {
size_t len_size; size_t len_size;
ssize_t nv_offset;
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
len_size = spdylay_frame_get_len_size(frame->hd.version); len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) { if(len_size == 0) {
return SPDYLAY_ERR_UNSUPPORTED_VERSION; 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);
nv_offset = spdylay_frame_nv_offset(SPDYLAY_HEADERS, frame->hd.version); nv_offset = spdylay_frame_nv_offset(SPDYLAY_HEADERS, frame->hd.version);
assert(nv_offset > 0); assert(nv_offset > 0);
if((ssize_t)(headlen + payloadlen) != nv_offset) { if((ssize_t)(headlen + payloadlen) != nv_offset) {
return SPDYLAY_ERR_INVALID_FRAME; return SPDYLAY_ERR_INVALID_FRAME;
} }
frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;
r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); frame->nv = NULL;
return r; return 0;
} }
ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr, ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr,

View File

@ -164,6 +164,22 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame,
const uint8_t *payload, size_t payloadlen, const uint8_t *payload, size_t payloadlen,
spdylay_buffer *inflatebuf); 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 * Packs SYN_REPLY frame |frame| in wire frame format and store it in
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. * |*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, const uint8_t *payload, size_t payloadlen,
spdylay_buffer *inflatebuf); 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 * Packs PING frame |frame| in wire format and store it in
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| * |*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, const uint8_t *payload, size_t payloadlen,
spdylay_buffer *inflatebuf); 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 * Packs RST_STREAM frame |frame| in wire frame format and store it in
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|

View File

@ -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], /* Internal error code. They must be in the range [-499, -100],
inclusive. */ inclusive. */
typedef enum { typedef enum {
SPDYLAY_ERR_CREDENTIAL_PENDING = -101 SPDYLAY_ERR_CREDENTIAL_PENDING = -101,
SPDYLAY_ERR_FRAME_TOO_LARGE = -102
} spdylay_internal_error; } spdylay_internal_error;
#endif /* SPDYLAY_INT_H */ #endif /* SPDYLAY_INT_H */

View File

@ -169,6 +169,8 @@ static int spdylay_session_new(spdylay_session **session_ptr,
(*session_ptr)->goaway_flags = SPDYLAY_GOAWAY_NONE; (*session_ptr)->goaway_flags = SPDYLAY_GOAWAY_NONE;
(*session_ptr)->last_good_stream_id = 0; (*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, r = spdylay_zlib_deflate_hd_init(&(*session_ptr)->hd_deflater,
(*session_ptr)->version); (*session_ptr)->version);
if(r != 0) { 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. */ /* For errors, this function only returns FATAL error. */
static int spdylay_session_process_ctrl_frame(spdylay_session *session) 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.buf,
session->iframe.buflen, session->iframe.buflen,
&session->iframe.inflatebuf); &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 { } else {
r = session->iframe.error_code; 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); r = spdylay_session_on_syn_stream_received(session, &frame);
spdylay_frame_syn_stream_free(&frame.syn_stream); spdylay_frame_syn_stream_free(&frame.syn_stream);
/* TODO if r indicates mulformed NV pairs (multiple nulls) or } else if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK ||
invalid frame, send RST_STREAM with PROTOCOL_ERROR. Same for r == SPDYLAY_ERR_FRAME_TOO_LARGE) {
other control frames. */
} else if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK) {
r = spdylay_session_handle_invalid_stream r = spdylay_session_handle_invalid_stream
(session, frame.syn_stream.stream_id, SPDYLAY_SYN_STREAM, &frame, (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); spdylay_frame_syn_stream_free(&frame.syn_stream);
} else if(spdylay_is_non_fatal(r)) { } else if(spdylay_is_non_fatal(r)) {
spdylay_session_handle_parse_error(session, type, 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.buf,
session->iframe.buflen, session->iframe.buflen,
&session->iframe.inflatebuf); &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 { } else {
r = session->iframe.error_code; 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); r = spdylay_session_on_syn_reply_received(session, &frame);
spdylay_frame_syn_reply_free(&frame.syn_reply); 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 r = spdylay_session_handle_invalid_stream
(session, frame.syn_reply.stream_id, SPDYLAY_SYN_REPLY, &frame, (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); spdylay_frame_syn_reply_free(&frame.syn_reply);
} else if(spdylay_is_non_fatal(r)) { } else if(spdylay_is_non_fatal(r)) {
spdylay_session_handle_parse_error(session, type, 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.buf,
session->iframe.buflen, session->iframe.buflen,
&session->iframe.inflatebuf); &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 { } else {
r = session->iframe.error_code; 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); r = spdylay_session_on_headers_received(session, &frame);
spdylay_frame_headers_free(&frame.headers); 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 r = spdylay_session_handle_invalid_stream
(session, frame.headers.stream_id, SPDYLAY_HEADERS, &frame, (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); spdylay_frame_headers_free(&frame.headers);
} else if(spdylay_is_non_fatal(r)) { } else if(spdylay_is_non_fatal(r)) {
spdylay_session_handle_parse_error(session, type, 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; ssize_t buflen;
buflen = spdylay_inbound_frame_payload_nv_offset(&session->iframe); buflen = spdylay_inbound_frame_payload_nv_offset(&session->iframe);
if(buflen == -1) { if(buflen == -1) {
/* TODO Check if payloadlen is small enough for buffering */ /* 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; buflen = session->iframe.payloadlen;
}
} else if(buflen < (ssize_t)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; session->iframe.state = SPDYLAY_RECV_PAYLOAD_PRE_NV;
} }
/* buflen >= session->iframe.payloadlen means frame is /* buflen >= session->iframe.payloadlen means frame is
malformed. In this case, we just buffer these bytes and malformed. In this case, we just buffer these bytes and
handle error later. */ handle error later. */
session->iframe.buflen = buflen; 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, r = spdylay_reserve_buffer(&session->iframe.buf,
&session->iframe.bufmax, &session->iframe.bufmax,
buflen); buflen);
@ -2470,7 +2516,8 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session,
} }
if(session->iframe.state == SPDYLAY_RECV_PAYLOAD || if(session->iframe.state == SPDYLAY_RECV_PAYLOAD ||
session->iframe.state == SPDYLAY_RECV_PAYLOAD_PRE_NV || 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 rempayloadlen;
size_t bufavail, readlen; size_t bufavail, readlen;
int32_t data_stream_id = 0; 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 /* For frame with name/value header block, the compressed
portion of the block is incrementally decompressed. The portion of the block is incrementally decompressed. The
result is stored in inflatebuf. */ 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; 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, decomplen = spdylay_zlib_inflate_hd(&session->hd_inflater,
&session->iframe.inflatebuf, &session->iframe.inflatebuf,
inmark, readlen); inmark, readlen);
/* TODO If total length in inflatebuf exceeds certain limit, 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 set TOO_LARGE_FRAME to error_code and issue RST_STREAM
later. */ later. */
if(decomplen < 0) { session->iframe.error_code = SPDYLAY_ERR_FRAME_TOO_LARGE;
session->iframe.error_code = decomplen;
} }
} }
} else if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { } else if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) {
if(session->iframe.state != SPDYLAY_RECV_PAYLOAD_IGN) {
memcpy(session->iframe.buf+session->iframe.off, inmark, readlen); memcpy(session->iframe.buf+session->iframe.off, inmark, readlen);
}
} else { } else {
/* For data frame, We don't buffer data. Instead, just pass /* For data frame, We don't buffer data. Instead, just pass
received data to callback function. */ received data to callback function. */
@ -2827,6 +2887,18 @@ int spdylay_session_set_option(spdylay_session *session,
return SPDYLAY_ERR_INVALID_ARGUMENT; return SPDYLAY_ERR_INVALID_ARGUMENT;
} }
break; 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: default:
return SPDYLAY_ERR_INVALID_ARGUMENT; return SPDYLAY_ERR_INVALID_ARGUMENT;
} }

View File

@ -93,6 +93,8 @@ typedef enum {
SPDYLAY_RECV_HEAD, SPDYLAY_RECV_HEAD,
/* Receiving frame payload (comes after length field) */ /* Receiving frame payload (comes after length field) */
SPDYLAY_RECV_PAYLOAD, 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 /* Receiving frame payload that comes before name/value header
block. Applied only for SYN_STREAM, SYN_REPLY and HEADERS. */ block. Applied only for SYN_STREAM, SYN_REPLY and HEADERS. */
SPDYLAY_RECV_PAYLOAD_PRE_NV, 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. */ /* Option flags. This is bitwise-OR of 0 or more of spdylay_optmask. */
uint32_t opt_flags; uint32_t opt_flags;
/* Maxmum size of buffer to use when receving control frame. */
uint32_t max_recv_ctrl_frame_buf;
/* Client certificate vector */ /* Client certificate vector */
spdylay_client_cert_vector cli_certvec; spdylay_client_cert_vector cli_certvec;

View File

@ -242,6 +242,16 @@ void test_spdylay_session_recv(void)
const char *upcase_nv[] = { const char *upcase_nv[] = {
"URL", "/", NULL "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; uint8_t *framedata = NULL, *nvbuf = NULL;
size_t framedatalen = 0, nvbuflen = 0; size_t framedatalen = 0, nvbuflen = 0;
ssize_t framelen; ssize_t framelen;
@ -315,6 +325,93 @@ void test_spdylay_session_recv(void)
spdylay_session_del(session); 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, spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,
&user_data); &user_data);
/* Receive SYN_REPLY with invalid header block */ /* Receive SYN_REPLY with invalid header block */