Path: blob/master/Utilities/cmnghttp2/lib/nghttp2_frame.c
3153 views
/*1* nghttp2 - HTTP/2 C Library2*3* Copyright (c) 2013 Tatsuhiro Tsujikawa4*5* Permission is hereby granted, free of charge, to any person obtaining6* a copy of this software and associated documentation files (the7* "Software"), to deal in the Software without restriction, including8* without limitation the rights to use, copy, modify, merge, publish,9* distribute, sublicense, and/or sell copies of the Software, and to10* permit persons to whom the Software is furnished to do so, subject to11* the following conditions:12*13* The above copyright notice and this permission notice shall be14* included in all copies or substantial portions of the Software.15*16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,17* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF18* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND19* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE20* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION21* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION22* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.23*/24#include "nghttp2_frame.h"2526#include <string.h>27#include <assert.h>28#include <stdio.h>29#include <errno.h>3031#include "nghttp2_helper.h"32#include "nghttp2_net.h"33#include "nghttp2_priority_spec.h"34#include "nghttp2_debug.h"3536void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) {37nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8));38buf[3] = hd->type;39buf[4] = hd->flags;40nghttp2_put_uint32be(&buf[5], (uint32_t)hd->stream_id);41/* ignore hd->reserved for now */42}4344void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) {45hd->length = nghttp2_get_uint32(&buf[0]) >> 8;46hd->type = buf[3];47hd->flags = buf[4];48hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK;49hd->reserved = 0;50}5152void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type,53uint8_t flags, int32_t stream_id) {54hd->length = length;55hd->type = type;56hd->flags = flags;57hd->stream_id = stream_id;58hd->reserved = 0;59}6061void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags,62int32_t stream_id, nghttp2_headers_category cat,63const nghttp2_priority_spec *pri_spec,64nghttp2_nv *nva, size_t nvlen) {65nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id);66frame->padlen = 0;67frame->nva = nva;68frame->nvlen = nvlen;69frame->cat = cat;7071if (pri_spec) {72frame->pri_spec = *pri_spec;73} else {74nghttp2_priority_spec_default_init(&frame->pri_spec);75}76}7778void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) {79nghttp2_nv_array_del(frame->nva, mem);80}8182void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,83const nghttp2_priority_spec *pri_spec) {84nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY,85NGHTTP2_FLAG_NONE, stream_id);86frame->pri_spec = *pri_spec;87}8889void nghttp2_frame_priority_free(nghttp2_priority *frame) { (void)frame; }9091void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id,92uint32_t error_code) {93nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE,94stream_id);95frame->error_code = error_code;96}9798void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame) { (void)frame; }99100void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags,101nghttp2_settings_entry *iv, size_t niv) {102nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH,103NGHTTP2_SETTINGS, flags, 0);104frame->niv = niv;105frame->iv = iv;106}107108void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) {109nghttp2_mem_free(mem, frame->iv);110}111112void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags,113int32_t stream_id,114int32_t promised_stream_id,115nghttp2_nv *nva, size_t nvlen) {116nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id);117frame->padlen = 0;118frame->nva = nva;119frame->nvlen = nvlen;120frame->promised_stream_id = promised_stream_id;121frame->reserved = 0;122}123124void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame,125nghttp2_mem *mem) {126nghttp2_nv_array_del(frame->nva, mem);127}128129void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags,130const uint8_t *opaque_data) {131nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0);132if (opaque_data) {133memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data));134} else {135memset(frame->opaque_data, 0, sizeof(frame->opaque_data));136}137}138139void nghttp2_frame_ping_free(nghttp2_ping *frame) { (void)frame; }140141void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id,142uint32_t error_code, uint8_t *opaque_data,143size_t opaque_data_len) {144nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY,145NGHTTP2_FLAG_NONE, 0);146frame->last_stream_id = last_stream_id;147frame->error_code = error_code;148frame->opaque_data = opaque_data;149frame->opaque_data_len = opaque_data_len;150frame->reserved = 0;151}152153void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) {154nghttp2_mem_free(mem, frame->opaque_data);155}156157void nghttp2_frame_window_update_init(nghttp2_window_update *frame,158uint8_t flags, int32_t stream_id,159int32_t window_size_increment) {160nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id);161frame->window_size_increment = window_size_increment;162frame->reserved = 0;163}164165void nghttp2_frame_window_update_free(nghttp2_window_update *frame) {166(void)frame;167}168169size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) {170/* We have iframe->padlen == 0, but iframe->frame.hd.flags may have171NGHTTP2_FLAG_PADDED set. This happens when receiving172CONTINUATION frame, since we don't reset flags after HEADERS was173received. */174if (padlen == 0) {175return 0;176}177return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0);178}179180void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags,181int32_t stream_id) {182/* At this moment, the length of DATA frame is unknown */183nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id);184frame->padlen = 0;185}186187void nghttp2_frame_data_free(nghttp2_data *frame) { (void)frame; }188189void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,190uint8_t flags, int32_t stream_id,191void *payload) {192nghttp2_frame_hd_init(&frame->hd, 0, type, flags, stream_id);193frame->payload = payload;194}195196void nghttp2_frame_extension_free(nghttp2_extension *frame) { (void)frame; }197198void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,199uint8_t *origin, size_t origin_len,200uint8_t *field_value, size_t field_value_len) {201nghttp2_ext_altsvc *altsvc;202203nghttp2_frame_hd_init(&frame->hd, 2 + origin_len + field_value_len,204NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, stream_id);205206altsvc = frame->payload;207altsvc->origin = origin;208altsvc->origin_len = origin_len;209altsvc->field_value = field_value;210altsvc->field_value_len = field_value_len;211}212213void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) {214nghttp2_ext_altsvc *altsvc;215216altsvc = frame->payload;217if (altsvc == NULL) {218return;219}220/* We use the same buffer for altsvc->origin and221altsvc->field_value. */222nghttp2_mem_free(mem, altsvc->origin);223}224225void nghttp2_frame_origin_init(nghttp2_extension *frame,226nghttp2_origin_entry *ov, size_t nov) {227nghttp2_ext_origin *origin;228size_t payloadlen = 0;229size_t i;230231for (i = 0; i < nov; ++i) {232payloadlen += 2 + ov[i].origin_len;233}234235nghttp2_frame_hd_init(&frame->hd, payloadlen, NGHTTP2_ORIGIN,236NGHTTP2_FLAG_NONE, 0);237238origin = frame->payload;239origin->ov = ov;240origin->nov = nov;241}242243void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) {244nghttp2_ext_origin *origin;245246origin = frame->payload;247if (origin == NULL) {248return;249}250/* We use the same buffer for all resources pointed by the field of251origin directly or indirectly. */252nghttp2_mem_free(mem, origin->ov);253}254255void nghttp2_frame_priority_update_init(nghttp2_extension *frame,256int32_t stream_id, uint8_t *field_value,257size_t field_value_len) {258nghttp2_ext_priority_update *priority_update;259260nghttp2_frame_hd_init(&frame->hd, 4 + field_value_len,261NGHTTP2_PRIORITY_UPDATE, NGHTTP2_FLAG_NONE, 0);262263priority_update = frame->payload;264priority_update->stream_id = stream_id;265priority_update->field_value = field_value;266priority_update->field_value_len = field_value_len;267}268269void nghttp2_frame_priority_update_free(nghttp2_extension *frame,270nghttp2_mem *mem) {271nghttp2_ext_priority_update *priority_update;272273priority_update = frame->payload;274if (priority_update == NULL) {275return;276}277nghttp2_mem_free(mem, priority_update->field_value);278}279280size_t nghttp2_frame_priority_len(uint8_t flags) {281if (flags & NGHTTP2_FLAG_PRIORITY) {282return NGHTTP2_PRIORITY_SPECLEN;283}284285return 0;286}287288size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) {289return nghttp2_frame_priority_len(frame->hd.flags);290}291292/*293* Call this function after payload was serialized, but not before294* changing buf->pos and serializing frame header.295*296* This function assumes bufs->cur points to the last buf chain of the297* frame(s).298*299* This function serializes frame header for HEADERS/PUSH_PROMISE and300* handles their successive CONTINUATION frames.301*302* We don't process any padding here.303*/304static int frame_pack_headers_shared(nghttp2_bufs *bufs,305nghttp2_frame_hd *frame_hd) {306nghttp2_buf *buf;307nghttp2_buf_chain *ci, *ce;308nghttp2_frame_hd hd;309310buf = &bufs->head->buf;311312hd = *frame_hd;313hd.length = nghttp2_buf_len(buf);314315DEBUGF("send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", hd.length);316317/* We have multiple frame buffers, which means one or more318CONTINUATION frame is involved. Remove END_HEADERS flag from the319first frame. */320if (bufs->head != bufs->cur) {321hd.flags = (uint8_t)(hd.flags & ~NGHTTP2_FLAG_END_HEADERS);322}323324buf->pos -= NGHTTP2_FRAME_HDLEN;325nghttp2_frame_pack_frame_hd(buf->pos, &hd);326327if (bufs->head != bufs->cur) {328/* 2nd and later frames are CONTINUATION frames. */329hd.type = NGHTTP2_CONTINUATION;330/* We don't have no flags except for last CONTINUATION */331hd.flags = NGHTTP2_FLAG_NONE;332333ce = bufs->cur;334335for (ci = bufs->head->next; ci != ce; ci = ci->next) {336buf = &ci->buf;337338hd.length = nghttp2_buf_len(buf);339340DEBUGF("send: int CONTINUATION, payloadlen=%zu\n", hd.length);341342buf->pos -= NGHTTP2_FRAME_HDLEN;343nghttp2_frame_pack_frame_hd(buf->pos, &hd);344}345346buf = &ci->buf;347hd.length = nghttp2_buf_len(buf);348/* Set END_HEADERS flag for last CONTINUATION */349hd.flags = NGHTTP2_FLAG_END_HEADERS;350351DEBUGF("send: last CONTINUATION, payloadlen=%zu\n", hd.length);352353buf->pos -= NGHTTP2_FRAME_HDLEN;354nghttp2_frame_pack_frame_hd(buf->pos, &hd);355}356357return 0;358}359360int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame,361nghttp2_hd_deflater *deflater) {362size_t nv_offset;363int rv;364nghttp2_buf *buf;365366assert(bufs->head == bufs->cur);367368nv_offset = nghttp2_frame_headers_payload_nv_offset(frame);369370buf = &bufs->cur->buf;371372buf->pos += nv_offset;373buf->last = buf->pos;374375/* This call will adjust buf->last to the correct position */376rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen);377378if (rv == NGHTTP2_ERR_BUFFER_ERROR) {379rv = NGHTTP2_ERR_HEADER_COMP;380}381382buf->pos -= nv_offset;383384if (rv != 0) {385return rv;386}387388if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {389nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec);390}391392frame->padlen = 0;393frame->hd.length = nghttp2_bufs_len(bufs);394395return frame_pack_headers_shared(bufs, &frame->hd);396}397398void nghttp2_frame_pack_priority_spec(uint8_t *buf,399const nghttp2_priority_spec *pri_spec) {400nghttp2_put_uint32be(buf, (uint32_t)pri_spec->stream_id);401if (pri_spec->exclusive) {402buf[0] |= 0x80;403}404buf[4] = (uint8_t)(pri_spec->weight - 1);405}406407void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,408const uint8_t *payload) {409int32_t dep_stream_id;410uint8_t exclusive;411int32_t weight;412413dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;414exclusive = (payload[0] & 0x80) > 0;415weight = payload[4] + 1;416417nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive);418}419420int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,421const uint8_t *payload) {422if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {423nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload);424} else {425nghttp2_priority_spec_default_init(&frame->pri_spec);426}427428frame->nva = NULL;429frame->nvlen = 0;430431return 0;432}433434int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) {435nghttp2_buf *buf;436437assert(bufs->head == bufs->cur);438439buf = &bufs->head->buf;440441assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN);442443buf->pos -= NGHTTP2_FRAME_HDLEN;444445nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);446447nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec);448449buf->last += NGHTTP2_PRIORITY_SPECLEN;450451return 0;452}453454void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame,455const uint8_t *payload) {456nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload);457}458459int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,460nghttp2_rst_stream *frame) {461nghttp2_buf *buf;462463assert(bufs->head == bufs->cur);464465buf = &bufs->head->buf;466467assert(nghttp2_buf_avail(buf) >= 4);468469buf->pos -= NGHTTP2_FRAME_HDLEN;470471nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);472473nghttp2_put_uint32be(buf->last, frame->error_code);474buf->last += 4;475476return 0;477}478479void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame,480const uint8_t *payload) {481frame->error_code = nghttp2_get_uint32(payload);482}483484int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) {485nghttp2_buf *buf;486487assert(bufs->head == bufs->cur);488489buf = &bufs->head->buf;490491if (nghttp2_buf_avail(buf) < frame->hd.length) {492return NGHTTP2_ERR_FRAME_SIZE_ERROR;493}494495buf->pos -= NGHTTP2_FRAME_HDLEN;496497nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);498499buf->last +=500nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv);501502return 0;503}504505size_t nghttp2_frame_pack_settings_payload(uint8_t *buf,506const nghttp2_settings_entry *iv,507size_t niv) {508size_t i;509for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {510nghttp2_put_uint16be(buf, (uint16_t)iv[i].settings_id);511nghttp2_put_uint32be(buf + 2, iv[i].value);512}513return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv;514}515516void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,517nghttp2_settings_entry *iv,518size_t niv) {519frame->iv = iv;520frame->niv = niv;521}522523void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,524const uint8_t *payload) {525iv->settings_id = nghttp2_get_uint16(&payload[0]);526iv->value = nghttp2_get_uint32(&payload[2]);527}528529int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,530size_t *niv_ptr,531const uint8_t *payload,532size_t payloadlen,533nghttp2_mem *mem) {534size_t i;535536*niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH;537538if (*niv_ptr == 0) {539*iv_ptr = NULL;540541return 0;542}543544*iv_ptr =545nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry));546547if (*iv_ptr == NULL) {548return NGHTTP2_ERR_NOMEM;549}550551for (i = 0; i < *niv_ptr; ++i) {552size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH;553nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]);554}555556return 0;557}558559int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,560nghttp2_push_promise *frame,561nghttp2_hd_deflater *deflater) {562size_t nv_offset = 4;563int rv;564nghttp2_buf *buf;565566assert(bufs->head == bufs->cur);567568buf = &bufs->cur->buf;569570buf->pos += nv_offset;571buf->last = buf->pos;572573/* This call will adjust buf->last to the correct position */574rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen);575576if (rv == NGHTTP2_ERR_BUFFER_ERROR) {577rv = NGHTTP2_ERR_HEADER_COMP;578}579580buf->pos -= nv_offset;581582if (rv != 0) {583return rv;584}585586nghttp2_put_uint32be(buf->pos, (uint32_t)frame->promised_stream_id);587588frame->padlen = 0;589frame->hd.length = nghttp2_bufs_len(bufs);590591return frame_pack_headers_shared(bufs, &frame->hd);592}593594int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,595const uint8_t *payload) {596frame->promised_stream_id =597nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;598frame->nva = NULL;599frame->nvlen = 0;600return 0;601}602603int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) {604nghttp2_buf *buf;605606assert(bufs->head == bufs->cur);607608buf = &bufs->head->buf;609610assert(nghttp2_buf_avail(buf) >= 8);611612buf->pos -= NGHTTP2_FRAME_HDLEN;613614nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);615616buf->last =617nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data));618619return 0;620}621622void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame,623const uint8_t *payload) {624memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data));625}626627int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) {628int rv;629nghttp2_buf *buf;630631assert(bufs->head == bufs->cur);632633buf = &bufs->head->buf;634635buf->pos -= NGHTTP2_FRAME_HDLEN;636637nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);638639nghttp2_put_uint32be(buf->last, (uint32_t)frame->last_stream_id);640buf->last += 4;641642nghttp2_put_uint32be(buf->last, frame->error_code);643buf->last += 4;644645rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len);646647if (rv == NGHTTP2_ERR_BUFFER_ERROR) {648return NGHTTP2_ERR_FRAME_SIZE_ERROR;649}650651if (rv != 0) {652return rv;653}654655return 0;656}657658void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame,659const uint8_t *payload,660uint8_t *var_gift_payload,661size_t var_gift_payloadlen) {662frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;663frame->error_code = nghttp2_get_uint32(payload + 4);664665frame->opaque_data = var_gift_payload;666frame->opaque_data_len = var_gift_payloadlen;667}668669int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,670const uint8_t *payload,671size_t payloadlen, nghttp2_mem *mem) {672uint8_t *var_gift_payload;673size_t var_gift_payloadlen;674675if (payloadlen > 8) {676var_gift_payloadlen = payloadlen - 8;677} else {678var_gift_payloadlen = 0;679}680681if (!var_gift_payloadlen) {682var_gift_payload = NULL;683} else {684var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen);685686if (var_gift_payload == NULL) {687return NGHTTP2_ERR_NOMEM;688}689690memcpy(var_gift_payload, payload + 8, var_gift_payloadlen);691}692693nghttp2_frame_unpack_goaway_payload(frame, payload, var_gift_payload,694var_gift_payloadlen);695696return 0;697}698699int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,700nghttp2_window_update *frame) {701nghttp2_buf *buf;702703assert(bufs->head == bufs->cur);704705buf = &bufs->head->buf;706707assert(nghttp2_buf_avail(buf) >= 4);708709buf->pos -= NGHTTP2_FRAME_HDLEN;710711nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);712713nghttp2_put_uint32be(buf->last, (uint32_t)frame->window_size_increment);714buf->last += 4;715716return 0;717}718719void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,720const uint8_t *payload) {721frame->window_size_increment =722nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK;723}724725int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) {726int rv;727nghttp2_buf *buf;728nghttp2_ext_altsvc *altsvc;729730/* This is required with --disable-assert. */731(void)rv;732733altsvc = frame->payload;734735buf = &bufs->head->buf;736737assert(nghttp2_buf_avail(buf) >=7382 + altsvc->origin_len + altsvc->field_value_len);739740buf->pos -= NGHTTP2_FRAME_HDLEN;741742nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);743744nghttp2_put_uint16be(buf->last, (uint16_t)altsvc->origin_len);745buf->last += 2;746747rv = nghttp2_bufs_add(bufs, altsvc->origin, altsvc->origin_len);748749assert(rv == 0);750751rv = nghttp2_bufs_add(bufs, altsvc->field_value, altsvc->field_value_len);752753assert(rv == 0);754755return 0;756}757758void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,759size_t origin_len, uint8_t *payload,760size_t payloadlen) {761nghttp2_ext_altsvc *altsvc;762uint8_t *p;763764altsvc = frame->payload;765p = payload;766767altsvc->origin = p;768769p += origin_len;770771altsvc->origin_len = origin_len;772773altsvc->field_value = p;774altsvc->field_value_len = (size_t)(payload + payloadlen - p);775}776777int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame,778const uint8_t *payload,779size_t payloadlen, nghttp2_mem *mem) {780uint8_t *buf;781size_t origin_len;782783if (payloadlen < 2) {784return NGHTTP2_FRAME_SIZE_ERROR;785}786787origin_len = nghttp2_get_uint16(payload);788789buf = nghttp2_mem_malloc(mem, payloadlen - 2);790if (!buf) {791return NGHTTP2_ERR_NOMEM;792}793794nghttp2_cpymem(buf, payload + 2, payloadlen - 2);795796nghttp2_frame_unpack_altsvc_payload(frame, origin_len, buf, payloadlen - 2);797798return 0;799}800801int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *frame) {802nghttp2_buf *buf;803nghttp2_ext_origin *origin;804nghttp2_origin_entry *orig;805size_t i;806807origin = frame->payload;808809buf = &bufs->head->buf;810811if (nghttp2_buf_avail(buf) < frame->hd.length) {812return NGHTTP2_ERR_FRAME_SIZE_ERROR;813}814815buf->pos -= NGHTTP2_FRAME_HDLEN;816817nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);818819for (i = 0; i < origin->nov; ++i) {820orig = &origin->ov[i];821nghttp2_put_uint16be(buf->last, (uint16_t)orig->origin_len);822buf->last += 2;823buf->last = nghttp2_cpymem(buf->last, orig->origin, orig->origin_len);824}825826assert(nghttp2_buf_len(buf) == NGHTTP2_FRAME_HDLEN + frame->hd.length);827828return 0;829}830831int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,832const uint8_t *payload,833size_t payloadlen, nghttp2_mem *mem) {834nghttp2_ext_origin *origin;835const uint8_t *p, *end;836uint8_t *dst;837size_t originlen;838nghttp2_origin_entry *ov;839size_t nov = 0;840size_t len = 0;841842origin = frame->payload;843p = end = payload;844if (payloadlen) {845end += payloadlen;846}847848for (; p != end;) {849if (end - p < 2) {850return NGHTTP2_ERR_FRAME_SIZE_ERROR;851}852originlen = nghttp2_get_uint16(p);853p += 2;854if (originlen == 0) {855continue;856}857if (originlen > (size_t)(end - p)) {858return NGHTTP2_ERR_FRAME_SIZE_ERROR;859}860p += originlen;861/* 1 for terminal NULL */862len += originlen + 1;863++nov;864}865866if (nov == 0) {867origin->ov = NULL;868origin->nov = 0;869870return 0;871}872873len += nov * sizeof(nghttp2_origin_entry);874875ov = nghttp2_mem_malloc(mem, len);876if (ov == NULL) {877return NGHTTP2_ERR_NOMEM;878}879880origin->ov = ov;881origin->nov = nov;882883dst = (uint8_t *)ov + nov * sizeof(nghttp2_origin_entry);884p = payload;885886for (; p != end;) {887originlen = nghttp2_get_uint16(p);888p += 2;889if (originlen == 0) {890continue;891}892ov->origin = dst;893ov->origin_len = originlen;894dst = nghttp2_cpymem(dst, p, originlen);895*dst++ = '\0';896p += originlen;897++ov;898}899900return 0;901}902903int nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs,904nghttp2_extension *frame) {905int rv;906nghttp2_buf *buf;907nghttp2_ext_priority_update *priority_update;908909/* This is required with --disable-assert. */910(void)rv;911912priority_update = frame->payload;913914buf = &bufs->head->buf;915916assert(nghttp2_buf_avail(buf) >= 4 + priority_update->field_value_len);917918buf->pos -= NGHTTP2_FRAME_HDLEN;919920nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);921922nghttp2_put_uint32be(buf->last, (uint32_t)priority_update->stream_id);923buf->last += 4;924925rv = nghttp2_bufs_add(bufs, priority_update->field_value,926priority_update->field_value_len);927928assert(rv == 0);929930return 0;931}932933void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame,934uint8_t *payload,935size_t payloadlen) {936nghttp2_ext_priority_update *priority_update;937938assert(payloadlen >= 4);939940priority_update = frame->payload;941942priority_update->stream_id =943nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;944945if (payloadlen > 4) {946priority_update->field_value = payload + 4;947priority_update->field_value_len = payloadlen - 4;948} else {949priority_update->field_value = NULL;950priority_update->field_value_len = 0;951}952}953954nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,955size_t niv, nghttp2_mem *mem) {956nghttp2_settings_entry *iv_copy;957size_t len = niv * sizeof(nghttp2_settings_entry);958959if (len == 0) {960return NULL;961}962963iv_copy = nghttp2_mem_malloc(mem, len);964965if (iv_copy == NULL) {966return NULL;967}968969memcpy(iv_copy, iv, len);970971return iv_copy;972}973974int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) {975if (a->namelen != b->namelen || a->valuelen != b->valuelen) {976return 0;977}978979if (a->name == NULL || b->name == NULL) {980assert(a->namelen == 0);981assert(b->namelen == 0);982} else if (memcmp(a->name, b->name, a->namelen) != 0) {983return 0;984}985986if (a->value == NULL || b->value == NULL) {987assert(a->valuelen == 0);988assert(b->valuelen == 0);989} else if (memcmp(a->value, b->value, a->valuelen) != 0) {990return 0;991}992993return 1;994}995996void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) {997nghttp2_mem_free(mem, nva);998}9991000static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b,1001size_t blen) {1002int rv;10031004if (alen == blen) {1005return memcmp(a, b, alen);1006}10071008if (alen < blen) {1009rv = memcmp(a, b, alen);10101011if (rv == 0) {1012return -1;1013}10141015return rv;1016}10171018rv = memcmp(a, b, blen);10191020if (rv == 0) {1021return 1;1022}10231024return rv;1025}10261027int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) {1028return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen);1029}10301031static int nv_compar(const void *lhs, const void *rhs) {1032const nghttp2_nv *a = (const nghttp2_nv *)lhs;1033const nghttp2_nv *b = (const nghttp2_nv *)rhs;1034int rv;10351036rv = bytes_compar(a->name, a->namelen, b->name, b->namelen);10371038if (rv == 0) {1039return bytes_compar(a->value, a->valuelen, b->value, b->valuelen);1040}10411042return rv;1043}10441045void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) {1046qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar);1047}10481049int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,1050size_t nvlen, nghttp2_mem *mem) {1051size_t i;1052uint8_t *data = NULL;1053size_t buflen = 0;1054nghttp2_nv *p;10551056if (nvlen == 0) {1057*nva_ptr = NULL;10581059return 0;1060}10611062for (i = 0; i < nvlen; ++i) {1063/* + 1 for null-termination */1064if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) == 0) {1065buflen += nva[i].namelen + 1;1066}1067if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) == 0) {1068buflen += nva[i].valuelen + 1;1069}1070}10711072buflen += sizeof(nghttp2_nv) * nvlen;10731074*nva_ptr = nghttp2_mem_malloc(mem, buflen);10751076if (*nva_ptr == NULL) {1077return NGHTTP2_ERR_NOMEM;1078}10791080p = *nva_ptr;1081data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen;10821083for (i = 0; i < nvlen; ++i) {1084p->flags = nva[i].flags;10851086if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) {1087p->name = nva[i].name;1088p->namelen = nva[i].namelen;1089} else {1090if (nva[i].namelen) {1091memcpy(data, nva[i].name, nva[i].namelen);1092}1093p->name = data;1094p->namelen = nva[i].namelen;1095data[p->namelen] = '\0';1096nghttp2_downcase(p->name, p->namelen);1097data += nva[i].namelen + 1;1098}10991100if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) {1101p->value = nva[i].value;1102p->valuelen = nva[i].valuelen;1103} else {1104if (nva[i].valuelen) {1105memcpy(data, nva[i].value, nva[i].valuelen);1106}1107p->value = data;1108p->valuelen = nva[i].valuelen;1109data[p->valuelen] = '\0';1110data += nva[i].valuelen + 1;1111}11121113++p;1114}1115return 0;1116}11171118int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {1119size_t i;1120for (i = 0; i < niv; ++i) {1121switch (iv[i].settings_id) {1122case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:1123break;1124case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:1125break;1126case NGHTTP2_SETTINGS_ENABLE_PUSH:1127if (iv[i].value != 0 && iv[i].value != 1) {1128return 0;1129}1130break;1131case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:1132if (iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) {1133return 0;1134}1135break;1136case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:1137if (iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN ||1138iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) {1139return 0;1140}1141break;1142case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:1143break;1144case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:1145if (iv[i].value != 0 && iv[i].value != 1) {1146return 0;1147}1148break;1149case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:1150if (iv[i].value != 0 && iv[i].value != 1) {1151return 0;1152}1153break;1154}1155}1156return 1;1157}11581159static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) {1160size_t trail_padlen;1161size_t newlen;11621163DEBUGF("send: padlen=%zu, shift left 1 bytes\n", padlen);11641165memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN);11661167--buf->pos;11681169buf->pos[4] |= NGHTTP2_FLAG_PADDED;11701171newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen;1172nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3]));11731174if (framehd_only) {1175return;1176}11771178trail_padlen = padlen - 1;1179buf->pos[NGHTTP2_FRAME_HDLEN] = (uint8_t)trail_padlen;11801181/* zero out padding */1182memset(buf->last, 0, trail_padlen);1183/* extend buffers trail_padlen bytes, since we ate previous padlen -1184trail_padlen byte(s) */1185buf->last += trail_padlen;1186}11871188int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,1189size_t padlen, int framehd_only) {1190nghttp2_buf *buf;11911192if (padlen == 0) {1193DEBUGF("send: padlen = 0, nothing to do\n");11941195return 0;1196}11971198/*1199* We have arranged bufs like this:1200*1201* 0 1 2 31202* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 11203* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+1204* | |Frame header | Frame payload... :1205* +-+-----------------+-------------------------------------------+1206* | |Frame header | Frame payload... :1207* +-+-----------------+-------------------------------------------+1208* | |Frame header | Frame payload... :1209* +-+-----------------+-------------------------------------------+1210*1211* We arranged padding so that it is included in the first frame1212* completely. For padded frame, we are going to adjust buf->pos of1213* frame which includes padding and serialize (memmove) frame header1214* in the correct position. Also extends buf->last to include1215* padding.1216*/12171218buf = &bufs->head->buf;12191220assert(nghttp2_buf_avail(buf) >= padlen - 1);12211222frame_set_pad(buf, padlen, framehd_only);12231224hd->length += padlen;1225hd->flags |= NGHTTP2_FLAG_PADDED;12261227DEBUGF("send: final payloadlen=%zu, padlen=%zu\n", hd->length, padlen);12281229return 0;1230}123112321233