Pass unknown SETTINGS values to nghttp2_on_frame_recv_callback

This commit is contained in:
Tatsuhiro Tsujikawa 2016-04-10 16:36:04 +09:00
parent d88f962565
commit 40f3779eb1
4 changed files with 77 additions and 68 deletions

View File

@ -463,25 +463,11 @@ size_t nghttp2_frame_pack_settings_payload(uint8_t *buf,
return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv; return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv;
} }
int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
nghttp2_settings_entry *iv, nghttp2_settings_entry *iv,
size_t niv, nghttp2_mem *mem) { size_t niv) {
size_t payloadlen = niv * sizeof(nghttp2_settings_entry); frame->iv = iv;
if (niv == 0) {
frame->iv = NULL;
} else {
frame->iv = nghttp2_mem_malloc(mem, payloadlen);
if (frame->iv == NULL) {
return NGHTTP2_ERR_NOMEM;
}
memcpy(frame->iv, iv, payloadlen);
}
frame->niv = niv; frame->niv = niv;
return 0;
} }
void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,

View File

@ -215,18 +215,12 @@ void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,
const uint8_t *payload); const uint8_t *payload);
/* /*
* Makes a copy of |iv| in frame->settings.iv. The |niv| is assigned * Initializes payload of frame->settings. The |frame| takes
* to frame->settings.niv. * ownership of |iv|.
*
* This function returns 0 if it succeeds or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/ */
int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
nghttp2_settings_entry *iv, nghttp2_settings_entry *iv,
size_t niv, nghttp2_mem *mem); size_t niv);
/* /*
* Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are * Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are

View File

@ -305,6 +305,13 @@ static void session_inbound_frame_reset(nghttp2_session *session) {
break; break;
case NGHTTP2_SETTINGS: case NGHTTP2_SETTINGS:
nghttp2_frame_settings_free(&iframe->frame.settings, mem); nghttp2_frame_settings_free(&iframe->frame.settings, mem);
nghttp2_mem_free(mem, iframe->iv);
iframe->iv = NULL;
iframe->niv = 0;
iframe->max_niv = 0;
break; break;
case NGHTTP2_PUSH_PROMISE: case NGHTTP2_PUSH_PROMISE:
nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem); nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem);
@ -351,12 +358,8 @@ static void session_inbound_frame_reset(nghttp2_session *session) {
iframe->raw_lbuf = NULL; iframe->raw_lbuf = NULL;
iframe->niv = 0;
iframe->payloadleft = 0; iframe->payloadleft = 0;
iframe->padlen = 0; iframe->padlen = 0;
iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].settings_id =
NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].value = UINT32_MAX;
} }
static void init_settings(nghttp2_settings_storage *settings) { static void init_settings(nghttp2_settings_storage *settings) {
@ -4365,39 +4368,39 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
} }
static int session_process_settings_frame(nghttp2_session *session) { static int session_process_settings_frame(nghttp2_session *session) {
int rv;
nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_inbound_frame *iframe = &session->iframe;
nghttp2_frame *frame = &iframe->frame; nghttp2_frame *frame = &iframe->frame;
size_t i; size_t i;
nghttp2_settings_entry min_header_size_entry; nghttp2_settings_entry min_header_size_entry;
nghttp2_mem *mem;
mem = &session->mem; if (iframe->max_niv) {
min_header_size_entry = iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1]; min_header_size_entry = iframe->iv[iframe->max_niv - 1];
if (min_header_size_entry.value < UINT32_MAX) { if (min_header_size_entry.value < UINT32_MAX) {
/* If we have less value, then we must have /* If we have less value, then we must have
SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */ SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */
for (i = 0; i < iframe->niv; ++i) { for (i = 0; i < iframe->niv; ++i) {
if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {
break; break;
}
}
assert(i < iframe->niv);
if (min_header_size_entry.value != iframe->iv[i].value) {
iframe->iv[iframe->niv++] = iframe->iv[i];
iframe->iv[i] = min_header_size_entry;
} }
} }
assert(i < iframe->niv);
if (min_header_size_entry.value != iframe->iv[i].value) {
iframe->iv[iframe->niv++] = iframe->iv[i];
iframe->iv[i] = min_header_size_entry;
}
} }
rv = nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv, nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv,
iframe->niv, mem); iframe->niv);
if (rv != 0) {
assert(nghttp2_is_fatal(rv)); iframe->iv = NULL;
return rv; iframe->niv = 0;
} iframe->max_niv = 0;
return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */); return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */);
} }
@ -5053,6 +5056,7 @@ static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe,
*/ */
static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) { static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
nghttp2_settings_entry iv; nghttp2_settings_entry iv;
nghttp2_settings_entry *min_header_table_size_entry;
size_t i; size_t i;
nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos); nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos);
@ -5066,8 +5070,11 @@ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
break; break;
default: default:
DEBUGF(fprintf(stderr, "recv: ignore unknown settings id=0x%02x\n", DEBUGF(
iv.settings_id)); fprintf(stderr, "recv: unknown settings id=0x%02x\n", iv.settings_id));
iframe->iv[iframe->niv++] = iv;
return; return;
} }
@ -5082,10 +5089,13 @@ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
iframe->iv[iframe->niv++] = iv; iframe->iv[iframe->niv++] = iv;
} }
if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE && if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {
iv.value < iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].value) { /* Keep track of minimum value of SETTINGS_HEADER_TABLE_SIZE */
min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];
iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1] = iv; if (iv.value < min_header_table_size_entry->value) {
min_header_table_size_entry->value = iv.value;
}
} }
} }
@ -5458,6 +5468,25 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
iframe->state = NGHTTP2_IB_READ_SETTINGS; iframe->state = NGHTTP2_IB_READ_SETTINGS;
if (iframe->payloadleft) { if (iframe->payloadleft) {
nghttp2_settings_entry *min_header_table_size_entry;
/* We allocate iv with addtional one entry, to store the
minimum header table size. */
iframe->max_niv =
iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) *
iframe->max_niv);
if (!iframe->iv) {
return NGHTTP2_ERR_NOMEM;
}
min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];
min_header_table_size_entry->settings_id =
NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
min_header_table_size_entry->value = UINT32_MAX;
inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
break; break;
} }

