1
0
mirror of https://github.com/moparisthebest/spdylay synced 2024-11-05 09:05:07 -05:00
spdylay/lib/spdylay_frame.c
Tatsuhiro Tsujikawa 4dd9c32c25 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.
2012-05-25 13:49:18 +09:00

1271 lines
38 KiB
C

/*
* Spdylay - SPDY Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "spdylay_frame.h"
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include "spdylay_helper.h"
#include "spdylay_net.h"
size_t spdylay_frame_get_len_size(uint16_t version)
{
if(SPDYLAY_PROTO_SPDY2 == version) {
return 2;
} else if(SPDYLAY_PROTO_SPDY3 == version) {
return 4;
} else {
/* Unsupported version */
return 0;
}
}
static uint8_t spdylay_unpack_pri(const uint8_t *data)
{
return (data[0] >> 6) & 0x3;
}
static uint8_t* spdylay_pack_str(uint8_t *buf, const char *str, size_t len,
size_t len_size)
{
spdylay_frame_put_nv_len(buf, len, len_size);
buf += len_size;
memcpy(buf, str, len);
return buf+len;
}
static void spdylay_frame_pack_ctrl_hd(uint8_t* buf, const spdylay_ctrl_hd *hd)
{
spdylay_put_uint16be(&buf[0], hd->version);
buf[0] |= 1 << 7;
spdylay_put_uint16be(&buf[2], hd->type);
spdylay_put_uint32be(&buf[4], hd->length);
buf[4] = hd->flags;
}
static void spdylay_frame_unpack_ctrl_hd(spdylay_ctrl_hd *hd,
const uint8_t* buf)
{
hd->version = spdylay_get_uint16(buf) & SPDYLAY_VERSION_MASK;
hd->type = spdylay_get_uint16(&buf[2]);
hd->flags = buf[4];
hd->length = spdylay_get_uint32(&buf[4]) & SPDYLAY_LENGTH_MASK;
}
ssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr,
size_t *buflen_ptr,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
char **nv, size_t nv_offset,
size_t len_size,
spdylay_zlib *deflater)
{
size_t nvspace;
size_t maxframelen;
ssize_t framelen;
int r;
nvspace = spdylay_frame_count_nv_space(nv, len_size);
r = spdylay_reserve_buffer(nvbuf_ptr, nvbuflen_ptr, nvspace);
if(r != 0) {
return SPDYLAY_ERR_NOMEM;
}
maxframelen = nv_offset+spdylay_zlib_deflate_hd_bound(deflater, nvspace);
r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, maxframelen);
if(r != 0) {
return SPDYLAY_ERR_NOMEM;
}
spdylay_frame_pack_nv(*nvbuf_ptr, nv, len_size);
framelen = spdylay_zlib_deflate_hd(deflater,
(*buf_ptr)+nv_offset,
maxframelen-nv_offset,
*nvbuf_ptr, nvspace);
if(framelen < 0) {
return framelen;
}
framelen += nv_offset;
return framelen;
}
int spdylay_frame_count_unpack_nv_space(size_t *nvlen_ptr, size_t *buflen_ptr,
spdylay_buffer *in, size_t len_size)
{
uint32_t n;
size_t buflen = 0;
size_t nvlen = 0;
size_t off = 0;
size_t inlen = spdylay_buffer_length(in);
size_t i;
spdylay_buffer_reader reader;
if(inlen < len_size) {
return SPDYLAY_ERR_INVALID_FRAME;
}
spdylay_buffer_reader_init(&reader, in);
/* TODO limit n in a reasonable number */
n = spdylay_frame_get_nv_len(&reader, len_size);
off += len_size;
for(i = 0; i < n; ++i) {
uint32_t len;
size_t j;
for(j = 0; j < 2; ++j) {
if(inlen-off < len_size) {
return SPDYLAY_ERR_INVALID_FRAME;
}
len = spdylay_frame_get_nv_len(&reader, len_size);
off += len_size;
if(inlen-off < len) {
return SPDYLAY_ERR_INVALID_FRAME;
}
buflen += len+1;
off += len;
if(j == 0) {
spdylay_buffer_reader_advance(&reader, len);
}
}
for(j = off, off -= len; off != j; ++off) {
uint8_t b = spdylay_buffer_reader_uint8(&reader);
if(b == '\0') {
++nvlen;
}
}
++nvlen;
}
if(inlen == off) {
*nvlen_ptr = nvlen;
*buflen_ptr = buflen+(nvlen*2+1)*sizeof(char*);
return 0;
} else {
return SPDYLAY_ERR_INVALID_FRAME;
}
}
int spdylay_frame_unpack_nv(char ***nv_ptr, spdylay_buffer *in,
size_t len_size)
{
size_t nvlen, buflen;
int r;
size_t i;
char *buf, **idx, *data;
uint32_t n;
int invalid_header_block = 0;
spdylay_buffer_reader reader;
r = spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, in, len_size);
if(r != 0) {
return r;
}
buf = malloc(buflen);
if(buf == NULL) {
return SPDYLAY_ERR_NOMEM;
}
spdylay_buffer_reader_init(&reader, in);
idx = (char**)buf;
data = buf+(nvlen*2+1)*sizeof(char*);
n = spdylay_frame_get_nv_len(&reader, len_size);
for(i = 0; i < n; ++i) {
uint32_t len;
char *name, *val;
char *stop;
len = spdylay_frame_get_nv_len(&reader, len_size);
if(len == 0) {
invalid_header_block = 1;
}
name = data;
spdylay_buffer_reader_data(&reader, (uint8_t*)data, len);
for(stop = data+len; data != stop; ++data) {
unsigned char c = *data;
if(c < 0x20 || c > 0x7e || ('A' <= c && c <= 'Z')) {
invalid_header_block = 1;
}
}
*data = '\0';
++data;
len = spdylay_frame_get_nv_len(&reader, len_size);
val = data;
spdylay_buffer_reader_data(&reader, (uint8_t*)data, len);
for(stop = data+len; data != stop; ++data) {
if(*data == '\0') {
*idx++ = name;
*idx++ = val;
if(val == data) {
invalid_header_block = 1;
}
val = data+1;
}
}
*data = '\0';
++data;
*idx++ = name;
*idx++ = val;
}
*idx = NULL;
assert((size_t)((char*)idx - buf) == (nvlen*2)*sizeof(char*));
*nv_ptr = (char**)buf;
if(!invalid_header_block) {
spdylay_frame_nv_sort(*nv_ptr);
for(i = 2; i < nvlen*2; i += 2) {
if(strcmp((*nv_ptr)[i-2], (*nv_ptr)[i]) == 0 &&
(*nv_ptr)[i-2] != (*nv_ptr)[i]) {
invalid_header_block = 1;
break;
}
}
}
return invalid_header_block ? SPDYLAY_ERR_INVALID_HEADER_BLOCK : 0;
}
size_t spdylay_frame_count_nv_space(char **nv, size_t len_size)
{
size_t sum = len_size;
int i;
const char *prev = "";
size_t prevlen = 0;
for(i = 0; nv[i]; i += 2) {
const char *key = nv[i];
const char *val = nv[i+1];
size_t keylen = strlen(key);
size_t vallen = strlen(val);
if(prevlen == keylen && memcmp(prev, key, keylen) == 0) {
/* Join previous value, with NULL character */
sum += vallen+1;
} else {
prev = key;
prevlen = keylen;
/* SPDY NV header does not include terminating NULL byte */
sum += keylen+vallen+len_size*2;
}
}
return sum;
}
ssize_t spdylay_frame_pack_nv(uint8_t *buf, char **nv, size_t len_size)
{
int i;
uint8_t *bufp = buf+len_size;
uint32_t num_nv = 0;
/* TODO Join values with same keys, using '\0' as a delimiter */
const char *prev = "";
uint8_t *prev_vallen_buf = NULL;
uint32_t prev_vallen = 0;
for(i = 0; nv[i]; i += 2) {
const char *key = nv[i];
const char *val = nv[i+1];
size_t keylen = strlen(key);
size_t vallen = strlen(val);
if(strcmp(prev, key) == 0) {
prev_vallen += vallen+1;
spdylay_frame_put_nv_len(prev_vallen_buf, prev_vallen, len_size);
*bufp = '\0';
++bufp;
memcpy(bufp, val, vallen);
bufp += vallen;
} else {
++num_nv;
bufp = spdylay_pack_str(bufp, key, keylen, len_size);
prev = key;
prev_vallen_buf = bufp;
prev_vallen = vallen;
bufp = spdylay_pack_str(bufp, val, vallen, len_size);
}
}
spdylay_frame_put_nv_len(buf, num_nv, len_size);
return bufp-buf;
}
int spdylay_frame_is_ctrl_frame(uint8_t first_byte)
{
return first_byte & 0x80;
}
void spdylay_frame_nv_del(char **nv)
{
free(nv);
}
char** spdylay_frame_nv_copy(const char **nv)
{
int i;
char *buf;
char **idx, *data;
size_t buflen = 0;
for(i = 0; nv[i]; ++i) {
buflen += strlen(nv[i])+1;
}
buflen += (i+1)*sizeof(char*);
buf = malloc(buflen);
if(buf == NULL) {
return NULL;
}
idx = (char**)buf;
data = buf+(i+1)*sizeof(char*);
for(i = 0; nv[i]; ++i) {
size_t len = strlen(nv[i])+1;
memcpy(data, nv[i], len);
*idx++ = data;
data += len;
}
*idx = NULL;
return (char**)buf;
}
static int spdylay_string_compar(const void *lhs, const void *rhs)
{
return strcmp(*(char **)lhs, *(char **)rhs);
}
void spdylay_frame_nv_sort(char **nv)
{
int n;
for(n = 0; nv[n]; ++n);
qsort(nv, n/2, 2*sizeof(char*), spdylay_string_compar);
}
void spdylay_frame_nv_downcase(char **nv)
{
int i, j;
for(i = 0; nv[i]; i += 2) {
for(j = 0; nv[i][j] != '\0'; ++j) {
if('A' <= nv[i][j] && nv[i][j] <= 'Z') {
nv[i][j] += 'a'-'A';
}
}
}
}
char** spdylay_frame_nv_norm_copy(const char **nv)
{
char **nv_copy;
nv_copy = spdylay_frame_nv_copy(nv);
if(nv_copy != NULL) {
spdylay_frame_nv_downcase(nv_copy);
spdylay_frame_nv_sort(nv_copy);
}
return nv_copy;
}
/* Table to translate SPDY/3 header names to SPDY/2. */
static const char *spdylay_nv_3to2[] = {
":host", "host",
":method", "method",
":path", "url",
":scheme", "scheme",
":status", "status",
":version", "version",
NULL
};
void spdylay_frame_nv_3to2(char **nv)
{
int i, j;
for(i = 0; nv[i]; i += 2) {
for(j = 0; spdylay_nv_3to2[j]; j += 2) {
if(strcmp(nv[i], spdylay_nv_3to2[j]) == 0) {
nv[i] = (char*)spdylay_nv_3to2[j+1];
break;
}
}
}
}
void spdylay_frame_nv_2to3(char **nv)
{
int i, j;
for(i = 0; nv[i]; i += 2) {
for(j = 0; spdylay_nv_3to2[j]; j += 2) {
if(strcmp(nv[i], spdylay_nv_3to2[j+1]) == 0) {
nv[i] = (char*)spdylay_nv_3to2[j];
break;
}
}
}
}
#define SPDYLAY_HTTPS_PORT 443
int spdylay_frame_nv_set_origin(char **nv, spdylay_origin *origin)
{
int scheme_found, host_found;
int i;
scheme_found = host_found = 0;
for(i = 0; nv[i]; i += 2) {
if(scheme_found == 0 && strcmp(":scheme", nv[i]) == 0) {
size_t len = strlen(nv[i+1]);
if(len <= SPDYLAY_MAX_SCHEME) {
strcpy(origin->scheme, nv[i+1]);
scheme_found = 1;
}
} else if(host_found == 0 && strcmp(":host", nv[i]) == 0) {
size_t len = strlen(nv[i+1]);
char *sep = memchr(nv[i+1], ':', len);
size_t hostlen;
if(sep == NULL) {
origin->port = SPDYLAY_HTTPS_PORT;
sep = nv[i+1]+len;
} else {
unsigned long int port;
errno = 0;
port = strtoul(sep+1, NULL, 10);
if(errno != 0 || port == 0 || port > UINT16_MAX) {
continue;
}
origin->port = port;
}
hostlen = sep-nv[i+1];
if(hostlen > SPDYLAY_MAX_HOSTNAME) {
continue;
}
memcpy(origin->host, nv[i+1], hostlen);
origin->host[hostlen] = '\0';
host_found = 1;
}
}
if(scheme_found && host_found) {
return 0;
} else {
return SPDYLAY_ERR_INVALID_ARGUMENT;
}
}
void spdylay_frame_syn_stream_init(spdylay_syn_stream *frame,
uint16_t version, uint8_t flags,
int32_t stream_id, int32_t assoc_stream_id,
uint8_t pri, char **nv)
{
memset(frame, 0, sizeof(spdylay_syn_stream));
frame->hd.version = version;
frame->hd.type = SPDYLAY_SYN_STREAM;
frame->hd.flags = flags;
frame->stream_id = stream_id;
frame->assoc_stream_id = assoc_stream_id;
frame->pri = pri;
frame->nv = nv;
}
void spdylay_frame_syn_stream_free(spdylay_syn_stream *frame)
{
spdylay_frame_nv_del(frame->nv);
}
void spdylay_frame_syn_reply_init(spdylay_syn_reply *frame,
uint16_t version, uint8_t flags,
int32_t stream_id, char **nv)
{
memset(frame, 0, sizeof(spdylay_syn_reply));
frame->hd.version = version;
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_del(frame->nv);
}
void spdylay_frame_ping_init(spdylay_ping *frame,
uint16_t version, uint32_t unique_id)
{
memset(frame, 0, sizeof(spdylay_ping));
frame->hd.version = version;
frame->hd.type = SPDYLAY_PING;
frame->hd.flags = SPDYLAY_CTRL_FLAG_NONE;
frame->hd.length = 4;
frame->unique_id = unique_id;
}
void spdylay_frame_ping_free(spdylay_ping *frame)
{}
void spdylay_frame_goaway_init(spdylay_goaway *frame,
uint16_t version, int32_t last_good_stream_id,
uint32_t status_code)
{
memset(frame, 0, sizeof(spdylay_goaway));
frame->hd.version = version;
frame->hd.type = SPDYLAY_GOAWAY;
if(version == SPDYLAY_PROTO_SPDY2) {
frame->hd.length = 4;
} else if(version == SPDYLAY_PROTO_SPDY3) {
frame->hd.length = 8;
frame->status_code = status_code;
} else {
frame->hd.length = 0;
}
frame->last_good_stream_id = last_good_stream_id;
}
void spdylay_frame_goaway_free(spdylay_goaway *frame)
{}
void spdylay_frame_headers_init(spdylay_headers *frame,
uint16_t version, uint8_t flags,
int32_t stream_id, char **nv)
{
memset(frame, 0, sizeof(spdylay_headers));
frame->hd.version = version;
frame->hd.type = SPDYLAY_HEADERS;
frame->hd.flags = flags;
frame->stream_id = stream_id;
frame->nv = nv;
}
void spdylay_frame_headers_free(spdylay_headers *frame)
{
spdylay_frame_nv_del(frame->nv);
}
void spdylay_frame_rst_stream_init(spdylay_rst_stream *frame,
uint16_t version,
int32_t stream_id, uint32_t status_code)
{
memset(frame, 0, sizeof(spdylay_rst_stream));
frame->hd.version = version;
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)
{}
void spdylay_frame_window_update_init(spdylay_window_update *frame,
uint16_t version,
int32_t stream_id,
int32_t delta_window_size)
{
memset(frame, 0, sizeof(spdylay_window_update));
frame->hd.version = version;
frame->hd.type = SPDYLAY_WINDOW_UPDATE;
frame->hd.flags = 0;
frame->hd.length = 8;
frame->stream_id = stream_id;
frame->delta_window_size = delta_window_size;
}
void spdylay_frame_window_update_free(spdylay_window_update *frame)
{}
void spdylay_frame_settings_init(spdylay_settings *frame,
uint16_t version, uint8_t flags,
spdylay_settings_entry *iv, size_t niv)
{
memset(frame, 0, sizeof(spdylay_settings));
frame->hd.version = version;
frame->hd.type = SPDYLAY_SETTINGS;
frame->hd.flags = flags;
frame->hd.length = 4+niv*8;
frame->niv = niv;
frame->iv = iv;
}
void spdylay_frame_settings_free(spdylay_settings *frame)
{
free(frame->iv);
}
void spdylay_frame_credential_init(spdylay_credential *frame,
uint16_t version, uint16_t slot,
spdylay_mem_chunk *proof,
spdylay_mem_chunk *certs,
size_t ncerts)
{
size_t i;
memset(frame, 0, sizeof(spdylay_credential));
frame->hd.version = version;
frame->hd.type = SPDYLAY_CREDENTIAL;
frame->slot = slot;
frame->proof = *proof;
frame->certs = certs;
frame->ncerts = ncerts;
frame->hd.length = 2+4+frame->proof.length;
for(i = 0; i < ncerts; ++i) {
frame->hd.length += 4+frame->certs[i].length;
}
}
void spdylay_frame_credential_free(spdylay_credential *frame)
{
size_t i;
free(frame->proof.data);
for(i = 0; i < frame->ncerts; ++i) {
free(frame->certs[i].data);
}
free(frame->certs);
}
void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id,
uint8_t flags,
const spdylay_data_provider *data_prd)
{
memset(frame, 0, sizeof(spdylay_data));
frame->stream_id = stream_id;
frame->flags = flags;
frame->data_prd = *data_prd;
}
void spdylay_frame_data_free(spdylay_data *frame)
{}
ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr,
size_t *buflen_ptr,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
spdylay_syn_stream *frame,
spdylay_zlib *deflater)
{
ssize_t framelen;
size_t len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) {
return SPDYLAY_ERR_UNSUPPORTED_VERSION;
}
framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr,
nvbuf_ptr, nvbuflen_ptr,
frame->nv,
SPDYLAY_SYN_STREAM_NV_OFFSET,
len_size,
deflater);
if(framelen < 0) {
return framelen;
}
frame->hd.length = framelen-SPDYLAY_FRAME_HEAD_LENGTH;
memset(*buf_ptr, 0, SPDYLAY_SYN_STREAM_NV_OFFSET);
/* pack ctrl header after length is determined */
spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);
spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id);
spdylay_put_uint32be(&(*buf_ptr)[12], frame->assoc_stream_id);
(*buf_ptr)[16] = (frame->pri << 6);
if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {
(*buf_ptr)[17] = frame->slot;
}
return framelen;
}
int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
spdylay_buffer *inflatebuf)
{
int r;
size_t len_size;
r = spdylay_frame_unpack_syn_stream_without_nv(frame, head, headlen,
payload, payloadlen);
len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) {
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->assoc_stream_id =
spdylay_get_uint32(payload+4) & SPDYLAY_STREAM_ID_MASK;
frame->pri = spdylay_unpack_pri(payload+8);
if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {
frame->slot = payload[9];
} else {
frame->slot = 0;
}
frame->nv = NULL;
return 0;
}
ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr,
size_t *buflen_ptr,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
spdylay_syn_reply *frame,
spdylay_zlib *deflater)
{
ssize_t framelen;
size_t len_size;
ssize_t nv_offset;
len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) {
return SPDYLAY_ERR_UNSUPPORTED_VERSION;
}
nv_offset = spdylay_frame_nv_offset(SPDYLAY_SYN_REPLY, frame->hd.version);
assert(nv_offset > 0);
framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr,
nvbuf_ptr, nvbuflen_ptr,
frame->nv, nv_offset,
len_size, deflater);
if(framelen < 0) {
return framelen;
}
frame->hd.length = framelen-SPDYLAY_FRAME_HEAD_LENGTH;
memset(*buf_ptr, 0, nv_offset);
spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);
spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id);
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,
spdylay_buffer *inflatebuf)
{
int r;
r = spdylay_frame_unpack_syn_reply_without_nv(frame, head, headlen,
payload, payloadlen);
if(r == 0) {
size_t len_size;
len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) {
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);
assert(nv_offset > 0);
if((ssize_t)(headlen + payloadlen) != nv_offset) {
return SPDYLAY_ERR_INVALID_FRAME;
}
frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;
frame->nv = NULL;
return 0;
}
ssize_t spdylay_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr,
spdylay_ping *frame)
{
ssize_t framelen = 12;
int r;
r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);
if(r != 0) {
return r;
}
memset(*buf_ptr, 0, framelen);
spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);
spdylay_put_uint32be(&(*buf_ptr)[8], frame->unique_id);
return framelen;
}
int spdylay_frame_unpack_ping(spdylay_ping *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen)
{
if(payloadlen != 4) {
return SPDYLAY_ERR_INVALID_FRAME;
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
frame->unique_id = spdylay_get_uint32(payload);
return 0;
}
ssize_t spdylay_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr,
spdylay_goaway *frame)
{
ssize_t framelen;
int r;
if(frame->hd.version == SPDYLAY_PROTO_SPDY2) {
framelen = 12;
} else if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {
framelen = 16;
} else {
return SPDYLAY_ERR_UNSUPPORTED_VERSION;
}
r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);
if(r != 0) {
return r;
}
memset(*buf_ptr, 0, framelen);
spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);
spdylay_put_uint32be(&(*buf_ptr)[8], frame->last_good_stream_id);
if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {
spdylay_put_uint32be(&(*buf_ptr)[12], frame->status_code);
}
return framelen;
}
int spdylay_frame_unpack_goaway(spdylay_goaway *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen)
{
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
if(frame->hd.version == SPDYLAY_PROTO_SPDY2) {
if(payloadlen != 4) {
return SPDYLAY_ERR_INVALID_FRAME;
}
} else if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {
if(payloadlen != 8) {
return SPDYLAY_ERR_INVALID_FRAME;
}
} else {
return SPDYLAY_ERR_UNSUPPORTED_VERSION;
}
frame->last_good_stream_id = spdylay_get_uint32(payload) &
SPDYLAY_STREAM_ID_MASK;
if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {
frame->status_code = spdylay_get_uint32(payload+4);
} else {
frame->status_code = 0;
}
return 0;
}
ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr,
uint8_t **nvbuf_ptr, size_t *nvbuflen_ptr,
spdylay_headers *frame,
spdylay_zlib *deflater)
{
ssize_t framelen;
size_t len_size;
size_t nv_offset;
len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) {
return SPDYLAY_ERR_UNSUPPORTED_VERSION;
}
nv_offset = spdylay_frame_nv_offset(SPDYLAY_HEADERS, frame->hd.version);
assert(nv_offset > 0);
framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr,
nvbuf_ptr, nvbuflen_ptr,
frame->nv, nv_offset,
len_size, deflater);
if(framelen < 0) {
return framelen;
}
frame->hd.length = framelen-SPDYLAY_FRAME_HEAD_LENGTH;
memset(*buf_ptr, 0, nv_offset);
spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);
spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id);
return framelen;
}
int spdylay_frame_unpack_headers(spdylay_headers *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
spdylay_buffer *inflatebuf)
{
int r;
r = spdylay_frame_unpack_headers_without_nv(frame, head, headlen,
payload, payloadlen);
if(r == 0) {
size_t len_size;
len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) {
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);
assert(nv_offset > 0);
if((ssize_t)(headlen + payloadlen) != nv_offset) {
return SPDYLAY_ERR_INVALID_FRAME;
}
frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;
frame->nv = NULL;
return 0;
}
ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr,
spdylay_rst_stream *frame)
{
ssize_t framelen = 16;
int r;
r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);
if(r != 0) {
return r;
}
memset(*buf_ptr, 0, framelen);
spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);
spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id);
spdylay_put_uint32be(&(*buf_ptr)[12], frame->status_code);
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) & SPDYLAY_STREAM_ID_MASK;
frame->status_code = spdylay_get_uint32(payload+4);
return 0;
}
ssize_t spdylay_frame_pack_window_update(uint8_t **buf_ptr, size_t *buflen_ptr,
spdylay_window_update *frame)
{
ssize_t framelen = 16;
int r;
r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);
if(r != 0) {
return r;
}
memset(*buf_ptr, 0, framelen);
spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);
spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id);
spdylay_put_uint32be(&(*buf_ptr)[12], frame->delta_window_size);
return framelen;
}
int spdylay_frame_unpack_window_update(spdylay_window_update *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) & SPDYLAY_STREAM_ID_MASK;
frame->delta_window_size = spdylay_get_uint32(&payload[4]) &
SPDYLAY_DELTA_WINDOW_SIZE_MASK;
return 0;
}
ssize_t spdylay_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr,
spdylay_settings *frame)
{
ssize_t framelen = SPDYLAY_FRAME_HEAD_LENGTH+frame->hd.length;
size_t i;
int r;
if(frame->hd.version != SPDYLAY_PROTO_SPDY2 &&
frame->hd.version != SPDYLAY_PROTO_SPDY3) {
return SPDYLAY_ERR_UNSUPPORTED_VERSION;
}
r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);
if(r != 0) {
return r;
}
memset(*buf_ptr, 0, framelen);
spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);
spdylay_put_uint32be(&(*buf_ptr)[8], frame->niv);
if(frame->hd.version == SPDYLAY_PROTO_SPDY2) {
for(i = 0; i < frame->niv; ++i) {
int off = i*8;
/* spdy/2 spec says ID is network byte order, but publicly
deployed server sends little endian host byte order. */
(*buf_ptr)[12+off+0] = (frame->iv[i].settings_id) & 0xff;
(*buf_ptr)[12+off+1] = (frame->iv[i].settings_id >> 8) & 0xff;
(*buf_ptr)[12+off+2] = (frame->iv[i].settings_id >>16) & 0xff;
(*buf_ptr)[15+off] = frame->iv[i].flags;
spdylay_put_uint32be(&(*buf_ptr)[16+off], frame->iv[i].value);
}
} else {
for(i = 0; i < frame->niv; ++i) {
int off = i*8;
spdylay_put_uint32be(&(*buf_ptr)[12+off], frame->iv[i].settings_id);
(*buf_ptr)[12+off] = frame->iv[i].flags;
spdylay_put_uint32be(&(*buf_ptr)[16+off], frame->iv[i].value);
}
}
return framelen;
}
int spdylay_frame_unpack_settings(spdylay_settings *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen)
{
size_t i;
if(payloadlen < 4) {
return SPDYLAY_ERR_INVALID_FRAME;
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
if(frame->hd.version != SPDYLAY_PROTO_SPDY2 &&
frame->hd.version != SPDYLAY_PROTO_SPDY3) {
return SPDYLAY_ERR_UNSUPPORTED_VERSION;
}
frame->niv = spdylay_get_uint32(payload);
if(payloadlen != 4+frame->niv*8) {
return SPDYLAY_ERR_INVALID_FRAME;
}
frame->iv = malloc(frame->niv*sizeof(spdylay_settings_entry));
if(frame->iv == NULL) {
return SPDYLAY_ERR_NOMEM;
}
if(frame->hd.version == SPDYLAY_PROTO_SPDY2) {
for(i = 0; i < frame->niv; ++i) {
size_t off = i*8;
/* ID is little endian. See comments in
spdylay_frame_pack_settings(). */
frame->iv[i].settings_id = payload[4+off] + (payload[4+off+1] << 8)
+(payload[4+off+2] << 16);
frame->iv[i].flags = payload[7+off];
frame->iv[i].value = spdylay_get_uint32(&payload[8+off]);
}
} else {
for(i = 0; i < frame->niv; ++i) {
size_t off = i*8;
frame->iv[i].settings_id = spdylay_get_uint32(&payload[4+off]) &
SPDYLAY_SETTINGS_ID_MASK;
frame->iv[i].flags = payload[4+off];
frame->iv[i].value = spdylay_get_uint32(&payload[8+off]);
}
}
return 0;
}
ssize_t spdylay_frame_pack_credential(uint8_t **buf_ptr, size_t *buflen_ptr,
spdylay_credential *frame)
{
ssize_t framelen;
int r;
size_t i, offset;
framelen = SPDYLAY_FRAME_HEAD_LENGTH+2+4+frame->proof.length;
for(i = 0; i < frame->ncerts; ++i) {
framelen += 4+frame->certs[i].length;
}
r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);
if(r != 0) {
return r;
}
memset(*buf_ptr, 0, framelen);
spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);
offset = SPDYLAY_FRAME_HEAD_LENGTH;
spdylay_put_uint16be(&(*buf_ptr)[offset], frame->slot);
offset += 2;
spdylay_put_uint32be(&(*buf_ptr)[offset], frame->proof.length);
offset += 4;
memcpy(&(*buf_ptr)[offset], frame->proof.data, frame->proof.length);
offset += frame->proof.length;
for(i = 0; i < frame->ncerts; ++i) {
spdylay_put_uint32be(&(*buf_ptr)[offset], frame->certs[i].length);
offset += 4;
memcpy(&(*buf_ptr)[offset], frame->certs[i].data, frame->certs[i].length);
offset += frame->certs[i].length;
}
return framelen;
}
/*
* Counts number of client certificate in CREDENTIAL frame payload
* |payload| with length |payloadlen|. The |payload| points to the
* length of the first certificate. This function also checks the
* frame payload is properly composed.
*
* This function returns the number of certificates in |payload| if it
* succeeds, or one of the following negative error codes:
*
* SPDYLAY_ERR_INVALID_FRAME
* The frame payload is invalid.
*/
static int spdylay_frame_count_unpack_cert(const uint8_t *payload,
size_t payloadlen)
{
size_t n, offset = 0;
for(n = 1; 1; ++n) {
size_t len;
if(offset+4 > payloadlen) {
return SPDYLAY_ERR_INVALID_FRAME;
}
len = spdylay_get_uint32(&payload[offset]);
offset += 4;
if(len > payloadlen || offset+len > payloadlen) {
return SPDYLAY_ERR_INVALID_FRAME;
} else {
offset += len;
if(offset == payloadlen) {
break;
}
}
}
return n;
}
/*
* Unpacks client certificates in the |payload| with length
* |payloadlen|. First allocates memory to store the |ncerts|
* certificates. Stores certificates in the allocated space and set
* its pointer to |*certs_ptr|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* SPDYLAY_ERR_NOMEM
* Out of memory
*/
static int spdylay_frame_unpack_cert(spdylay_mem_chunk **certs_ptr,
size_t ncerts,
const uint8_t *payload, size_t payloadlen)
{
size_t offset, i, j;
spdylay_mem_chunk *certs;
certs = malloc(sizeof(spdylay_mem_chunk)*ncerts);
if(certs == NULL) {
return SPDYLAY_ERR_NOMEM;
}
offset = 0;
for(i = 0; i < ncerts; ++i) {
certs[i].length = spdylay_get_uint32(&payload[offset]);
offset += 4;
certs[i].data = malloc(certs[i].length);
if(certs[i].data == NULL) {
goto fail;
}
memcpy(certs[i].data, &payload[offset], certs[i].length);
offset += certs[i].length;
}
*certs_ptr = certs;
return 0;
fail:
for(j = 0; j < i; ++j) {
free(certs[j].data);
}
free(certs);
return SPDYLAY_ERR_NOMEM;
}
int spdylay_frame_unpack_credential(spdylay_credential *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload,
size_t payloadlen)
{
size_t offset;
int rv;
if(payloadlen < 10) {
return SPDYLAY_ERR_INVALID_FRAME;
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
offset = 0;
frame->slot = spdylay_get_uint16(&payload[offset]);
offset += 2;
frame->proof.length = spdylay_get_uint32(&payload[offset]);
offset += 4;
if(frame->proof.length > payloadlen ||
offset+frame->proof.length > payloadlen) {
return SPDYLAY_ERR_INVALID_FRAME;
}
frame->proof.data = malloc(frame->proof.length);
if(frame->proof.data == NULL) {
return SPDYLAY_ERR_NOMEM;
}
memcpy(frame->proof.data, &payload[offset], frame->proof.length);
offset += frame->proof.length;
rv = spdylay_frame_count_unpack_cert(payload+offset, payloadlen-offset);
if(rv < 0) {
goto fail;
}
frame->ncerts = rv;
rv = spdylay_frame_unpack_cert(&frame->certs, frame->ncerts,
payload+offset, payloadlen-offset);
if(rv != 0) {
goto fail;
}
return 0;
fail:
free(frame->proof.data);
return rv;
}
spdylay_settings_entry* spdylay_frame_iv_copy(const spdylay_settings_entry *iv,
size_t niv)
{
spdylay_settings_entry *iv_copy;
size_t len = niv*sizeof(spdylay_settings_entry);
iv_copy = malloc(len);
if(iv_copy == NULL) {
return NULL;
}
memcpy(iv_copy, iv, len);
return iv_copy;
}
static int spdylay_settings_entry_compar(const void *lhs, const void *rhs)
{
return ((spdylay_settings_entry *)lhs)->settings_id
-((spdylay_settings_entry *)rhs)->settings_id;
}
void spdylay_frame_iv_sort(spdylay_settings_entry *iv, size_t niv)
{
qsort(iv, niv, sizeof(spdylay_settings_entry), spdylay_settings_entry_compar);
}
ssize_t spdylay_frame_nv_offset(spdylay_frame_type type, uint16_t version)
{
switch(type) {
case SPDYLAY_SYN_STREAM:
return SPDYLAY_SYN_STREAM_NV_OFFSET;
case SPDYLAY_SYN_REPLY: {
if(version == SPDYLAY_PROTO_SPDY2) {
return SPDYLAY_SPDY2_SYN_REPLY_NV_OFFSET;
} else if(version == SPDYLAY_PROTO_SPDY3) {
return SPDYLAY_SPDY3_SYN_REPLY_NV_OFFSET;
}
break;
}
case SPDYLAY_HEADERS: {
if(version == SPDYLAY_PROTO_SPDY2) {
return SPDYLAY_SPDY2_HEADERS_NV_OFFSET;
} else if(version == SPDYLAY_PROTO_SPDY3) {
return SPDYLAY_SPDY3_HEADERS_NV_OFFSET;
}
break;
}
default:
break;
}
return -1;
}