1
0
mirror of https://github.com/moparisthebest/spdylay synced 2024-08-13 17:03:54 -04:00
spdylay/lib/spdylay_frame.c
Tatsuhiro Tsujikawa 5deef03687 Moved include of arpa/inet.h to spdylay_net.h
spdylay_net.h is compatibility layer for network related header files.
In the nature of spdylay library it should not depend on the actual
networking implementations, but we need some system headers for
optimization. Currently, arpha/inet.h and netinet/in.h are needed for
ntoh*/hton* functions.
2012-03-24 00:14:04 +09:00

1016 lines
31 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 "spdylay_helper.h"
#include "spdylay_net.h"
#define spdylay_frame_get_nv_len(IN, LEN_SIZE) \
(LEN_SIZE == 2 ? spdylay_get_uint16(IN) : spdylay_get_uint32(IN))
#define spdylay_frame_put_nv_len(OUT, VAL, LEN_SIZE) \
(LEN_SIZE == 2 ? \
spdylay_put_uint16be(OUT, VAL) : spdylay_put_uint32be(OUT, VAL))
/* Returns the number of bytes in length of name/value pair for the
given protocol version |version|. If |version| is not supported,
returns 0. */
static 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, const uint8_t *in, size_t inlen,
size_t len_size)
{
uint32_t n;
size_t buflen = 0;
size_t nvlen = 0;
size_t off = 0;
size_t i;
if(inlen < len_size) {
return SPDYLAY_ERR_INVALID_FRAME;
}
/* TODO limit n in a reasonable number */
n = spdylay_frame_get_nv_len(in, 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(in+off, len_size);
off += len_size;
if(inlen-off < len) {
return SPDYLAY_ERR_INVALID_FRAME;
}
buflen += len+1;
off += len;
}
for(j = off, off -= len; off != j; ++off) {
if(in[off] == '\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, const uint8_t *in, size_t inlen,
size_t len_size)
{
size_t nvlen, buflen;
int r;
size_t i;
char *buf, **index, *data;
uint32_t n;
r = spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, in, inlen, len_size);
if(r != 0) {
return r;
}
buf = malloc(buflen);
if(buf == NULL) {
return SPDYLAY_ERR_NOMEM;
}
index = (char**)buf;
data = buf+(nvlen*2+1)*sizeof(char*);
n = spdylay_frame_get_nv_len(in, len_size);
in += len_size;
for(i = 0; i < n; ++i) {
uint32_t len;
char *name, *val;
char *stop;
len = spdylay_frame_get_nv_len(in, len_size);
in += len_size;
name = data;
memcpy(data, in, len);
data += len;
*data = '\0';
++data;
in += len;
len = spdylay_frame_get_nv_len(in, len_size);
in += len_size;
val = data;
memcpy(data, in, len);
for(stop = data+len; data != stop; ++data) {
if(*data == '\0') {
*index++ = name;
*index++ = val;
val = data+1;
}
}
*data = '\0';
++data;
in += len;
*index++ = name;
*index++ = val;
}
*index = NULL;
assert((size_t)((char*)index - buf) == (nvlen*2)*sizeof(char*));
*nv_ptr = (char**)buf;
return 0;
}
int spdylay_frame_alloc_unpack_nv(char ***nv_ptr,
spdylay_buffer *inflatebuf,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
const uint8_t *in, size_t inlen,
size_t len_size,
spdylay_zlib *inflater)
{
ssize_t nvspace;
int r;
nvspace = spdylay_zlib_inflate_hd(inflater, inflatebuf, in, inlen);
if(nvspace < 0) {
return nvspace;
} else {
r = spdylay_reserve_buffer(nvbuf_ptr, nvbuflen_ptr, nvspace);
if(r != 0) {
return SPDYLAY_ERR_NOMEM;
}
spdylay_buffer_serialize(inflatebuf, *nvbuf_ptr);
r = spdylay_frame_unpack_nv(nv_ptr, *nvbuf_ptr, nvspace, len_size);
return r;
}
}
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 **index, *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;
}
index = (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);
*index++ = data;
data += len;
}
*index = 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 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] = 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] = spdylay_nv_3to2[j];
break;
}
}
}
}
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_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)
{}
#define SPDYLAY_SYN_STREAM_NV_OFFSET 18
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,
spdylay_buffer *inflatebuf,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
spdylay_zlib *inflater)
{
int r;
size_t len_size;
if(payloadlen < 12) {
return SPDYLAY_ERR_INVALID_FRAME;
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) {
return SPDYLAY_ERR_UNSUPPORTED_VERSION;
}
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;
}
r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf,
nvbuf_ptr, nvbuflen_ptr,
payload+10, payloadlen-10,
len_size,
inflater);
return r;
}
#define SPDYLAY_SPDY2_SYN_REPLY_NV_OFFSET 14
#define SPDYLAY_SPDY3_SYN_REPLY_NV_OFFSET 12
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;
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 = frame->hd.version == SPDYLAY_PROTO_SPDY2 ?
SPDYLAY_SPDY2_SYN_REPLY_NV_OFFSET : SPDYLAY_SPDY3_SYN_REPLY_NV_OFFSET;
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,
spdylay_buffer *inflatebuf,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
spdylay_zlib *inflater)
{
int r;
size_t len_size;
size_t nv_offset;
if(payloadlen < 8) {
return SPDYLAY_ERR_INVALID_FRAME;
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) {
return SPDYLAY_ERR_UNSUPPORTED_VERSION;
}
nv_offset = frame->hd.version == SPDYLAY_PROTO_SPDY2 ? 6 : 4;
frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;
r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf,
nvbuf_ptr, nvbuflen_ptr,
payload+nv_offset, payloadlen-nv_offset,
len_size, inflater);
return r;
}
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;
}
#define SPDYLAY_SPDY2_HEADERS_NV_OFFSET 14
#define SPDYLAY_SPDY3_HEADERS_NV_OFFSET 12
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 = frame->hd.version == SPDYLAY_PROTO_SPDY2 ?
SPDYLAY_SPDY2_HEADERS_NV_OFFSET : SPDYLAY_SPDY3_HEADERS_NV_OFFSET;
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,
spdylay_buffer *inflatebuf,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
spdylay_zlib *inflater)
{
int r;
size_t len_size;
size_t nv_offset;
if(payloadlen < 8) {
return SPDYLAY_ERR_INVALID_FRAME;
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
len_size = spdylay_frame_get_len_size(frame->hd.version);
if(len_size == 0) {
return SPDYLAY_ERR_UNSUPPORTED_VERSION;
}
nv_offset = frame->hd.version == SPDYLAY_PROTO_SPDY2 ? 6 : 4;
frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;
r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf,
nvbuf_ptr, nvbuflen_ptr,
payload+nv_offset, payloadlen-nv_offset,
len_size, inflater);
return r;
}
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. */
char *id_ptr = (char*)(&frame->iv[i].settings_id);
#ifdef WORDS_BIGENDIAN
(*buf_ptr)[12+off] = id_ptr[3];
(*buf_ptr)[12+off+1] = id_ptr[2];
(*buf_ptr)[12+off+2] = id_ptr[1];
#else /* !WORDS_BIGENDIAN */
memcpy(&(*buf_ptr)[12+off], id_ptr, 3);
#endif /* !WORDS_BIGENDIAN */
(*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 = 0;
#ifdef WORDS_BIGENDIAN
*(char*)(&frame->iv[i].settings_id[1]) = &payload[4+off+2];
*(char*)(&frame->iv[i].settings_id[2]) = &payload[4+off+1];
*(char*)(&frame->iv[i].settings_id[3]) = &payload[4+off+0];
#else /* !WORDS_BIGENDIAN */
memcpy(&frame->iv[i].settings_id, &payload[4+off], 3);
#endif /* !WORDS_BIGENDIAN */
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;
}
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);
}