View File

@ -120,18 +120,16 @@ typedef enum {
NGHTTP2_IB_READ_EXTENSION_PAYLOAD NGHTTP2_IB_READ_EXTENSION_PAYLOAD
} nghttp2_inbound_state; } nghttp2_inbound_state;
#define NGHTTP2_INBOUND_NUM_IV 7
typedef struct { typedef struct {
nghttp2_frame frame; nghttp2_frame frame;
/* Storage for extension frame payload. frame->ext.payload points /* Storage for extension frame payload. frame->ext.payload points
to this structure to avoid frequent memory allocation. */ to this structure to avoid frequent memory allocation. */
nghttp2_ext_frame_payload ext_frame_payload; nghttp2_ext_frame_payload ext_frame_payload;
/* The received SETTINGS entry. The protocol says that we only cares /* The received SETTINGS entry. For the standard settings entries,
about the defined settings ID. If unknown ID is received, it is we only keep the last seen value. For
ignored. We use last entry to hold minimum header table size if SETTINGS_HEADER_TABLE_SIZE, we also keep minimum value in the
same settings are multiple times. */ last index. */
nghttp2_settings_entry iv[NGHTTP2_INBOUND_NUM_IV]; nghttp2_settings_entry *iv;
/* buffer pointers to small buffer, raw_sbuf */ /* buffer pointers to small buffer, raw_sbuf */
nghttp2_buf sbuf; nghttp2_buf sbuf;
/* buffer pointers to large buffer, raw_lbuf */ /* buffer pointers to large buffer, raw_lbuf */
@ -140,6 +138,8 @@ typedef struct {
uint8_t *raw_lbuf; uint8_t *raw_lbuf;
/* The number of entry filled in |iv| */ /* The number of entry filled in |iv| */
size_t niv; size_t niv;
/* The number of entries |iv| can store. */
size_t max_niv;
/* How many bytes we still need to receive for current frame */ /* How many bytes we still need to receive for current frame */
size_t payloadleft; size_t payloadleft;
/* padding length for the current frame */ /* padding length for the current frame */