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:
parent
a6ae4fc72c
commit
4dd9c32c25
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue