From f59e94a7b666fb55fe164c74b14528a7e97a668d Mon Sep 17 00:00:00 2001 From: Joris Vink Date: Sat, 13 Jul 2013 19:56:38 +0200 Subject: [PATCH] Add spdy_session_teardown() which can properly teardown a SPDY session. Use this throughout the spdy code to propagate session errors (if any) occur. At the same time fix BSD's missing CONN_WRITE_BLOCK --- includes/kore.h | 2 + includes/spdy.h | 6 +++ modules/example/module.conf | 1 + modules/example/src/example.c | 9 ++++ src/bsd.c | 6 ++- src/http.c | 7 ++- src/linux.c | 3 +- src/spdy.c | 92 +++++++++++++++++++++++++++-------- src/worker.c | 3 +- 9 files changed, 104 insertions(+), 25 deletions(-) diff --git a/includes/kore.h b/includes/kore.h index 5501ddc..30957c7 100644 --- a/includes/kore.h +++ b/includes/kore.h @@ -92,6 +92,7 @@ struct listener { #define CONN_WRITE_POSSIBLE 0x02 #define CONN_WRITE_BLOCK 0x04 #define CONN_IDLE_TIMER_ACT 0x10 +#define CONN_READ_BLOCK 0x20 #define KORE_IDLE_TIMER_MAX 20000 @@ -299,6 +300,7 @@ int spdy_stream_get_header(struct spdy_header_block *, char *, char **); int spdy_frame_recv(struct netbuf *); +void spdy_session_teardown(struct connection *c, u_int8_t); void spdy_frame_send(struct connection *, u_int16_t, u_int8_t, u_int32_t, struct spdy_stream *, u_int32_t); void spdy_header_block_add(struct spdy_header_block *, diff --git a/includes/spdy.h b/includes/spdy.h index 801d1b0..9357c51 100644 --- a/includes/spdy.h +++ b/includes/spdy.h @@ -76,9 +76,15 @@ extern const unsigned char SPDY_dictionary_txt[]; #define SPDY_CTRL_FRAME_SYN_REPLY 2 #define SPDY_CTRL_FRAME_SETTINGS 4 #define SPDY_CTRL_FRAME_PING 6 +#define SPDY_CTRL_FRAME_GOAWAY 7 #define SPDY_CTRL_FRAME_WINDOW 9 #define SPDY_DATA_FRAME 99 +/* session error codes */ +#define SPDY_SESSION_ERROR_OK 0 +#define SPDY_SESSION_ERROR_PROTOCOL 1 +#define SPDY_SESSION_ERROR_INTERNAL 2 + /* flags */ #define FLAG_FIN 0x01 #define FLAG_UNIDIRECTIONAL 0x02 diff --git a/modules/example/module.conf b/modules/example/module.conf index 800ff48..190de6d 100644 --- a/modules/example/module.conf +++ b/modules/example/module.conf @@ -55,6 +55,7 @@ domain 10.211.55.3 { static / serve_index static /intro.jpg serve_intro static /b64test serve_b64test + static /spdy-reset serve_spdyreset } #domain domain.com { diff --git a/modules/example/src/example.c b/modules/example/src/example.c index 2418063..24839ef 100644 --- a/modules/example/src/example.c +++ b/modules/example/src/example.c @@ -23,6 +23,8 @@ int serve_style_css(struct http_request *); int serve_index(struct http_request *); int serve_intro(struct http_request *); int serve_b64test(struct http_request *); +int serve_spdyreset(struct http_request *); + void test_base64(u_int8_t *, u_int32_t, struct kore_buf *); char *b64tests[] = { @@ -111,6 +113,13 @@ serve_b64test(struct http_request *req) return (ret); } +int +serve_spdyreset(struct http_request *req) +{ + spdy_session_teardown(req->owner, SPDY_SESSION_ERROR_OK); + return (KORE_RESULT_OK); +} + void test_base64(u_int8_t *src, u_int32_t slen, struct kore_buf *res) { diff --git a/src/bsd.c b/src/bsd.c index d381f52..0df0415 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -98,9 +98,11 @@ kore_platform_event_wait(void) } } else { c = (struct connection *)events[i].udata; - if (events[i].filter == EVFILT_READ) + if (events[i].filter == EVFILT_READ && + !(c->flags & CONN_READ_BLOCK)) c->flags |= CONN_READ_POSSIBLE; - if (events[i].filter == EVFILT_WRITE) + if (events[i].filter == EVFILT_WRITE && + !(c->flags & CONN_WRITE_BLOCK)) c->flags |= CONN_WRITE_POSSIBLE; if (!kore_connection_handle(c)) { diff --git a/src/http.c b/src/http.c index 384e72b..e1b7cb8 100644 --- a/src/http.c +++ b/src/http.c @@ -222,8 +222,11 @@ http_response(struct http_request *req, int status, u_int8_t *d, u_int32_t len) spdy_header_block_add(hblock, hdr->header, hdr->value); htext = spdy_header_block_release(req->owner, hblock, &hlen); - if (htext == NULL) - return (KORE_RESULT_ERROR); + if (htext == NULL) { + spdy_session_teardown(req->owner, + SPDY_SESSION_ERROR_INTERNAL); + return (KORE_RESULT_OK); + } spdy_frame_send(req->owner, SPDY_CTRL_FRAME_SYN_REPLY, 0, hlen, req->stream, 0); diff --git a/src/linux.c b/src/linux.c index b507508..1cbe3d2 100644 --- a/src/linux.c +++ b/src/linux.c @@ -104,7 +104,8 @@ kore_platform_event_wait(void) } } else { c = (struct connection *)events[i].data.ptr; - if (events[i].events & EPOLLIN) + if (events[i].events & EPOLLIN && + !(c->flags & CONN_READ_BLOCK)) c->flags |= CONN_READ_POSSIBLE; if (events[i].events & EPOLLOUT && !(c->flags & CONN_WRITE_BLOCK)) diff --git a/src/spdy.c b/src/spdy.c index 21ba940..48f84bf 100644 --- a/src/spdy.c +++ b/src/spdy.c @@ -24,11 +24,12 @@ static int spdy_ctrl_frame_ping(struct netbuf *); static int spdy_ctrl_frame_window(struct netbuf *); static int spdy_data_frame_recv(struct netbuf *); static int spdy_frame_send_done(struct netbuf *); +static int spdy_goaway_send_done(struct netbuf *); + static void spdy_update_wsize(struct connection *, struct spdy_stream *, u_int32_t); static void spdy_stream_close(struct connection *, struct spdy_stream *); - static int spdy_zlib_inflate(struct connection *, u_int8_t *, size_t, u_int8_t **, u_int32_t *); static int spdy_zlib_deflate(struct connection *, u_int8_t *, @@ -61,7 +62,9 @@ spdy_frame_recv(struct netbuf *nb) if (ctrl.version != 3) { kore_debug("protocol mismatch (recv version %d)", ctrl.version); - return (KORE_RESULT_ERROR); + + spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); + return (KORE_RESULT_OK); } switch (ctrl.type) { @@ -108,6 +111,9 @@ spdy_frame_recv(struct netbuf *nb) if (r == KORE_RESULT_OK) { net_recv_queue(c, SPDY_FRAME_SIZE, 0, NULL, spdy_frame_recv); + } else { + r = KORE_RESULT_OK; + spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); } return (r); @@ -145,6 +151,14 @@ spdy_frame_send(struct connection *c, u_int16_t type, u_int8_t flags, length = 12; break; + case SPDY_CTRL_FRAME_GOAWAY: + net_write16(&nb[0], 3); + nb[0] |= (1 << 7); + net_write16(&nb[2], type); + net_write32(&nb[4], len); + nb[4] = flags; + length = 8; + break; case SPDY_DATA_FRAME: net_write32(&nb[0], s->stream_id); nb[0] &= ~(1 << 7); @@ -309,6 +323,26 @@ spdy_frame_data_done(struct netbuf *nb) return (KORE_RESULT_OK); } +void +spdy_session_teardown(struct connection *c, u_int8_t err) +{ + u_int8_t d[8]; + + kore_debug("spdy_session_teardown(%p, %d)", c, err); + + net_write32((u_int8_t *)&d[0], c->client_stream_id); + net_write32((u_int8_t *)&d[4], err); + + spdy_frame_send(c, SPDY_CTRL_FRAME_GOAWAY, 0, 8, NULL, 0); + net_send_queue(c, d, sizeof(d), 0, NULL, spdy_goaway_send_done); + + c->flags &= ~CONN_READ_POSSIBLE; + c->flags |= CONN_READ_BLOCK; + + c->idle_timer.length = 5; + kore_connection_start_idletimer(c); +} + static int spdy_ctrl_frame_syn_stream(struct netbuf *nb) { @@ -337,19 +371,22 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb) if ((syn.stream_id % 2) == 0 || syn.stream_id == 0) { kore_debug("client sent incorrect id for SPDY_SYN_STREAM (%d)", syn.stream_id); - return (KORE_RESULT_ERROR); + spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); + return (KORE_RESULT_OK); } /* XXX need to send protocol error. */ if (syn.stream_id < c->client_stream_id) { kore_debug("client sent incorrect id SPDY_SYN_STREAM (%d < %d)", syn.stream_id, c->client_stream_id); - return (KORE_RESULT_ERROR); + spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); + return (KORE_RESULT_OK); } if ((s = spdy_stream_lookup(c, syn.stream_id)) != NULL) { kore_debug("duplicate SPDY_SYN_STREAM (%d)", syn.stream_id); - return (KORE_RESULT_ERROR); + spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); + return (KORE_RESULT_OK); } s = (struct spdy_stream *)kore_malloc(sizeof(*s)); @@ -366,7 +403,8 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb) kore_mem_free(s->hblock->header_block); kore_mem_free(s->hblock); kore_mem_free(s); - return (KORE_RESULT_ERROR); + spdy_session_teardown(c, SPDY_SESSION_ERROR_INTERNAL); + return (KORE_RESULT_OK); } s->hblock->header_pairs = net_read32(s->hblock->header_block); @@ -376,19 +414,20 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb) host = NULL; method = NULL; -#define GET_HEADER(n, r) \ - if (!spdy_stream_get_header(s->hblock, n, r)) { \ +#define GET_HEADER(n, r) \ + if (!spdy_stream_get_header(s->hblock, n, r)) { \ kore_mem_free(s->hblock->header_block); \ kore_mem_free(s->hblock); \ kore_mem_free(s); \ - kore_debug("no such header: %s", n); \ - if (path != NULL) \ + kore_debug("no such header: %s", n); \ + if (path != NULL) \ kore_mem_free(path); \ - if (host != NULL) \ + if (host != NULL) \ kore_mem_free(host); \ - if (method != NULL) \ + if (method != NULL) \ kore_mem_free(method); \ - return (KORE_RESULT_ERROR); \ + spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); \ + return (KORE_RESULT_OK); \ } GET_HEADER(":path", &path); @@ -403,7 +442,8 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb) kore_mem_free(s->hblock->header_block); kore_mem_free(s->hblock); kore_mem_free(s); - return (KORE_RESULT_ERROR); + spdy_session_teardown(c, SPDY_SESSION_ERROR_INTERNAL); + return (KORE_RESULT_OK); } kore_mem_free(path); @@ -432,7 +472,8 @@ spdy_ctrl_frame_settings(struct netbuf *nb) if (length != ((ecount * 8) + 4)) { kore_debug("ecount is not correct (%d != %d)", length, (ecount * 8) + 4); - return (KORE_RESULT_ERROR); + spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); + return (KORE_RESULT_OK); } buf = nb->buf + SPDY_FRAME_SIZE + 4; @@ -469,7 +510,8 @@ spdy_ctrl_frame_ping(struct netbuf *nb) /* XXX todo - check if we sent the ping. */ if ((id % 2) == 0) { kore_debug("received malformed client PING (%d)", id); - return (KORE_RESULT_ERROR); + spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); + return (KORE_RESULT_OK); } spdy_frame_send(c, SPDY_CTRL_FRAME_PING, 0, 4, NULL, id); @@ -489,7 +531,8 @@ spdy_ctrl_frame_window(struct netbuf *nb) if ((s = spdy_stream_lookup(c, stream_id)) == NULL) { kore_debug("received WINDOW_UPDATE for nonexistant stream"); kore_debug("stream_id: %d", stream_id); - return (KORE_RESULT_ERROR); + spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); + return (KORE_RESULT_OK); } kore_debug("SPDY_WINDOW_UPDATE: %d:%d", stream_id, window_size); @@ -521,13 +564,15 @@ spdy_data_frame_recv(struct netbuf *nb) data.flags, data.length); if ((s = spdy_stream_lookup(c, data.stream_id)) == NULL) { - kore_debug("session data for incorrect stream"); - return (KORE_RESULT_OK); + kore_debug("session data for non-existant stream"); + /* stream error */ + return (KORE_RESULT_ERROR); } req = (struct http_request *)s->httpreq; if (req->method != HTTP_METHOD_POST) { kore_debug("data frame for non post received"); + /* stream error */ return (KORE_RESULT_ERROR); } @@ -573,6 +618,15 @@ spdy_frame_send_done(struct netbuf *nb) return (KORE_RESULT_OK); } +static int +spdy_goaway_send_done(struct netbuf *nb) +{ + struct connection *c = (struct connection *)nb->owner; + + kore_connection_disconnect(c); + return (KORE_RESULT_OK); +} + static void spdy_update_wsize(struct connection *c, struct spdy_stream *s, u_int32_t len) { diff --git a/src/worker.c b/src/worker.c index 3f89853..a22cbb3 100644 --- a/src/worker.c +++ b/src/worker.c @@ -231,7 +231,8 @@ kore_worker_entry(struct kore_worker *kw) idle_check = now; TAILQ_FOREACH(c, &worker_clients, list) { if (c->proto == CONN_PROTO_SPDY && - !(c->flags & CONN_WRITE_BLOCK)) + !(c->flags & CONN_WRITE_BLOCK) && + !(c->flags & CONN_READ_BLOCK)) continue; if (!(c->flags & CONN_IDLE_TIMER_ACT)) continue;