Commit 4dd9c32c authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

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.
parent a6ae4fc7
...@@ -487,7 +487,7 @@ typedef struct { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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); ...@@ -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 */
This diff is collapsed.
...@@ -93,6 +93,8 @@ typedef enum { ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment