1/* 2 * nghttp2 - HTTP/2 C Library 3 * 4 * Copyright (c) 2013 Tatsuhiro Tsujikawa 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining 7 * a copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sublicense, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be 15 * included in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 */ 25#include "nghttp2_frame.h" 26 27#include <string.h> 28#include <assert.h> 29#include <stdio.h> 30#include <errno.h> 31 32#include "nghttp2_helper.h" 33#include "nghttp2_net.h" 34#include "nghttp2_priority_spec.h" 35#include "nghttp2_debug.h" 36 37void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) { 38 nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8)); 39 buf[3] = hd->type; 40 buf[4] = hd->flags; 41 nghttp2_put_uint32be(&buf[5], (uint32_t)hd->stream_id); 42 /* ignore hd->reserved for now */ 43} 44 45void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) { 46 hd->length = nghttp2_get_uint32(&buf[0]) >> 8; 47 hd->type = buf[3]; 48 hd->flags = buf[4]; 49 hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK; 50 hd->reserved = 0; 51} 52 53void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, 54 uint8_t flags, int32_t stream_id) { 55 hd->length = length; 56 hd->type = type; 57 hd->flags = flags; 58 hd->stream_id = stream_id; 59 hd->reserved = 0; 60} 61 62void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, 63 int32_t stream_id, nghttp2_headers_category cat, 64 const nghttp2_priority_spec *pri_spec, 65 nghttp2_nv *nva, size_t nvlen) { 66 nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id); 67 frame->padlen = 0; 68 frame->nva = nva; 69 frame->nvlen = nvlen; 70 frame->cat = cat; 71 72 if (pri_spec) { 73 frame->pri_spec = *pri_spec; 74 } else { 75 nghttp2_priority_spec_default_init(&frame->pri_spec); 76 } 77} 78 79void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) { 80 nghttp2_nv_array_del(frame->nva, mem); 81} 82 83void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, 84 const nghttp2_priority_spec *pri_spec) { 85 nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY, 86 NGHTTP2_FLAG_NONE, stream_id); 87 frame->pri_spec = *pri_spec; 88} 89 90void nghttp2_frame_priority_free(nghttp2_priority *frame) { (void)frame; } 91 92void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, 93 uint32_t error_code) { 94 nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 95 stream_id); 96 frame->error_code = error_code; 97} 98 99void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame) { (void)frame; } 100 101void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, 102 nghttp2_settings_entry *iv, size_t niv) { 103 nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, 104 NGHTTP2_SETTINGS, flags, 0); 105 frame->niv = niv; 106 frame->iv = iv; 107} 108 109void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) { 110 nghttp2_mem_free(mem, frame->iv); 111} 112 113void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, 114 int32_t stream_id, 115 int32_t promised_stream_id, 116 nghttp2_nv *nva, size_t nvlen) { 117 nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id); 118 frame->padlen = 0; 119 frame->nva = nva; 120 frame->nvlen = nvlen; 121 frame->promised_stream_id = promised_stream_id; 122 frame->reserved = 0; 123} 124 125void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, 126 nghttp2_mem *mem) { 127 nghttp2_nv_array_del(frame->nva, mem); 128} 129 130void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, 131 const uint8_t *opaque_data) { 132 nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0); 133 if (opaque_data) { 134 memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data)); 135 } else { 136 memset(frame->opaque_data, 0, sizeof(frame->opaque_data)); 137 } 138} 139 140void nghttp2_frame_ping_free(nghttp2_ping *frame) { (void)frame; } 141 142void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, 143 uint32_t error_code, uint8_t *opaque_data, 144 size_t opaque_data_len) { 145 nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY, 146 NGHTTP2_FLAG_NONE, 0); 147 frame->last_stream_id = last_stream_id; 148 frame->error_code = error_code; 149 frame->opaque_data = opaque_data; 150 frame->opaque_data_len = opaque_data_len; 151 frame->reserved = 0; 152} 153 154void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) { 155 nghttp2_mem_free(mem, frame->opaque_data); 156} 157 158void nghttp2_frame_window_update_init(nghttp2_window_update *frame, 159 uint8_t flags, int32_t stream_id, 160 int32_t window_size_increment) { 161 nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id); 162 frame->window_size_increment = window_size_increment; 163 frame->reserved = 0; 164} 165 166void nghttp2_frame_window_update_free(nghttp2_window_update *frame) { 167 (void)frame; 168} 169 170size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) { 171 /* We have iframe->padlen == 0, but iframe->frame.hd.flags may have 172 NGHTTP2_FLAG_PADDED set. This happens when receiving 173 CONTINUATION frame, since we don't reset flags after HEADERS was 174 received. */ 175 if (padlen == 0) { 176 return 0; 177 } 178 return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); 179} 180 181void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, 182 int32_t stream_id) { 183 /* At this moment, the length of DATA frame is unknown */ 184 nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id); 185 frame->padlen = 0; 186} 187 188void nghttp2_frame_data_free(nghttp2_data *frame) { (void)frame; } 189 190void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type, 191 uint8_t flags, int32_t stream_id, 192 void *payload) { 193 nghttp2_frame_hd_init(&frame->hd, 0, type, flags, stream_id); 194 frame->payload = payload; 195} 196 197void nghttp2_frame_extension_free(nghttp2_extension *frame) { (void)frame; } 198 199void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id, 200 uint8_t *origin, size_t origin_len, 201 uint8_t *field_value, size_t field_value_len) { 202 nghttp2_ext_altsvc *altsvc; 203 204 nghttp2_frame_hd_init(&frame->hd, 2 + origin_len + field_value_len, 205 NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, stream_id); 206 207 altsvc = frame->payload; 208 altsvc->origin = origin; 209 altsvc->origin_len = origin_len; 210 altsvc->field_value = field_value; 211 altsvc->field_value_len = field_value_len; 212} 213 214void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) { 215 nghttp2_ext_altsvc *altsvc; 216 217 altsvc = frame->payload; 218 if (altsvc == NULL) { 219 return; 220 } 221 /* We use the same buffer for altsvc->origin and 222 altsvc->field_value. */ 223 nghttp2_mem_free(mem, altsvc->origin); 224} 225 226void nghttp2_frame_origin_init(nghttp2_extension *frame, 227 nghttp2_origin_entry *ov, size_t nov) { 228 nghttp2_ext_origin *origin; 229 size_t payloadlen = 0; 230 size_t i; 231 232 for (i = 0; i < nov; ++i) { 233 payloadlen += 2 + ov[i].origin_len; 234 } 235 236 nghttp2_frame_hd_init(&frame->hd, payloadlen, NGHTTP2_ORIGIN, 237 NGHTTP2_FLAG_NONE, 0); 238 239 origin = frame->payload; 240 origin->ov = ov; 241 origin->nov = nov; 242} 243 244void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) { 245 nghttp2_ext_origin *origin; 246 247 origin = frame->payload; 248 if (origin == NULL) { 249 return; 250 } 251 /* We use the same buffer for all resources pointed by the field of 252 origin directly or indirectly. */ 253 nghttp2_mem_free(mem, origin->ov); 254} 255 256void nghttp2_frame_priority_update_init(nghttp2_extension *frame, 257 int32_t stream_id, uint8_t *field_value, 258 size_t field_value_len) { 259 nghttp2_ext_priority_update *priority_update; 260 261 nghttp2_frame_hd_init(&frame->hd, 4 + field_value_len, 262 NGHTTP2_PRIORITY_UPDATE, NGHTTP2_FLAG_NONE, 0); 263 264 priority_update = frame->payload; 265 priority_update->stream_id = stream_id; 266 priority_update->field_value = field_value; 267 priority_update->field_value_len = field_value_len; 268} 269 270void nghttp2_frame_priority_update_free(nghttp2_extension *frame, 271 nghttp2_mem *mem) { 272 nghttp2_ext_priority_update *priority_update; 273 274 priority_update = frame->payload; 275 if (priority_update == NULL) { 276 return; 277 } 278 nghttp2_mem_free(mem, priority_update->field_value); 279} 280 281size_t nghttp2_frame_priority_len(uint8_t flags) { 282 if (flags & NGHTTP2_FLAG_PRIORITY) { 283 return NGHTTP2_PRIORITY_SPECLEN; 284 } 285 286 return 0; 287} 288 289size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) { 290 return nghttp2_frame_priority_len(frame->hd.flags); 291} 292 293/* 294 * Call this function after payload was serialized, but not before 295 * changing buf->pos and serializing frame header. 296 * 297 * This function assumes bufs->cur points to the last buf chain of the 298 * frame(s). 299 * 300 * This function serializes frame header for HEADERS/PUSH_PROMISE and 301 * handles their successive CONTINUATION frames. 302 * 303 * We don't process any padding here. 304 */ 305static int frame_pack_headers_shared(nghttp2_bufs *bufs, 306 nghttp2_frame_hd *frame_hd) { 307 nghttp2_buf *buf; 308 nghttp2_buf_chain *ci, *ce; 309 nghttp2_frame_hd hd; 310 311 buf = &bufs->head->buf; 312 313 hd = *frame_hd; 314 hd.length = nghttp2_buf_len(buf); 315 316 DEBUGF("send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", hd.length); 317 318 /* We have multiple frame buffers, which means one or more 319 CONTINUATION frame is involved. Remove END_HEADERS flag from the 320 first frame. */ 321 if (bufs->head != bufs->cur) { 322 hd.flags = (uint8_t)(hd.flags & ~NGHTTP2_FLAG_END_HEADERS); 323 } 324 325 buf->pos -= NGHTTP2_FRAME_HDLEN; 326 nghttp2_frame_pack_frame_hd(buf->pos, &hd); 327 328 if (bufs->head != bufs->cur) { 329 /* 2nd and later frames are CONTINUATION frames. */ 330 hd.type = NGHTTP2_CONTINUATION; 331 /* We don't have no flags except for last CONTINUATION */ 332 hd.flags = NGHTTP2_FLAG_NONE; 333 334 ce = bufs->cur; 335 336 for (ci = bufs->head->next; ci != ce; ci = ci->next) { 337 buf = &ci->buf; 338 339 hd.length = nghttp2_buf_len(buf); 340 341 DEBUGF("send: int CONTINUATION, payloadlen=%zu\n", hd.length); 342 343 buf->pos -= NGHTTP2_FRAME_HDLEN; 344 nghttp2_frame_pack_frame_hd(buf->pos, &hd); 345 } 346 347 buf = &ci->buf; 348 hd.length = nghttp2_buf_len(buf); 349 /* Set END_HEADERS flag for last CONTINUATION */ 350 hd.flags = NGHTTP2_FLAG_END_HEADERS; 351 352 DEBUGF("send: last CONTINUATION, payloadlen=%zu\n", hd.length); 353 354 buf->pos -= NGHTTP2_FRAME_HDLEN; 355 nghttp2_frame_pack_frame_hd(buf->pos, &hd); 356 } 357 358 return 0; 359} 360 361int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, 362 nghttp2_hd_deflater *deflater) { 363 size_t nv_offset; 364 int rv; 365 nghttp2_buf *buf; 366 367 assert(bufs->head == bufs->cur); 368 369 nv_offset = nghttp2_frame_headers_payload_nv_offset(frame); 370 371 buf = &bufs->cur->buf; 372 373 buf->pos += nv_offset; 374 buf->last = buf->pos; 375 376 /* This call will adjust buf->last to the correct position */ 377 rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); 378 379 if (rv == NGHTTP2_ERR_BUFFER_ERROR) { 380 rv = NGHTTP2_ERR_HEADER_COMP; 381 } 382 383 buf->pos -= nv_offset; 384 385 if (rv != 0) { 386 return rv; 387 } 388 389 if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { 390 nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec); 391 } 392 393 frame->padlen = 0; 394 frame->hd.length = nghttp2_bufs_len(bufs); 395 396 return frame_pack_headers_shared(bufs, &frame->hd); 397} 398 399void nghttp2_frame_pack_priority_spec(uint8_t *buf, 400 const nghttp2_priority_spec *pri_spec) { 401 nghttp2_put_uint32be(buf, (uint32_t)pri_spec->stream_id); 402 if (pri_spec->exclusive) { 403 buf[0] |= 0x80; 404 } 405 buf[4] = (uint8_t)(pri_spec->weight - 1); 406} 407 408void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, 409 const uint8_t *payload) { 410 int32_t dep_stream_id; 411 uint8_t exclusive; 412 int32_t weight; 413 414 dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; 415 exclusive = (payload[0] & 0x80) > 0; 416 weight = payload[4] + 1; 417 418 nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive); 419} 420 421void nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, 422 const uint8_t *payload) { 423 if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { 424 nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); 425 } else { 426 nghttp2_priority_spec_default_init(&frame->pri_spec); 427 } 428 429 frame->nva = NULL; 430 frame->nvlen = 0; 431} 432 433void nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { 434 nghttp2_buf *buf; 435 436 assert(bufs->head == bufs->cur); 437 438 buf = &bufs->head->buf; 439 440 assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN); 441 442 buf->pos -= NGHTTP2_FRAME_HDLEN; 443 444 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); 445 446 nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec); 447 448 buf->last += NGHTTP2_PRIORITY_SPECLEN; 449} 450 451void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, 452 const uint8_t *payload) { 453 nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); 454} 455 456void nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, 457 nghttp2_rst_stream *frame) { 458 nghttp2_buf *buf; 459 460 assert(bufs->head == bufs->cur); 461 462 buf = &bufs->head->buf; 463 464 assert(nghttp2_buf_avail(buf) >= 4); 465 466 buf->pos -= NGHTTP2_FRAME_HDLEN; 467 468 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); 469 470 nghttp2_put_uint32be(buf->last, frame->error_code); 471 buf->last += 4; 472} 473 474void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, 475 const uint8_t *payload) { 476 frame->error_code = nghttp2_get_uint32(payload); 477} 478 479int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) { 480 nghttp2_buf *buf; 481 482 assert(bufs->head == bufs->cur); 483 484 buf = &bufs->head->buf; 485 486 if (nghttp2_buf_avail(buf) < frame->hd.length) { 487 return NGHTTP2_ERR_FRAME_SIZE_ERROR; 488 } 489 490 buf->pos -= NGHTTP2_FRAME_HDLEN; 491 492 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); 493 494 buf->last += 495 nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv); 496 497 return 0; 498} 499 500size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, 501 const nghttp2_settings_entry *iv, 502 size_t niv) { 503 size_t i; 504 for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { 505 nghttp2_put_uint16be(buf, (uint16_t)iv[i].settings_id); 506 nghttp2_put_uint32be(buf + 2, iv[i].value); 507 } 508 return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv; 509} 510 511void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, 512 nghttp2_settings_entry *iv, 513 size_t niv) { 514 frame->iv = iv; 515 frame->niv = niv; 516} 517 518void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, 519 const uint8_t *payload) { 520 iv->settings_id = nghttp2_get_uint16(&payload[0]); 521 iv->value = nghttp2_get_uint32(&payload[2]); 522} 523 524int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, 525 size_t *niv_ptr, 526 const uint8_t *payload, 527 size_t payloadlen, 528 nghttp2_mem *mem) { 529 size_t i; 530 531 *niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; 532 533 if (*niv_ptr == 0) { 534 *iv_ptr = NULL; 535 536 return 0; 537 } 538 539 *iv_ptr = 540 nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry)); 541 542 if (*iv_ptr == NULL) { 543 return NGHTTP2_ERR_NOMEM; 544 } 545 546 for (i = 0; i < *niv_ptr; ++i) { 547 size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; 548 nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]); 549 } 550 551 return 0; 552} 553 554int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, 555 nghttp2_push_promise *frame, 556 nghttp2_hd_deflater *deflater) { 557 size_t nv_offset = 4; 558 int rv; 559 nghttp2_buf *buf; 560 561 assert(bufs->head == bufs->cur); 562 563 buf = &bufs->cur->buf; 564 565 buf->pos += nv_offset; 566 buf->last = buf->pos; 567 568 /* This call will adjust buf->last to the correct position */ 569 rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); 570 571 if (rv == NGHTTP2_ERR_BUFFER_ERROR) { 572 rv = NGHTTP2_ERR_HEADER_COMP; 573 } 574 575 buf->pos -= nv_offset; 576 577 if (rv != 0) { 578 return rv; 579 } 580 581 nghttp2_put_uint32be(buf->pos, (uint32_t)frame->promised_stream_id); 582 583 frame->padlen = 0; 584 frame->hd.length = nghttp2_bufs_len(bufs); 585 586 return frame_pack_headers_shared(bufs, &frame->hd); 587} 588 589void nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, 590 const uint8_t *payload) { 591 frame->promised_stream_id = 592 nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; 593 frame->nva = NULL; 594 frame->nvlen = 0; 595} 596 597void nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { 598 nghttp2_buf *buf; 599 600 assert(bufs->head == bufs->cur); 601 602 buf = &bufs->head->buf; 603 604 assert(nghttp2_buf_avail(buf) >= 8); 605 606 buf->pos -= NGHTTP2_FRAME_HDLEN; 607 608 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); 609 610 buf->last = 611 nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data)); 612} 613 614void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, 615 const uint8_t *payload) { 616 memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data)); 617} 618 619int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) { 620 int rv; 621 nghttp2_buf *buf; 622 623 assert(bufs->head == bufs->cur); 624 625 buf = &bufs->head->buf; 626 627 buf->pos -= NGHTTP2_FRAME_HDLEN; 628 629 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); 630 631 nghttp2_put_uint32be(buf->last, (uint32_t)frame->last_stream_id); 632 buf->last += 4; 633 634 nghttp2_put_uint32be(buf->last, frame->error_code); 635 buf->last += 4; 636 637 rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len); 638 639 if (rv == NGHTTP2_ERR_BUFFER_ERROR) { 640 return NGHTTP2_ERR_FRAME_SIZE_ERROR; 641 } 642 643 if (rv != 0) { 644 return rv; 645 } 646 647 return 0; 648} 649 650void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, 651 const uint8_t *payload, 652 uint8_t *var_gift_payload, 653 size_t var_gift_payloadlen) { 654 frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; 655 frame->error_code = nghttp2_get_uint32(payload + 4); 656 657 frame->opaque_data = var_gift_payload; 658 frame->opaque_data_len = var_gift_payloadlen; 659} 660 661int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, 662 const uint8_t *payload, 663 size_t payloadlen, nghttp2_mem *mem) { 664 uint8_t *var_gift_payload; 665 size_t var_gift_payloadlen; 666 667 if (payloadlen > 8) { 668 var_gift_payloadlen = payloadlen - 8; 669 } else { 670 var_gift_payloadlen = 0; 671 } 672 673 if (!var_gift_payloadlen) { 674 var_gift_payload = NULL; 675 } else { 676 var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen); 677 678 if (var_gift_payload == NULL) { 679 return NGHTTP2_ERR_NOMEM; 680 } 681 682 memcpy(var_gift_payload, payload + 8, var_gift_payloadlen); 683 } 684 685 nghttp2_frame_unpack_goaway_payload(frame, payload, var_gift_payload, 686 var_gift_payloadlen); 687 688 return 0; 689} 690 691void nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, 692 nghttp2_window_update *frame) { 693 nghttp2_buf *buf; 694 695 assert(bufs->head == bufs->cur); 696 697 buf = &bufs->head->buf; 698 699 assert(nghttp2_buf_avail(buf) >= 4); 700 701 buf->pos -= NGHTTP2_FRAME_HDLEN; 702 703 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); 704 705 nghttp2_put_uint32be(buf->last, (uint32_t)frame->window_size_increment); 706 buf->last += 4; 707} 708 709void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, 710 const uint8_t *payload) { 711 frame->window_size_increment = 712 nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK; 713} 714 715void nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) { 716 int rv; 717 nghttp2_buf *buf; 718 nghttp2_ext_altsvc *altsvc; 719 720 /* This is required with --disable-assert. */ 721 (void)rv; 722 723 altsvc = frame->payload; 724 725 buf = &bufs->head->buf; 726 727 assert(nghttp2_buf_avail(buf) >= 728 2 + altsvc->origin_len + altsvc->field_value_len); 729 730 buf->pos -= NGHTTP2_FRAME_HDLEN; 731 732 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); 733 734 nghttp2_put_uint16be(buf->last, (uint16_t)altsvc->origin_len); 735 buf->last += 2; 736 737 rv = nghttp2_bufs_add(bufs, altsvc->origin, altsvc->origin_len); 738 739 assert(rv == 0); 740 741 rv = nghttp2_bufs_add(bufs, altsvc->field_value, altsvc->field_value_len); 742 743 assert(rv == 0); 744} 745 746void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, 747 size_t origin_len, uint8_t *payload, 748 size_t payloadlen) { 749 nghttp2_ext_altsvc *altsvc; 750 uint8_t *p; 751 752 altsvc = frame->payload; 753 p = payload; 754 755 altsvc->origin = p; 756 757 p += origin_len; 758 759 altsvc->origin_len = origin_len; 760 761 altsvc->field_value = p; 762 altsvc->field_value_len = (size_t)(payload + payloadlen - p); 763} 764 765int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame, 766 const uint8_t *payload, 767 size_t payloadlen, nghttp2_mem *mem) { 768 uint8_t *buf; 769 size_t origin_len; 770 771 if (payloadlen < 2) { 772 return NGHTTP2_FRAME_SIZE_ERROR; 773 } 774 775 origin_len = nghttp2_get_uint16(payload); 776 777 buf = nghttp2_mem_malloc(mem, payloadlen - 2); 778 if (!buf) { 779 return NGHTTP2_ERR_NOMEM; 780 } 781 782 nghttp2_cpymem(buf, payload + 2, payloadlen - 2); 783 784 nghttp2_frame_unpack_altsvc_payload(frame, origin_len, buf, payloadlen - 2); 785 786 return 0; 787} 788 789int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *frame) { 790 nghttp2_buf *buf; 791 nghttp2_ext_origin *origin; 792 nghttp2_origin_entry *orig; 793 size_t i; 794 795 origin = frame->payload; 796 797 buf = &bufs->head->buf; 798 799 if (nghttp2_buf_avail(buf) < frame->hd.length) { 800 return NGHTTP2_ERR_FRAME_SIZE_ERROR; 801 } 802 803 buf->pos -= NGHTTP2_FRAME_HDLEN; 804 805 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); 806 807 for (i = 0; i < origin->nov; ++i) { 808 orig = &origin->ov[i]; 809 nghttp2_put_uint16be(buf->last, (uint16_t)orig->origin_len); 810 buf->last += 2; 811 buf->last = nghttp2_cpymem(buf->last, orig->origin, orig->origin_len); 812 } 813 814 assert(nghttp2_buf_len(buf) == NGHTTP2_FRAME_HDLEN + frame->hd.length); 815 816 return 0; 817} 818 819int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, 820 const uint8_t *payload, 821 size_t payloadlen, nghttp2_mem *mem) { 822 nghttp2_ext_origin *origin; 823 const uint8_t *p, *end; 824 uint8_t *dst; 825 size_t originlen; 826 nghttp2_origin_entry *ov; 827 size_t nov = 0; 828 size_t len = 0; 829 830 origin = frame->payload; 831 p = end = payload; 832 if (payloadlen) { 833 end += payloadlen; 834 } 835 836 for (; p != end;) { 837 if (end - p < 2) { 838 return NGHTTP2_ERR_FRAME_SIZE_ERROR; 839 } 840 originlen = nghttp2_get_uint16(p); 841 p += 2; 842 if (originlen == 0) { 843 continue; 844 } 845 if (originlen > (size_t)(end - p)) { 846 return NGHTTP2_ERR_FRAME_SIZE_ERROR; 847 } 848 p += originlen; 849 /* 1 for terminal NULL */ 850 len += originlen + 1; 851 ++nov; 852 } 853 854 if (nov == 0) { 855 origin->ov = NULL; 856 origin->nov = 0; 857 858 return 0; 859 } 860 861 len += nov * sizeof(nghttp2_origin_entry); 862 863 ov = nghttp2_mem_malloc(mem, len); 864 if (ov == NULL) { 865 return NGHTTP2_ERR_NOMEM; 866 } 867 868 origin->ov = ov; 869 origin->nov = nov; 870 871 dst = (uint8_t *)ov + nov * sizeof(nghttp2_origin_entry); 872 p = payload; 873 874 for (; p != end;) { 875 originlen = nghttp2_get_uint16(p); 876 p += 2; 877 if (originlen == 0) { 878 continue; 879 } 880 ov->origin = dst; 881 ov->origin_len = originlen; 882 dst = nghttp2_cpymem(dst, p, originlen); 883 *dst++ = '\0'; 884 p += originlen; 885 ++ov; 886 } 887 888 return 0; 889} 890 891void nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs, 892 nghttp2_extension *frame) { 893 int rv; 894 nghttp2_buf *buf; 895 nghttp2_ext_priority_update *priority_update; 896 897 /* This is required with --disable-assert. */ 898 (void)rv; 899 900 priority_update = frame->payload; 901 902 buf = &bufs->head->buf; 903 904 assert(nghttp2_buf_avail(buf) >= 4 + priority_update->field_value_len); 905 906 buf->pos -= NGHTTP2_FRAME_HDLEN; 907 908 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); 909 910 nghttp2_put_uint32be(buf->last, (uint32_t)priority_update->stream_id); 911 buf->last += 4; 912 913 rv = nghttp2_bufs_add(bufs, priority_update->field_value, 914 priority_update->field_value_len); 915 916 assert(rv == 0); 917} 918 919void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame, 920 uint8_t *payload, 921 size_t payloadlen) { 922 nghttp2_ext_priority_update *priority_update; 923 924 assert(payloadlen >= 4); 925 926 priority_update = frame->payload; 927 928 priority_update->stream_id = 929 nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; 930 931 if (payloadlen > 4) { 932 priority_update->field_value = payload + 4; 933 priority_update->field_value_len = payloadlen - 4; 934 } else { 935 priority_update->field_value = NULL; 936 priority_update->field_value_len = 0; 937 } 938} 939 940nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, 941 size_t niv, nghttp2_mem *mem) { 942 nghttp2_settings_entry *iv_copy; 943 size_t len = niv * sizeof(nghttp2_settings_entry); 944 945 if (len == 0) { 946 return NULL; 947 } 948 949 iv_copy = nghttp2_mem_malloc(mem, len); 950 951 if (iv_copy == NULL) { 952 return NULL; 953 } 954 955 memcpy(iv_copy, iv, len); 956 957 return iv_copy; 958} 959 960int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) { 961 if (a->namelen != b->namelen || a->valuelen != b->valuelen) { 962 return 0; 963 } 964 965 if (a->name == NULL || b->name == NULL) { 966 assert(a->namelen == 0); 967 assert(b->namelen == 0); 968 } else if (memcmp(a->name, b->name, a->namelen) != 0) { 969 return 0; 970 } 971 972 if (a->value == NULL || b->value == NULL) { 973 assert(a->valuelen == 0); 974 assert(b->valuelen == 0); 975 } else if (memcmp(a->value, b->value, a->valuelen) != 0) { 976 return 0; 977 } 978 979 return 1; 980} 981 982void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) { 983 nghttp2_mem_free(mem, nva); 984} 985 986static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b, 987 size_t blen) { 988 int rv; 989 990 if (alen == blen) { 991 return memcmp(a, b, alen); 992 } 993 994 if (alen < blen) { 995 rv = memcmp(a, b, alen); 996 997 if (rv == 0) { 998 return -1; 999 } 1000 1001 return rv; 1002 } 1003 1004 rv = memcmp(a, b, blen); 1005 1006 if (rv == 0) { 1007 return 1; 1008 } 1009 1010 return rv; 1011} 1012 1013int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) { 1014 return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen); 1015} 1016 1017static int nv_compar(const void *lhs, const void *rhs) { 1018 const nghttp2_nv *a = (const nghttp2_nv *)lhs; 1019 const nghttp2_nv *b = (const nghttp2_nv *)rhs; 1020 int rv; 1021 1022 rv = bytes_compar(a->name, a->namelen, b->name, b->namelen); 1023 1024 if (rv == 0) { 1025 return bytes_compar(a->value, a->valuelen, b->value, b->valuelen); 1026 } 1027 1028 return rv; 1029} 1030 1031void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) { 1032 qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar); 1033} 1034 1035int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, 1036 size_t nvlen, nghttp2_mem *mem) { 1037 size_t i; 1038 uint8_t *data = NULL; 1039 size_t buflen = 0; 1040 nghttp2_nv *p; 1041 1042 if (nvlen == 0) { 1043 *nva_ptr = NULL; 1044 1045 return 0; 1046 } 1047 1048 for (i = 0; i < nvlen; ++i) { 1049 /* + 1 for null-termination */ 1050 if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) == 0) { 1051 buflen += nva[i].namelen + 1; 1052 } 1053 if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) == 0) { 1054 buflen += nva[i].valuelen + 1; 1055 } 1056 } 1057 1058 buflen += sizeof(nghttp2_nv) * nvlen; 1059 1060 *nva_ptr = nghttp2_mem_malloc(mem, buflen); 1061 1062 if (*nva_ptr == NULL) { 1063 return NGHTTP2_ERR_NOMEM; 1064 } 1065 1066 p = *nva_ptr; 1067 data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen; 1068 1069 for (i = 0; i < nvlen; ++i) { 1070 p->flags = nva[i].flags; 1071 1072 if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) { 1073 p->name = nva[i].name; 1074 p->namelen = nva[i].namelen; 1075 } else { 1076 if (nva[i].namelen) { 1077 memcpy(data, nva[i].name, nva[i].namelen); 1078 } 1079 p->name = data; 1080 p->namelen = nva[i].namelen; 1081 data[p->namelen] = '\0'; 1082 nghttp2_downcase(p->name, p->namelen); 1083 data += nva[i].namelen + 1; 1084 } 1085 1086 if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) { 1087 p->value = nva[i].value; 1088 p->valuelen = nva[i].valuelen; 1089 } else { 1090 if (nva[i].valuelen) { 1091 memcpy(data, nva[i].value, nva[i].valuelen); 1092 } 1093 p->value = data; 1094 p->valuelen = nva[i].valuelen; 1095 data[p->valuelen] = '\0'; 1096 data += nva[i].valuelen + 1; 1097 } 1098 1099 ++p; 1100 } 1101 return 0; 1102} 1103 1104int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) { 1105 size_t i; 1106 for (i = 0; i < niv; ++i) { 1107 switch (iv[i].settings_id) { 1108 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: 1109 break; 1110 case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: 1111 break; 1112 case NGHTTP2_SETTINGS_ENABLE_PUSH: 1113 if (iv[i].value != 0 && iv[i].value != 1) { 1114 return 0; 1115 } 1116 break; 1117 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: 1118 if (iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) { 1119 return 0; 1120 } 1121 break; 1122 case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: 1123 if (iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN || 1124 iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) { 1125 return 0; 1126 } 1127 break; 1128 case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: 1129 break; 1130 case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: 1131 if (iv[i].value != 0 && iv[i].value != 1) { 1132 return 0; 1133 } 1134 break; 1135 case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES: 1136 if (iv[i].value != 0 && iv[i].value != 1) { 1137 return 0; 1138 } 1139 break; 1140 } 1141 } 1142 return 1; 1143} 1144 1145static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) { 1146 size_t trail_padlen; 1147 size_t newlen; 1148 1149 DEBUGF("send: padlen=%zu, shift left 1 bytes\n", padlen); 1150 1151 memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN); 1152 1153 --buf->pos; 1154 1155 buf->pos[4] |= NGHTTP2_FLAG_PADDED; 1156 1157 newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen; 1158 nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3])); 1159 1160 if (framehd_only) { 1161 return; 1162 } 1163 1164 trail_padlen = padlen - 1; 1165 buf->pos[NGHTTP2_FRAME_HDLEN] = (uint8_t)trail_padlen; 1166 1167 /* zero out padding */ 1168 memset(buf->last, 0, trail_padlen); 1169 /* extend buffers trail_padlen bytes, since we ate previous padlen - 1170 trail_padlen byte(s) */ 1171 buf->last += trail_padlen; 1172} 1173 1174void nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, 1175 size_t padlen, int framehd_only) { 1176 nghttp2_buf *buf; 1177 1178 if (padlen == 0) { 1179 DEBUGF("send: padlen = 0, nothing to do\n"); 1180 1181 return; 1182 } 1183 1184 /* 1185 * We have arranged bufs like this: 1186 * 1187 * 0 1 2 3 1188 * 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 1 1189 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1190 * | |Frame header | Frame payload... : 1191 * +-+-----------------+-------------------------------------------+ 1192 * | |Frame header | Frame payload... : 1193 * +-+-----------------+-------------------------------------------+ 1194 * | |Frame header | Frame payload... : 1195 * +-+-----------------+-------------------------------------------+ 1196 * 1197 * We arranged padding so that it is included in the first frame 1198 * completely. For padded frame, we are going to adjust buf->pos of 1199 * frame which includes padding and serialize (memmove) frame header 1200 * in the correct position. Also extends buf->last to include 1201 * padding. 1202 */ 1203 1204 buf = &bufs->head->buf; 1205 1206 assert(nghttp2_buf_avail(buf) >= padlen - 1); 1207 1208 frame_set_pad(buf, padlen, framehd_only); 1209 1210 hd->length += padlen; 1211 hd->flags |= NGHTTP2_FLAG_PADDED; 1212 1213 DEBUGF("send: final payloadlen=%zu, padlen=%zu\n", hd->length, padlen); 1214} 1215