Queue RST_STREAM if invalid stream ID is received in SYN_STREAM or SYN_REPLY.

Fixed bug that Z_DATA_ERROR is not handled.
Fixed bug that spdylay_frame_alloc_pack_nv does not use nv_offset correctly.
This commit is contained in:
Tatsuhiro Tsujikawa 2012-01-25 21:31:28 +09:00
parent fa549aa3a7
commit 3bfe48972c
10 changed files with 397 additions and 60 deletions

View File

@ -40,12 +40,70 @@ typedef enum {
SPDYLAY_ERR_WOULDBLOCK = -504,
SPDYLAY_ERR_PROTO = -505,
SPDYLAY_ERR_CALLBACK_FAILURE = -505,
SPDYLAY_ERR_INVALID_FRAME = -506,
} spdylay_error;
typedef enum {
SPDYLAY_MSG_MORE
} spdylay_io_flag;
typedef enum {
SPDYLAY_SYN_STREAM = 1,
SPDYLAY_SYN_REPLY = 2,
SPDYLAY_RST_STREAM = 3,
SPDYLAY_SETTINGS = 4,
SPDYLAY_NOOP = 5,
SPDYLAY_PING = 6,
SPDYLAY_GOAWAY = 7,
} spdylay_frame_type;
typedef enum {
SPDYLAY_FLAG_FIN = 1
} spdylay_flag;
typedef enum {
SPDYLAY_PROTOCOL_ERROR = 1,
SPDYLAY_INVALID_STREAM = 2,
SPDYLAY_REFUSED_STREAM = 3,
SPDYLAY_UNSUPPORTED_VERSION = 4,
SPDYLAY_CANCEL = 5,
SPDYLAY_INTERNAL_ERROR = 6,
SPDYLAY_FLOW_CONTROL_ERROR = 7
} spdylay_status_code;
typedef struct {
uint16_t version;
uint16_t type;
uint8_t flags;
int32_t length;
} spdylay_ctrl_hd;
typedef struct {
spdylay_ctrl_hd hd;
int32_t stream_id;
int32_t assoc_stream_id;
uint8_t pri;
char **nv;
} spdylay_syn_stream;
typedef struct {
spdylay_ctrl_hd hd;
int32_t stream_id;
char **nv;
} spdylay_syn_reply;
typedef struct {
spdylay_ctrl_hd hd;
int32_t stream_id;
uint32_t status_code;
} spdylay_rst_stream;
typedef union {
spdylay_syn_stream syn_stream;
spdylay_syn_reply syn_reply;
spdylay_rst_stream rst_stream;
} spdylay_frame;
struct spdylay_session;
typedef struct spdylay_session spdylay_session;
@ -57,9 +115,28 @@ typedef ssize_t (*spdylay_recv_callback)
(spdylay_session *session,
uint8_t *buf, size_t length, int flags, void *user_data);
/*
* Callback function invoked by spdylay_session_recv() when a control
* frame is arrived.
*/
typedef void (*spdylay_on_ctrl_recv_callback)
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
void *user_data);
/*
* Callback function invoked by spdylay_session_recv() when an invalid
* control frame is arrived, which typically the case where RST_STREAM
* will be sent
*/
typedef void (*spdylay_on_invalid_ctrl_recv_callback)
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
void *user_data);
typedef struct {
spdylay_send_callback send_callback;
spdylay_recv_callback recv_callback;
spdylay_on_ctrl_recv_callback on_ctrl_recv_callback;
spdylay_on_invalid_ctrl_recv_callback on_invalid_ctrl_recv_callback;
} spdylay_session_callbacks;
int spdylay_session_client_new(spdylay_session **session_ptr,

View File

@ -75,14 +75,16 @@ static ssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr,
uint8_t *framebuf = malloc(maxframelen);
ssize_t framelen;
spdylay_frame_pack_nv(nvbuf, nv);
framelen = spdylay_zlib_deflate_hd(deflater, framebuf+18, maxframelen-18,
framelen = spdylay_zlib_deflate_hd(deflater,
framebuf+nv_offset,
maxframelen-nv_offset,
nvbuf, nvbuflen);
free(nvbuf);
if(framelen < 0) {
free(framebuf);
return framelen;
}
framelen += 18;
framelen += nv_offset;
*buf_ptr = framebuf;
return framelen;
}
@ -337,12 +339,38 @@ void spdylay_frame_syn_stream_free(spdylay_syn_stream *frame)
free(frame->nv);
}
void spdylay_frame_syn_reply_init(spdylay_syn_reply *frame, uint8_t flags,
int32_t stream_id, char **nv)
{
memset(frame, 0, sizeof(spdylay_syn_reply));
frame->hd.version = 2;
frame->hd.type = SPDYLAY_SYN_REPLY;
frame->hd.flags = flags;
frame->stream_id = stream_id;
frame->nv = nv;
}
void spdylay_frame_syn_reply_free(spdylay_syn_reply *frame)
{
spdylay_frame_nv_free(frame->nv);
free(frame->nv);
}
void spdylay_frame_rst_stream_init(spdylay_rst_stream *frame,
int32_t stream_id, uint32_t status_code)
{
memset(frame, 0, sizeof(spdylay_rst_stream));
frame->hd.version = 2;
frame->hd.type = SPDYLAY_RST_STREAM;
frame->hd.flags = 0;
frame->hd.length = 8;
frame->stream_id = stream_id;
frame->status_code = status_code;
}
void spdylay_frame_rst_stream_free(spdylay_rst_stream *frame)
{}
ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr,
spdylay_syn_stream *frame,
spdylay_zlib *deflater)
@ -371,15 +399,36 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame,
spdylay_zlib *inflater)
{
int r;
if(payloadlen < 12) {
return SPDYLAY_ERR_INVALID_FRAME;
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
frame->stream_id = spdylay_get_uint32(payload);
frame->assoc_stream_id = spdylay_get_uint32(payload+4);
frame->stream_id = spdylay_get_uint32(payload) & 0x7fffffff;
frame->assoc_stream_id = spdylay_get_uint32(payload+4) & 0x7fffffff;
frame->pri = spdylay_unpack_pri(payload+8);
r = spdylay_frame_alloc_unpack_nv(&frame->nv, payload+10, payloadlen-10,
inflater);
return r;
}
ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr,
spdylay_syn_reply *frame,
spdylay_zlib *deflater)
{
uint8_t *framebuf = NULL;
size_t framelen;
framelen = spdylay_frame_alloc_pack_nv(&framebuf, frame->nv, 14, deflater);
if(framelen < 0) {
return framelen;
}
frame->hd.length = framelen-8;
memset(framebuf, 0, 14);
spdylay_frame_pack_ctrl_hd(framebuf, &frame->hd);
spdylay_put_uint32be(&framebuf[8], frame->stream_id);
*buf_ptr = framebuf;
return framelen;
}
int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
@ -387,9 +436,38 @@ int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame,
{
int r;
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
frame->stream_id = spdylay_get_uint32(payload);
frame->stream_id = spdylay_get_uint32(payload) &0x7fffffff;
r = spdylay_frame_alloc_unpack_nv(&frame->nv, payload+6, payloadlen-6,
inflater);
return r;
}
ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr,
spdylay_rst_stream *frame)
{
uint8_t *framebuf;
size_t framelen = 16;
framebuf = malloc(framelen);
if(framebuf == NULL) {
return SPDYLAY_ERR_NOMEM;
}
memset(framebuf, 0, framelen);
spdylay_frame_pack_ctrl_hd(framebuf, &frame->hd);
spdylay_put_uint32be(&framebuf[8], frame->stream_id);
spdylay_put_uint32be(&framebuf[12], frame->status_code);
*buf_ptr = framebuf;
return framelen;
}
int spdylay_frame_unpack_rst_stream(spdylay_rst_stream *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen)
{
if(payloadlen < 8) {
return SPDYLAY_ERR_INVALID_FRAME;
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
frame->stream_id = spdylay_get_uint32(payload) & 0x7fffffff;
frame->status_code = spdylay_get_uint32(payload+4);
return 0;
}

View File

@ -29,48 +29,10 @@
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <spdylay/spdylay.h>
#include "spdylay_zlib.h"
#include "spdylay_buffer.h"
typedef enum {
SPDYLAY_SYN_STREAM = 1,
SPDYLAY_SYN_REPLY = 2,
SPDYLAY_RST_STREAM = 3,
SPDYLAY_SETTINGS = 4,
SPDYLAY_NOOP = 5,
SPDYLAY_PING = 6,
SPDYLAY_GOAWAY = 7,
} spdylay_frame_type;
typedef enum {
SPDYLAY_FLAG_FIN = 1
} spdylay_flag;
typedef struct {
uint16_t version;
uint16_t type;
uint8_t flags;
int32_t length;
} spdylay_ctrl_hd;
typedef struct {
spdylay_ctrl_hd hd;
int32_t stream_id;
int32_t assoc_stream_id;
uint8_t pri;
char **nv;
} spdylay_syn_stream;
typedef struct {
spdylay_ctrl_hd hd;
int32_t stream_id;
char **nv;
} spdylay_syn_reply;
typedef union {
spdylay_syn_stream syn_stream;
} spdylay_frame;
/*
* Packs SYN_STREAM frame |frame| in wire frame format and store it in
* |*buf_ptr|. This function allocates enough memory to store given
@ -94,6 +56,17 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame,
const uint8_t *payload, size_t payloadlen,
spdylay_zlib *inflater);
/*
* Packs SYN_REPLY frame |frame| in wire frame format and store it in
* |*buf_ptr|. This function allocates enough memory to store given
* frame in |*buf_ptr|. This function returns the size of packed frame
* it it succeeds, or returns negative error code. frame->hd.length is
* assigned after length is determined during packing process.
*/
ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr,
spdylay_syn_reply *frame,
spdylay_zlib *deflater);
/*
* Unpacks SYN_REPLY frame byte sequence into |frame|. This function
* returns 0 if it succeeds or negative error code.
@ -103,6 +76,24 @@ int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame,
const uint8_t *payload, size_t payloadlen,
spdylay_zlib *inflater);
/*
* Packs RST_STREAM frame |frame| in wire frame format and store it in
* |*buf_ptr|. This function allocates enough memory to store given
* frame in |*buf_ptr|. In spdy/2 spc, RST_STREAM wire format is
* always 16 bytes long. This function returns the size of packed
* frame if it succeeds, or negative error code.
*/
ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr,
spdylay_rst_stream *frame);
/*
* Unpacks RST_STREAM frame byte sequence into |frame|. This function
* returns 0 if it succeeds, or negative error code.
*/
int spdylay_frame_unpack_rst_stream(spdylay_rst_stream *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen);
/*
* Returns number of bytes to pack name/value pairs |nv|. This
* function expects |nv| is sorted in ascending order of key. This
@ -136,8 +127,16 @@ void spdylay_frame_syn_stream_init(spdylay_syn_stream *frame, uint8_t flags,
void spdylay_frame_syn_stream_free(spdylay_syn_stream *frame);
void spdylay_frame_syn_reply_init(spdylay_syn_reply *frame, uint8_t flags,
int32_t stream_id, char **nv);
void spdylay_frame_syn_reply_free(spdylay_syn_reply *frame);
void spdylay_frame_rst_stream_init(spdylay_rst_stream *frame,
int32_t stream_id, uint32_t status_code);
void spdylay_frame_rst_stream_free(spdylay_rst_stream *frame);
/*
* Returns 1 if the first byte of this frame indicates it is a control
* frame.

View File

@ -52,7 +52,7 @@ int spdylay_session_client_new(spdylay_session **session_ptr,
}
memset(*session_ptr, 0, sizeof(spdylay_session));
(*session_ptr)->next_stream_id = 1;
(*session_ptr)->last_accepted_stream_id = 0;
(*session_ptr)->last_recv_stream_id = 0;
r = spdylay_zlib_deflate_hd_init(&(*session_ptr)->hd_deflater);
if(r != 0) {
@ -157,6 +157,25 @@ int spdylay_session_add_frame(spdylay_session *session,
return 0;
}
int spdylay_session_add_rst_stream(spdylay_session *session,
int32_t stream_id, uint32_t status_code)
{
int r;
spdylay_frame *frame;
frame = malloc(sizeof(spdylay_frame));
if(frame == NULL) {
return SPDYLAY_ERR_NOMEM;
}
spdylay_frame_rst_stream_init(&frame->rst_stream, stream_id, status_code);
r = spdylay_session_add_frame(session, SPDYLAY_RST_STREAM, frame);
if(r != 0) {
spdylay_frame_rst_stream_free(&frame->rst_stream);
free(frame);
return r;
}
return 0;
}
int spdylay_session_open_stream(spdylay_session *session, int32_t stream_id)
{
int r;
@ -318,45 +337,143 @@ static void spdylay_debug_print_nv(char **nv)
}
}
int spdylay_session_process_ctrl_frame(spdylay_session *session)
static void spdylay_session_call_on_ctrl_frame_received
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame)
{
if(session->callbacks.on_ctrl_recv_callback) {
session->callbacks.on_ctrl_recv_callback
(session, type, frame, session->user_data);
}
}
/*
* Checks whether received stream_id is valid.
* This function returns 1 if it succeeds, or 0.
*/
static int spdylay_session_is_new_peer_stream_id(spdylay_session *session,
int32_t stream_id)
{
if(stream_id == 0) {
return 0;
}
if(session->server) {
return stream_id % 2 == 1 && session->last_recv_stream_id < stream_id;
} else {
return stream_id % 2 == 0 && session->last_recv_stream_id < stream_id;
}
}
/*
* Validates SYN_STREAM frame |frame|. This function returns 0 if it
* succeeds, or -1.
*/
static int spdylay_session_validate_syn_stream(spdylay_session *session,
spdylay_syn_stream *frame)
{
/* TODO Check assoc_stream_id */
if(spdylay_session_is_new_peer_stream_id(session, frame->stream_id)) {
return 0;
} else {
return -1;
}
}
static spdylay_stream* spdylay_session_get_stream(spdylay_session *session,
int32_t stream_id)
{
return (spdylay_stream*)spdylay_map_find(&session->streams, stream_id);
}
/*
* Validates SYN_REPLY frame |frame|. This function returns 0 if it
* succeeds, or -1.
*/
static int spdylay_session_validate_syn_reply(spdylay_session *session,
spdylay_syn_reply *frame)
{
spdylay_stream *stream;
stream = spdylay_session_get_stream(session, frame->stream_id);
if(stream && stream->state == SPDYLAY_STREAM_OPENING) {
return 0;
} else {
return -1;
}
}
static int spdylay_session_handle_invalid_ctrl_frame(spdylay_session *session,
int32_t stream_id,
spdylay_frame_type type,
spdylay_frame *frame)
{
int r;
r = spdylay_session_add_rst_stream(session, stream_id,
SPDYLAY_PROTOCOL_ERROR);
if(r != 0) {
return r;
}
if(session->callbacks.on_invalid_ctrl_recv_callback) {
session->callbacks.on_invalid_ctrl_recv_callback
(session, type, frame, session->user_data);
}
}
int spdylay_session_process_ctrl_frame(spdylay_session *session)
{
int r = 0;
uint16_t type;
spdylay_frame frame;
memcpy(&type, &session->iframe.headbuf[2], sizeof(uint16_t));
type = ntohs(type);
switch(type) {
case SPDYLAY_SYN_STREAM: {
spdylay_syn_stream frame;
case SPDYLAY_SYN_STREAM:
printf("SYN_STREAM\n");
r = spdylay_frame_unpack_syn_stream(&frame, session->iframe.headbuf,
r = spdylay_frame_unpack_syn_stream(&frame.syn_stream,
session->iframe.headbuf,
sizeof(session->iframe.headbuf),
session->iframe.buf,
session->iframe.len,
&session->hd_inflater);
if(r == 0) {
spdylay_debug_print_nv(frame.nv);
spdylay_frame_syn_stream_free(&frame);
r = spdylay_session_validate_syn_stream(session, &frame.syn_stream);
if(r == 0) {
spdylay_session_call_on_ctrl_frame_received(session, type, &frame);
} else {
r = spdylay_session_handle_invalid_ctrl_frame
(session, frame.syn_stream.stream_id, type, &frame);
}
spdylay_frame_syn_stream_free(&frame.syn_stream);
} else {
/* TODO if r indicates mulformed NV pairs (multiple nulls) or
invalid frame, send RST_STREAM with PROTOCOL_ERROR. Same for
other control frames. */
}
break;
}
case SPDYLAY_SYN_REPLY: {
spdylay_syn_reply frame;
case SPDYLAY_SYN_REPLY:
printf("SYN_REPLY\n");
r = spdylay_frame_unpack_syn_reply(&frame, session->iframe.headbuf,
r = spdylay_frame_unpack_syn_reply(&frame.syn_reply,
session->iframe.headbuf,
sizeof(session->iframe.headbuf),
session->iframe.buf,
session->iframe.len,
&session->hd_inflater);
if(r == 0) {
spdylay_debug_print_nv(frame.nv);
spdylay_frame_syn_reply_free(&frame);
r = spdylay_session_validate_syn_reply(session, &frame.syn_reply);
if(r == 0) {
spdylay_session_call_on_ctrl_frame_received(session, type, &frame);
} else {
r = spdylay_session_handle_invalid_ctrl_frame
(session, frame.syn_reply.stream_id, type, &frame);
}
spdylay_frame_syn_reply_free(&frame.syn_reply);
}
break;
}
default:
/* ignore */
printf("Received control frame type %x\n", type);
}
if(r != 0) {
return r;
}
return 0;
}

View File

@ -75,7 +75,7 @@ typedef struct {
typedef struct spdylay_session {
uint8_t server;
int32_t next_stream_id;
int32_t last_accepted_stream_id;
int32_t last_recv_stream_id;
spdylay_map /* <spdylay_stream*> */ streams;
spdylay_pq /* <spdylay_outbound_item*> */ ob_pq;
@ -98,6 +98,9 @@ int spdylay_session_add_frame(spdylay_session *session,
spdylay_frame_type frame_type,
spdylay_frame *frame);
int spdylay_session_add_rst_stream(spdylay_session *session,
int32_t stream_id, uint32_t status_code);
int spdylay_session_open_stream(spdylay_session *session, int32_t stream_id);
int spdylay_session_close_stream(spdylay_session *session, int32_t stream_id);

View File

@ -32,8 +32,15 @@
#include <spdylay/spdylay.h>
typedef enum {
/* For stream initiator: SYN_STREAM has been sent, but SYN_REPLY is
not received yet. For receiver: SYN_STREAM has been received,
but it does not send SYN_REPLY yet. */
SPDYLAY_STREAM_OPENING,
/* For stream initiator: SYN_REPLY is received. For receiver:
SYN_REPLY is sent. */
SPDYLAY_STREAM_OPENED,
/* RST_STREAM is received, but somehow we need to keep stream in
memory. */
SPDYLAY_STREAM_CLOSING
} spdylay_stream_state;

View File

@ -120,7 +120,7 @@ ssize_t spdylay_zlib_inflate_hd(spdylay_zlib *inflater,
inflater->zst.avail_out = spdylay_buffer_avail(buf);
inflater->zst.next_out = spdylay_buffer_get(buf);
r = inflate(&inflater->zst, Z_NO_FLUSH);
if(r == Z_STREAM_ERROR || r == Z_STREAM_END) {
if(r == Z_STREAM_ERROR || r == Z_STREAM_END || r == Z_DATA_ERROR) {
return SPDYLAY_ERR_ZLIB;
} else if(r == Z_NEED_DICT) {
if(Z_OK != inflateSetDictionary(&inflater->zst, (uint8_t*)hd_dict,

View File

@ -67,6 +67,8 @@ int main()
!CU_add_test(pSuite, "buffer", test_spdylay_buffer) ||
!CU_add_test(pSuite, "zlib", test_spdylay_zlib) ||
!CU_add_test(pSuite, "session_recv", test_spdylay_session_recv) ||
!CU_add_test(pSuite, "session_recv_invalid_stream_id",
test_spdylay_session_recv_invalid_stream_id) ||
!CU_add_test(pSuite, "session_add_frame",
test_spdylay_session_add_frame) ||
!CU_add_test(pSuite, "frame_unpack_nv", test_spdylay_frame_unpack_nv) ||

View File

@ -48,6 +48,7 @@ typedef struct {
typedef struct {
accumulator *acc;
scripted_data_feed *df;
int flags;
} my_user_data;
static void scripted_data_feed_init(scripted_data_feed *df,
@ -87,6 +88,15 @@ static ssize_t accumulator_send_callback(spdylay_session *session,
return len;
}
static void on_invalid_ctrl_recv_callback(spdylay_session *session,
spdylay_frame_type type,
spdylay_frame *frame,
void *user_data)
{
my_user_data *ud = (my_user_data*)user_data;
++ud->flags;
}
static char** dup_nv(const char **src)
{
int i;
@ -175,3 +185,46 @@ void test_spdylay_session_add_frame()
spdylay_session_del(session);
}
void test_spdylay_session_recv_invalid_stream_id()
{
spdylay_session *session;
spdylay_session_callbacks callbacks = {
NULL,
scripted_recv_callback,
NULL,
on_invalid_ctrl_recv_callback
};
scripted_data_feed df;
my_user_data user_data;
const char *nv[] = { NULL };
uint8_t *framedata;
size_t framelen;
spdylay_frame frame;
user_data.df = &df;
user_data.flags = 0;
spdylay_session_client_new(&session, &callbacks, &user_data);
spdylay_frame_syn_stream_init(&frame.syn_stream, 0, 1, 0, 3, dup_nv(nv));
framelen = spdylay_frame_pack_syn_stream(&framedata, &frame.syn_stream,
&session->hd_deflater);
scripted_data_feed_init(&df, framedata, framelen);
free(framedata);
spdylay_frame_syn_stream_free(&frame.syn_stream);
CU_ASSERT(0 == spdylay_session_recv(session));
CU_ASSERT(1 == user_data.flags);
spdylay_frame_syn_reply_init(&frame.syn_reply, 0, 100, dup_nv(nv));
framelen = spdylay_frame_pack_syn_reply(&framedata, &frame.syn_reply,
&session->hd_deflater);
scripted_data_feed_init(&df, framedata, framelen);
free(framedata);
spdylay_frame_syn_reply_free(&frame.syn_reply);
CU_ASSERT(0 == spdylay_session_recv(session));
CU_ASSERT(2 == user_data.flags);
spdylay_session_del(session);
}

View File

@ -26,6 +26,7 @@
#define SPDYLAY_SESSION_TEST_H
void test_spdylay_session_recv();
void test_spdylay_session_recv_invalid_stream_id();
void test_spdylay_session_add_frame();
#endif // SPDYLAY_SESSION_TEST_H