h2_frame_codec.c (49981B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2025 Evgeny Grin (Karlson2k) 5 6 GNU libmicrohttpd is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 GNU libmicrohttpd is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 Alternatively, you can redistribute GNU libmicrohttpd and/or 17 modify it under the terms of the GNU General Public License as 18 published by the Free Software Foundation; either version 2 of 19 the License, or (at your option) any later version, together 20 with the eCos exception, as follows: 21 22 As a special exception, if other files instantiate templates or 23 use macros or inline functions from this file, or you compile this 24 file and link it with other works to produce a work based on this 25 file, this file does not by itself cause the resulting work to be 26 covered by the GNU General Public License. However the source code 27 for this file must still be made available in accordance with 28 section (3) of the GNU General Public License v2. 29 30 This exception does not invalidate any other reasons why a work 31 based on this file might be covered by the GNU General Public 32 License. 33 34 You should have received copies of the GNU Lesser General Public 35 License and the GNU General Public License along with this library; 36 if not, see <https://www.gnu.org/licenses/>. 37 */ 38 39 /** 40 * @file src/mhd2/h2/h2_frame_codec.c 41 * @brief Implementation of HTTP/2 frame decoding and encoding functions 42 * @author Karlson2k (Evgeny Grin) 43 * 44 * Details of HTTP/2 frame layout are defined in RFC 9113. 45 */ 46 47 #include "mhd_sys_options.h" 48 49 #include "sys_bool_type.h" 50 #include "sys_base_types.h" 51 52 #include "mhd_assert.h" 53 #include "mhd_unreachable.h" 54 55 #include <string.h> 56 57 #include "mhd_bithelpers.h" 58 59 #include "h2_bit_masks.h" 60 61 #include "h2_frame_length.h" 62 63 #include "h2_frame_codec.h" 64 65 66 /** 67 * HTTP/2 frame flag PRIORITY (0x20) used by HEADERS frames 68 */ 69 #define mhd_FFLAG_PRIORITY (0x20u) 70 /** 71 * HTTP/2 frame flag PADDED (0x08) used by DATA, HEADERS and PUSH_PROMISE frames 72 */ 73 #define mhd_FFLAG_PADDED (0x08u) 74 /** 75 * HTTP/2 frame flag END_HEADERS (0x04) used by HEADERS and CONTINUATION frames 76 */ 77 #define mhd_FFLAG_END_HEADERS (0x04u) 78 /** 79 * HTTP/2 frame flag END_STREAM (0x01) used by DATA and HEADERS frames 80 */ 81 #define mhd_FFLAG_END_STREAM (0x01u) 82 /** 83 * HTTP/2 frame flag ACK (0x01) used by SETTINGS and PING frames 84 */ 85 #define mhd_FFLAG_ACK (0x01u) 86 87 /** 88 * Exclusive bit (0x80000000) for the HEADERS/PRIORITY dependency field. 89 */ 90 #define mhd_FFLAG_HEADERS_EXCLUSIVE (0x80000000u) 91 92 93 /** 94 * Decode a DATA frame. 95 * 96 * Validates sizes data and flags data for valid combinations. 97 * 98 * @param payload_buff_size the available input bytes in the @a payload_buff 99 * @param payload_buff the pointer to the beginning of the frame payload 100 * @param flags the frame flags byte 101 * @param[in,out] frame_info the frame information; updated with DATA details 102 * @param[out] frame_payload set to the final payload slice that starts after 103 * any extra header fields and ends before padding 104 * bytes (if any) 105 * @return #mhd_H2_F_DEC_OK on success, 106 * or a relevant decode error code 107 */ 108 static MHD_FN_PAR_NONNULL_ALL_ 109 MHD_FN_PAR_IN_SIZE_ (2,1) 110 MHD_FN_PAR_INOUT_ (4) MHD_FN_PAR_OUT_ (5) 111 enum mhd_H2FrameDecodeResult 112 frame_decode_data (size_t payload_buff_size, 113 uint8_t *restrict payload_buff, 114 uint_least8_t flags, 115 union mhd_H2FrameUnion *restrict frame_info, 116 struct mhd_Buffer *restrict frame_payload) 117 { 118 struct mhd_H2FrameDataInfo *const data = 119 &(frame_info->data); 120 size_t real_payload_pos; 121 122 mhd_assert (mhd_H2_FRAME_DATA_ID == data->type); 123 124 if (0u == data->stream_id) 125 return mhd_H2_F_DEC_CONN_ERR_PROT; 126 127 data->padded = (0 != (flags & mhd_FFLAG_PADDED)); 128 data->end_stream = (0 != (flags & mhd_FFLAG_END_STREAM)); 129 130 real_payload_pos = 0u; 131 if (data->padded) 132 { 133 if (real_payload_pos >= data->length) 134 return mhd_H2_F_DEC_CONN_ERR_PROT; 135 if (real_payload_pos >= payload_buff_size) 136 return mhd_H2_F_DEC_F_HEADER_INCOMPLETE; 137 138 data->pad_length = (uint_least8_t) payload_buff[real_payload_pos++]; 139 } 140 else 141 data->pad_length = 0u; 142 143 if (data->length < (real_payload_pos + data->pad_length)) 144 return mhd_H2_F_DEC_CONN_ERR_PROT; 145 if (payload_buff_size < data->length) 146 return mhd_H2_F_DEC_F_PAYLOAD_INCOMPLETE; 147 148 frame_payload->data = (char *) payload_buff + real_payload_pos; 149 frame_payload->size = 150 (size_t) (data->length - real_payload_pos - data->pad_length); 151 152 return mhd_H2_F_DEC_OK; 153 } 154 155 156 /** 157 * Decode a HEADERS frame. 158 * 159 * Validates sizes data and flags data for valid combinations. 160 * 161 * @param payload_buff_size the available input bytes in the @a payload_buff 162 * @param payload_buff the pointer to the beginning of the frame payload 163 * @param flags the frame flags byte 164 * @param[in,out] frame_info the frame information; 165 * updated with HEADERS details 166 * @param[out] frame_payload set to the final payload slice that starts after 167 * any extra header fields and ends before padding 168 * bytes (if any) 169 * @return #mhd_H2_F_DEC_OK on success, 170 * or a relevant decode error code 171 */ 172 static MHD_FN_PAR_NONNULL_ALL_ 173 MHD_FN_PAR_IN_SIZE_ (2,1) 174 MHD_FN_PAR_INOUT_ (4) MHD_FN_PAR_OUT_ (5) 175 enum mhd_H2FrameDecodeResult 176 frame_decode_headers (size_t payload_buff_size, 177 uint8_t *restrict payload_buff, 178 uint_least8_t flags, 179 union mhd_H2FrameUnion *restrict frame_info, 180 struct mhd_Buffer *restrict frame_payload) 181 { 182 struct mhd_H2FrameHeadersInfo *const headers = 183 &(frame_info->headers); 184 size_t real_payload_pos; 185 186 mhd_assert (mhd_H2_FRAME_HEADERS_ID == headers->type); 187 188 if (0u == headers->stream_id) 189 return mhd_H2_F_DEC_CONN_ERR_PROT; 190 191 headers->priority = (0 != (flags & mhd_FFLAG_PRIORITY)); 192 headers->padded = (0 != (flags & mhd_FFLAG_PADDED)); 193 headers->end_headers = (0 != (flags & mhd_FFLAG_END_HEADERS)); 194 headers->end_stream = (0 != (flags & mhd_FFLAG_END_STREAM)); 195 196 real_payload_pos = 0u; 197 if (headers->padded) 198 { 199 if (real_payload_pos >= headers->length) 200 return mhd_H2_F_DEC_CONN_ERR_PROT; 201 if (real_payload_pos >= payload_buff_size) 202 return mhd_H2_F_DEC_F_HEADER_INCOMPLETE; 203 headers->pad_length = (uint_least8_t) payload_buff[real_payload_pos++]; 204 } 205 else 206 headers->pad_length = 0u; 207 208 if (headers->priority) 209 { 210 uint_least32_t stream_dep_id; 211 if ((real_payload_pos + 5u) > headers->length) 212 return mhd_H2_F_DEC_CONN_ERR_PROT; 213 if ((real_payload_pos + 5u) > payload_buff_size) 214 return mhd_H2_F_DEC_F_HEADER_INCOMPLETE; 215 stream_dep_id = mhd_GET_32BIT_BE_UNALIGN (payload_buff + real_payload_pos); 216 real_payload_pos += 4u; 217 headers->exclusive = (0 != (stream_dep_id & mhd_FFLAG_HEADERS_EXCLUSIVE)); 218 headers->stream_dependency = (uint_least32_t) 219 (stream_dep_id & mhd_H2_STREAM_ID_MASK); 220 if (headers->stream_id == headers->stream_dependency) 221 return mhd_H2_F_DEC_STREAM_ERR_PROT; 222 /* Use "on-wire" 'weight' format. Add one to get a real number. 223 Do not bother handling calculations here as it is deprecated anyway. */ 224 headers->weight = payload_buff[real_payload_pos++]; 225 } 226 else 227 { 228 headers->exclusive = false; 229 headers->stream_dependency = 0u; 230 headers->weight = 0u; 231 } 232 233 if (headers->length < (real_payload_pos + headers->pad_length)) 234 return mhd_H2_F_DEC_CONN_ERR_PROT; 235 if (payload_buff_size < headers->length) 236 return mhd_H2_F_DEC_F_PAYLOAD_INCOMPLETE; 237 238 frame_payload->data = (char *) payload_buff + real_payload_pos; 239 frame_payload->size = 240 (size_t) (headers->length - real_payload_pos - headers->pad_length); 241 242 return mhd_H2_F_DEC_OK; 243 } 244 245 246 /** 247 * Decode a PRIORITY frame. 248 * 249 * Validates the fixed size of the frame. 250 * 251 * @param payload_buff_size the available input bytes in the @a payload_buff 252 * @param payload_buff the pointer to the beginning of the frame payload 253 * @param[in,out] frame_info the frame information; 254 * updated with PRIORITY details 255 * @return #mhd_H2_F_DEC_OK on success, 256 * or a relevant decode error code 257 */ 258 static MHD_FN_PAR_NONNULL_ALL_ 259 MHD_FN_PAR_IN_SIZE_ (2,1) 260 MHD_FN_PAR_INOUT_ (3) 261 enum mhd_H2FrameDecodeResult 262 frame_decode_priority (size_t payload_buff_size, 263 uint8_t *restrict payload_buff, 264 union mhd_H2FrameUnion *restrict frame_info) 265 { 266 struct mhd_H2FramePriorityInfo *const priority = 267 &(frame_info->priority); 268 uint_least32_t stream_dep_id; 269 270 mhd_assert (mhd_H2_FRAME_PRIORITY_ID == priority->type); 271 272 if (0u == priority->stream_id) 273 return mhd_H2_F_DEC_CONN_ERR_PROT; 274 if (5u != priority->length) 275 return mhd_H2_F_DEC_STREAM_ERR_F_SIZE; 276 if (5u > payload_buff_size) 277 return mhd_H2_F_DEC_F_HEADER_INCOMPLETE; 278 279 stream_dep_id = mhd_GET_32BIT_BE_UNALIGN (payload_buff + 0u); 280 priority->exclusive = (0 != (stream_dep_id & mhd_FFLAG_HEADERS_EXCLUSIVE)); 281 priority->stream_dependency = (uint_least32_t) 282 (stream_dep_id & mhd_H2_STREAM_ID_MASK); 283 /* Use "on-wire" 'weight' format. Add one to get a real number. 284 Do not bother handling calculations here as it is deprecated anyway. */ 285 priority->weight = payload_buff[4]; 286 287 if (priority->stream_id == priority->stream_dependency) 288 return mhd_H2_F_DEC_STREAM_ERR_PROT; 289 290 return mhd_H2_F_DEC_OK; 291 } 292 293 294 /** 295 * Decode an RST_STREAM frame. 296 * 297 * Validates the fixed size of the frame. 298 * 299 * @param payload_buff_size the available input bytes in the @a payload_buff 300 * @param payload_buff the pointer to the beginning of the frame payload 301 * @param[in,out] frame_info the frame information; 302 * updated with RST_STREAM details 303 * @return #mhd_H2_F_DEC_OK on success, 304 * or a relevant decode error code 305 */ 306 static MHD_FN_PAR_NONNULL_ALL_ 307 MHD_FN_PAR_IN_SIZE_ (2,1) 308 MHD_FN_PAR_INOUT_ (3) 309 enum mhd_H2FrameDecodeResult 310 frame_decode_rst_stream (size_t payload_buff_size, 311 uint8_t *restrict payload_buff, 312 union mhd_H2FrameUnion *restrict frame_info) 313 { 314 struct mhd_H2FrameRstStreamInfo *const rst_stream = 315 &(frame_info->rst_stream); 316 uint_fast32_t load_buff32b; 317 318 mhd_assert (mhd_H2_FRAME_RST_STREAM_ID == rst_stream->type); 319 320 if (0u == rst_stream->stream_id) 321 return mhd_H2_F_DEC_CONN_ERR_PROT; 322 if (4u != rst_stream->length) 323 return mhd_H2_F_DEC_STREAM_ERR_F_SIZE; 324 if (4u > payload_buff_size) 325 return mhd_H2_F_DEC_F_HEADER_INCOMPLETE; 326 327 load_buff32b = mhd_GET_32BIT_BE_UNALIGN (payload_buff + 0u); 328 rst_stream->error_code = (enum mhd_H2ErrorCode) load_buff32b; 329 330 return mhd_H2_F_DEC_OK; 331 } 332 333 334 /** 335 * Decode a SETTINGS frame. 336 * 337 * Validates stream identifier (must be zero), ACK semantics and that the 338 * length is a multiple of 6; exposes raw SETTINGS pairs via @a frame_payload 339 * 340 * @param payload_buff_size the available input bytes in the @a payload_buff 341 * @param payload_buff the pointer to the beginning of the frame payload 342 * @param flags the frame flags byte 343 * @param[in,out] frame_info the frame information; 344 * updated with SETTINGS details 345 * @param[out] frame_payload set to the contiguous sequence of 6-byte pairs 346 * @return #mhd_H2_F_DEC_OK on success, 347 * or a relevant decode error code 348 */ 349 static MHD_FN_PAR_NONNULL_ALL_ 350 MHD_FN_PAR_IN_SIZE_ (2,1) 351 MHD_FN_PAR_INOUT_ (4) MHD_FN_PAR_OUT_ (5) 352 enum mhd_H2FrameDecodeResult 353 frame_decode_settings (size_t payload_buff_size, 354 uint8_t *restrict payload_buff, 355 uint_least8_t flags, 356 union mhd_H2FrameUnion *restrict frame_info, 357 struct mhd_Buffer *restrict frame_payload) 358 { 359 struct mhd_H2FrameSettingsInfo *const settings = 360 &(frame_info->settings); 361 362 mhd_assert (mhd_H2_FRAME_SETTINGS_ID == settings->type); 363 364 if (0u != settings->stream_id) 365 return mhd_H2_F_DEC_CONN_ERR_PROT; 366 367 settings->ack = (0 != (flags & mhd_FFLAG_ACK)); 368 369 if (settings->ack && (0u != settings->length)) 370 return mhd_H2_F_DEC_CONN_ERR_F_SIZE; 371 372 if (0u != (settings->length % 6)) 373 return mhd_H2_F_DEC_CONN_ERR_F_SIZE; 374 375 if (payload_buff_size < settings->length) 376 return mhd_H2_F_DEC_F_PAYLOAD_INCOMPLETE; 377 378 frame_payload->data = (char *) payload_buff; 379 frame_payload->size = settings->length; 380 381 return mhd_H2_F_DEC_OK; 382 } 383 384 385 /** 386 * Decode a PUSH_PROMISE frame. 387 * 388 * Validates sizes and exposes the header block fragment via @a frame_payload. 389 * 390 * @param payload_buff_size the available input bytes in the @a payload_buff 391 * @param payload_buff the pointer to the beginning of the frame payload 392 * @param flags the frame flags byte 393 * @param[in,out] frame_info the frame information; 394 * updated with PUSH_PROMISE details 395 * @param[out] frame_payload set to the header block fragment slice within 396 * payload 397 * @return #mhd_H2_F_DEC_OK on success, 398 * or a relevant decode error code 399 */ 400 static MHD_FN_PAR_NONNULL_ALL_ 401 MHD_FN_PAR_IN_SIZE_ (2,1) 402 MHD_FN_PAR_INOUT_ (4) MHD_FN_PAR_OUT_ (5) 403 enum mhd_H2FrameDecodeResult 404 frame_decode_push_promise (size_t payload_buff_size, 405 uint8_t *restrict payload_buff, 406 uint_least8_t flags, 407 union mhd_H2FrameUnion *restrict frame_info, 408 struct mhd_Buffer *restrict frame_payload) 409 { 410 struct mhd_H2FramePushPromiseInfo *const push_promise = 411 &(frame_info->push_promise); 412 size_t real_payload_pos; 413 414 mhd_assert (mhd_H2_FRAME_PUSH_PROMISE_ID == push_promise->type); 415 416 push_promise->padded = (0 != (flags & mhd_FFLAG_PADDED)); 417 push_promise->end_headers = (0 != (flags & mhd_FFLAG_END_HEADERS)); 418 419 real_payload_pos = 0u; 420 if (push_promise->padded) 421 { 422 if (real_payload_pos >= push_promise->length) 423 return mhd_H2_F_DEC_CONN_ERR_PROT; 424 if (real_payload_pos >= payload_buff_size) 425 return mhd_H2_F_DEC_F_HEADER_INCOMPLETE; 426 push_promise->pad_length = 427 (uint_least8_t) payload_buff[real_payload_pos++]; 428 } 429 else 430 push_promise->pad_length = 0u; 431 432 if (real_payload_pos + 4u > push_promise->length) 433 return mhd_H2_F_DEC_CONN_ERR_PROT; 434 if (real_payload_pos + 4u > payload_buff_size) 435 return mhd_H2_F_DEC_F_HEADER_INCOMPLETE; 436 437 push_promise->promised_stream_id = 438 (uint_least32_t) 439 (mhd_GET_32BIT_BE_UNALIGN (payload_buff + real_payload_pos) 440 & mhd_H2_STREAM_ID_MASK); 441 real_payload_pos += 4u; 442 443 if (push_promise->length < (real_payload_pos + push_promise->pad_length)) 444 return mhd_H2_F_DEC_CONN_ERR_PROT; 445 if (0u == push_promise->stream_id) 446 return mhd_H2_F_DEC_CONN_ERR_PROT; 447 if (0u == push_promise->promised_stream_id) 448 return mhd_H2_F_DEC_CONN_ERR_PROT; 449 if (payload_buff_size < push_promise->length) 450 return mhd_H2_F_DEC_F_PAYLOAD_INCOMPLETE; 451 452 frame_payload->data = (char *) payload_buff + real_payload_pos; 453 frame_payload->size = 454 (size_t) 455 (push_promise->length - real_payload_pos - push_promise->pad_length); 456 457 return mhd_H2_F_DEC_OK; 458 } 459 460 461 /** 462 * Decode a PING frame. 463 * 464 * Validates stream identifier (must be zero) and fixed size (8). 465 * 466 * @param payload_buff_size the available input bytes in the @a payload_buff 467 * @param payload_buff the pointer to the beginning of the frame payload 468 * @param flags the frame flags byte 469 * @param[in,out] frame_info the frame information; 470 * updated with PING details 471 * @return #mhd_H2_F_DEC_OK on success, 472 * or a relevant decode error code 473 */ 474 static MHD_FN_PAR_NONNULL_ALL_ 475 MHD_FN_PAR_IN_SIZE_ (2,1) 476 MHD_FN_PAR_INOUT_ (4) 477 enum mhd_H2FrameDecodeResult 478 frame_decode_ping (size_t payload_buff_size, 479 uint8_t *restrict payload_buff, 480 uint_least8_t flags, 481 union mhd_H2FrameUnion *restrict frame_info) 482 { 483 struct mhd_H2FramePingInfo *const ping = 484 &(frame_info->ping); 485 486 mhd_assert (mhd_H2_FRAME_PING_ID == ping->type); 487 488 ping->ack = (0 != (flags & mhd_FFLAG_ACK)); 489 490 if (0u != ping->stream_id) 491 return mhd_H2_F_DEC_CONN_ERR_PROT; 492 if (8u != ping->length) 493 return mhd_H2_F_DEC_CONN_ERR_F_SIZE; 494 if (8u > payload_buff_size) 495 return mhd_H2_F_DEC_F_HEADER_INCOMPLETE; 496 497 memcpy (ping->opaque_data, 498 payload_buff, 499 8u); 500 501 return mhd_H2_F_DEC_OK; 502 } 503 504 505 /** 506 * Decode a GOAWAY frame. 507 * 508 * Validates stream identifier (must be zero) and that length is at least 8. 509 * 510 * @param payload_buff_size the available input bytes in the @a payload_buff 511 * @param payload_buff the pointer to the beginning of the frame payload 512 * @param[in,out] frame_info the frame information; 513 * updated with GOAWAY details 514 * @param[out] frame_payload set to the optional debug data slice 515 * @return #mhd_H2_F_DEC_OK on success, 516 * or a relevant decode error code 517 */ 518 static MHD_FN_PAR_NONNULL_ALL_ 519 MHD_FN_PAR_IN_SIZE_ (2,1) 520 MHD_FN_PAR_INOUT_ (3) MHD_FN_PAR_OUT_ (4) 521 enum mhd_H2FrameDecodeResult 522 frame_decode_goaway (size_t payload_buff_size, 523 uint8_t *restrict payload_buff, 524 union mhd_H2FrameUnion *restrict frame_info, 525 struct mhd_Buffer *restrict frame_payload) 526 { 527 struct mhd_H2FrameGoawayInfo *const goaway = 528 &(frame_info->goaway); 529 size_t real_payload_pos; 530 uint_fast32_t load_buff32b; 531 532 mhd_assert (mhd_H2_FRAME_GOAWAY_ID == goaway->type); 533 534 if (0u != goaway->stream_id) 535 return mhd_H2_F_DEC_CONN_ERR_PROT; 536 if (8u > goaway->length) 537 return mhd_H2_F_DEC_CONN_ERR_F_SIZE; 538 if (8u > payload_buff_size) 539 return mhd_H2_F_DEC_F_HEADER_INCOMPLETE; 540 541 real_payload_pos = 0u; 542 goaway->last_stream_id = 543 (uint_least32_t) 544 (mhd_GET_32BIT_BE_UNALIGN (payload_buff + real_payload_pos) 545 & mhd_MASK_31BITS); 546 real_payload_pos += 4u; 547 load_buff32b = mhd_GET_32BIT_BE_UNALIGN (payload_buff + real_payload_pos); 548 goaway->error_code = (enum mhd_H2ErrorCode) load_buff32b; 549 real_payload_pos += 4u; 550 551 frame_payload->data = (char *) payload_buff + real_payload_pos; 552 frame_payload->size = (size_t) (goaway->length - real_payload_pos); 553 554 return mhd_H2_F_DEC_OK; 555 } 556 557 558 /** 559 * Decode a WINDOW_UPDATE frame. 560 * 561 * Validates fixed size (4) and that the increment is non-zero. 562 * 563 * @param payload_buff_size the available input bytes in the @a payload_buff 564 * @param payload_buff the pointer to the beginning of the frame payload 565 * @param[in,out] frame_info the frame information; 566 * updated with WINDOW_UPDATE details 567 * @return #mhd_H2_F_DEC_OK on success, 568 * or a relevant decode error code 569 */ 570 static MHD_FN_PAR_NONNULL_ALL_ 571 MHD_FN_PAR_IN_SIZE_ (2,1) 572 MHD_FN_PAR_INOUT_ (3) 573 enum mhd_H2FrameDecodeResult 574 frame_decode_window_update (size_t payload_buff_size, 575 uint8_t *restrict payload_buff, 576 union mhd_H2FrameUnion *restrict frame_info) 577 { 578 struct mhd_H2FrameWindowUpdateInfo *const window_update = 579 &(frame_info->window_update); 580 581 mhd_assert (mhd_H2_FRAME_WINDOW_UPDATE_ID == window_update->type); 582 583 if (4u != window_update->length) 584 return mhd_H2_F_DEC_CONN_ERR_F_SIZE; 585 if (4u > payload_buff_size) 586 return mhd_H2_F_DEC_F_HEADER_INCOMPLETE; 587 588 window_update->window_size_increment = 589 (uint_least32_t) 590 (mhd_GET_32BIT_BE_UNALIGN (payload_buff + 0u) & mhd_MASK_31BITS); 591 592 if (0u == window_update->window_size_increment) 593 return (0u == window_update->stream_id) ? 594 mhd_H2_F_DEC_CONN_ERR_PROT : mhd_H2_F_DEC_STREAM_ERR_PROT; 595 596 return mhd_H2_F_DEC_OK; 597 } 598 599 600 /** 601 * Decode a CONTINUATION frame. 602 * 603 * @param payload_buff_size the available input bytes in the @a payload_buff 604 * @param payload_buff the pointer to the beginning of the frame payload 605 * @param flags the frame flags byte 606 * @param[in,out] frame_info the frame information; 607 * updated with CONTINUATION details 608 * @param[out] frame_payload set to the continuation fragment slice 609 * @return #mhd_H2_F_DEC_OK on success, 610 * or a relevant decode error code 611 */ 612 static MHD_FN_PAR_NONNULL_ALL_ 613 MHD_FN_PAR_IN_SIZE_ (2,1) 614 MHD_FN_PAR_INOUT_ (4) MHD_FN_PAR_OUT_ (5) 615 enum mhd_H2FrameDecodeResult 616 frame_decode_continuation (size_t payload_buff_size, 617 uint8_t *restrict payload_buff, 618 uint_least8_t flags, 619 union mhd_H2FrameUnion *restrict frame_info, 620 struct mhd_Buffer *restrict frame_payload) 621 { 622 struct mhd_H2FrameContinuationInfo *const continuation = 623 &(frame_info->continuation); 624 625 mhd_assert (mhd_H2_FRAME_CONTINUATION_ID == continuation->type); 626 627 if (0u == continuation->stream_id) 628 return mhd_H2_F_DEC_CONN_ERR_PROT; 629 630 continuation->end_headers = (0 != (flags & mhd_FFLAG_END_HEADERS)); 631 632 if (payload_buff_size < continuation->length) 633 return mhd_H2_F_DEC_F_PAYLOAD_INCOMPLETE; 634 635 frame_payload->data = (char *) payload_buff; 636 frame_payload->size = continuation->length; 637 638 return mhd_H2_F_DEC_OK; 639 } 640 641 642 /** 643 * Decode an unknown frame type as opaque bytes. 644 * 645 * Exposes the entire payload via @a frame_payload if fully available 646 * 647 * @param payload_buff_size the available input bytes in the @a payload_buff 648 * @param payload_buff the pointer to the beginning of the frame payload 649 * @param[in,out] frame_info the frame information; 650 * only selector fields are used 651 * @param[out] frame_payload set to the raw payload slice 652 * @return #mhd_H2_F_DEC_OK on success, 653 * or a relevant decode error code 654 */ 655 static MHD_FN_PAR_NONNULL_ALL_ 656 MHD_FN_PAR_IN_SIZE_ (2,1) 657 MHD_FN_PAR_INOUT_ (3) MHD_FN_PAR_OUT_ (4) 658 enum mhd_H2FrameDecodeResult 659 frame_decode_unknown_type (size_t payload_buff_size, 660 uint8_t *restrict payload_buff, 661 union mhd_H2FrameUnion *restrict frame_info, 662 struct mhd_Buffer *restrict frame_payload) 663 { 664 if (payload_buff_size < frame_info->selector.length) 665 return mhd_H2_F_DEC_F_PAYLOAD_INCOMPLETE; 666 667 frame_payload->data = (char *) payload_buff; 668 frame_payload->size = frame_info->selector.length; 669 670 return mhd_H2_F_DEC_OK; 671 } 672 673 674 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 675 MHD_FN_PAR_IN_SIZE_ (2,1) 676 MHD_FN_PAR_OUT_ (4) MHD_FN_PAR_OUT_ (5) enum mhd_H2FrameDecodeResult 677 mhd_h2_frame_decode (size_t buff_size, 678 uint8_t *restrict buff, 679 uint_least32_t max_frame_size, 680 union mhd_H2FrameUnion *restrict frame_info, 681 struct mhd_Buffer *restrict frame_payload) 682 { 683 uint_fast32_t len_and_type; 684 uint_least32_t length; 685 uint_least8_t type; 686 uint_least8_t flags; 687 uint_least32_t stream_id; 688 689 if (mhd_H2_FR_SIZE_MIN > buff_size) 690 return mhd_H2_F_DEC_F_HEADER_INCOMPLETE; 691 692 len_and_type = mhd_GET_32BIT_BE_UNALIGN (buff + 0u); 693 694 length = (uint_least32_t) (len_and_type >> 8u); 695 type = (uint_least8_t) (len_and_type & 0xFFu); 696 697 flags = buff[4]; 698 699 stream_id = (mhd_GET_32BIT_BE_UNALIGN (buff + 5u) & mhd_H2_STREAM_ID_MASK); 700 701 frame_info->selector.length = length; 702 frame_info->selector.type = (enum mhd_H2FrameIDs) type; 703 frame_info->selector.stream_id = stream_id; 704 705 if (max_frame_size < length) 706 return mhd_H2_F_DEC_CONN_ERR_F_SIZE; 707 708 switch ((enum mhd_H2FrameIDs) type) 709 { 710 case mhd_H2_FRAME_IDS_DATA_ID: 711 return frame_decode_data (buff_size - mhd_H2_FR_SIZE_MIN, 712 buff + mhd_H2_FR_SIZE_MIN, 713 flags, 714 frame_info, 715 frame_payload); 716 case mhd_H2_FRAME_IDS_HEADERS_ID: 717 return frame_decode_headers (buff_size - mhd_H2_FR_SIZE_MIN, 718 buff + mhd_H2_FR_SIZE_MIN, 719 flags, 720 frame_info, 721 frame_payload); 722 case mhd_H2_FRAME_IDS_PRIORITY_ID: 723 frame_payload->size = 0u; 724 frame_payload->data = NULL; 725 return frame_decode_priority (buff_size - mhd_H2_FR_SIZE_MIN, 726 buff + mhd_H2_FR_SIZE_MIN, 727 frame_info); 728 case mhd_H2_FRAME_IDS_RST_STREAM_ID: 729 frame_payload->size = 0u; 730 frame_payload->data = NULL; 731 return frame_decode_rst_stream (buff_size - mhd_H2_FR_SIZE_MIN, 732 buff + mhd_H2_FR_SIZE_MIN, 733 frame_info); 734 case mhd_H2_FRAME_IDS_SETTINGS_ID: 735 return frame_decode_settings (buff_size - mhd_H2_FR_SIZE_MIN, 736 buff + mhd_H2_FR_SIZE_MIN, 737 flags, 738 frame_info, 739 frame_payload); 740 case mhd_H2_FRAME_IDS_PUSH_PROMISE_ID: 741 return frame_decode_push_promise (buff_size - mhd_H2_FR_SIZE_MIN, 742 buff + mhd_H2_FR_SIZE_MIN, 743 flags, 744 frame_info, 745 frame_payload); 746 case mhd_H2_FRAME_IDS_PING_ID: 747 frame_payload->size = 0u; 748 frame_payload->data = NULL; 749 return frame_decode_ping (buff_size - mhd_H2_FR_SIZE_MIN, 750 buff + mhd_H2_FR_SIZE_MIN, 751 flags, 752 frame_info); 753 case mhd_H2_FRAME_IDS_GOAWAY_ID: 754 return frame_decode_goaway (buff_size - mhd_H2_FR_SIZE_MIN, 755 buff + mhd_H2_FR_SIZE_MIN, 756 frame_info, 757 frame_payload); 758 case mhd_H2_FRAME_IDS_WINDOW_UPDATE_ID: 759 frame_payload->size = 0u; 760 frame_payload->data = NULL; 761 return frame_decode_window_update (buff_size - mhd_H2_FR_SIZE_MIN, 762 buff + mhd_H2_FR_SIZE_MIN, 763 frame_info); 764 case mhd_H2_FRAME_IDS_CONTINUATION_ID: 765 return frame_decode_continuation (buff_size - mhd_H2_FR_SIZE_MIN, 766 buff + mhd_H2_FR_SIZE_MIN, 767 flags, 768 frame_info, 769 frame_payload); 770 default: 771 break; 772 } 773 return frame_decode_unknown_type (buff_size - mhd_H2_FR_SIZE_MIN, 774 buff + mhd_H2_FR_SIZE_MIN, 775 frame_info, 776 frame_payload); 777 } 778 779 780 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 781 MHD_FN_PAR_IN_ (1) 782 size_t 783 mhd_h2_frame_get_extra_hdr_size ( 784 const union mhd_H2FrameUnion *restrict frame_info) 785 { 786 size_t extra; 787 extra = 0u; 788 switch (frame_info->selector.type) 789 { 790 case mhd_H2_FRAME_IDS_DATA_ID: 791 if (frame_info->data.padded) 792 extra += 1u; 793 break; 794 case mhd_H2_FRAME_IDS_HEADERS_ID: 795 if (frame_info->headers.padded) 796 extra += 1u; 797 if (frame_info->headers.priority) 798 extra += 5u; 799 break; 800 case mhd_H2_FRAME_IDS_PRIORITY_ID: 801 extra += 5u; 802 break; 803 case mhd_H2_FRAME_IDS_RST_STREAM_ID: 804 extra += 4u; 805 break; 806 case mhd_H2_FRAME_IDS_SETTINGS_ID: 807 break; 808 case mhd_H2_FRAME_IDS_PUSH_PROMISE_ID: 809 if (frame_info->push_promise.padded) 810 extra += 1u; 811 extra += 4u; 812 break; 813 case mhd_H2_FRAME_IDS_PING_ID: 814 extra += mhd_H2_FR_FIXED_LEN_PING; 815 break; 816 case mhd_H2_FRAME_IDS_GOAWAY_ID: 817 extra += 8u; 818 break; 819 case mhd_H2_FRAME_IDS_WINDOW_UPDATE_ID: 820 extra += mhd_H2_FR_FIXED_LEN_WINDOW_UPDATE; 821 break; 822 case mhd_H2_FRAME_IDS_CONTINUATION_ID: 823 break; 824 default: 825 break; 826 } 827 return extra; 828 } 829 830 831 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 832 MHD_FN_PAR_IN_ (1) 833 size_t 834 mhd_h2_frame_get_padding_size ( 835 const union mhd_H2FrameUnion *restrict frame_info) 836 { 837 switch (frame_info->selector.type) 838 { 839 case mhd_H2_FRAME_IDS_DATA_ID: 840 mhd_assert ((0u == frame_info->data.pad_length) || \ 841 frame_info->data.padded); 842 return frame_info->data.pad_length; 843 case mhd_H2_FRAME_IDS_HEADERS_ID: 844 mhd_assert ((0u == frame_info->headers.pad_length) || \ 845 frame_info->headers.padded); 846 return frame_info->headers.pad_length; 847 case mhd_H2_FRAME_IDS_PUSH_PROMISE_ID: 848 mhd_assert ((0u == frame_info->push_promise.pad_length) || \ 849 frame_info->push_promise.padded); 850 return frame_info->push_promise.pad_length; 851 case mhd_H2_FRAME_IDS_PRIORITY_ID: 852 case mhd_H2_FRAME_IDS_RST_STREAM_ID: 853 case mhd_H2_FRAME_IDS_SETTINGS_ID: 854 case mhd_H2_FRAME_IDS_PING_ID: 855 case mhd_H2_FRAME_IDS_GOAWAY_ID: 856 case mhd_H2_FRAME_IDS_WINDOW_UPDATE_ID: 857 case mhd_H2_FRAME_IDS_CONTINUATION_ID: 858 default: 859 break; 860 } 861 return 0u; 862 } 863 864 865 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 866 MHD_FN_PAR_INOUT_ (1) size_t 867 mhd_h2_frame_set_payload_size (union mhd_H2FrameUnion *restrict frame_info, 868 size_t payload_size) 869 { 870 uint_least32_t fr_length; 871 872 mhd_assert (frame_info->selector.length == \ 873 (frame_info->selector.length & mhd_H2_FR_LENGTH_MASK)); 874 mhd_assert (frame_info->selector.type == \ 875 (((uint_least64_t) frame_info->selector.type) & 0xFFu)); 876 #ifndef NDEBUG 877 switch (frame_info->selector.type) 878 { 879 case mhd_H2_FRAME_IDS_PRIORITY_ID: 880 case mhd_H2_FRAME_IDS_RST_STREAM_ID: 881 case mhd_H2_FRAME_IDS_PING_ID: 882 case mhd_H2_FRAME_IDS_WINDOW_UPDATE_ID: 883 mhd_assert (0u == payload_size); 884 break; 885 case mhd_H2_FRAME_IDS_DATA_ID: 886 case mhd_H2_FRAME_IDS_HEADERS_ID: 887 case mhd_H2_FRAME_IDS_SETTINGS_ID: 888 case mhd_H2_FRAME_IDS_PUSH_PROMISE_ID: 889 case mhd_H2_FRAME_IDS_GOAWAY_ID: 890 case mhd_H2_FRAME_IDS_CONTINUATION_ID: 891 default: 892 break; 893 } 894 #endif /* ! NDEBUG */ 895 mhd_assert (frame_info->selector.length == \ 896 (frame_info->selector.length & mhd_H2_FR_LENGTH_MASK)); 897 898 fr_length = (uint_least32_t) 899 (mhd_h2_frame_get_extra_hdr_size (frame_info) 900 + mhd_h2_frame_get_padding_size (frame_info) 901 + payload_size); 902 903 mhd_assert (fr_length == (fr_length & mhd_H2_FR_LENGTH_MASK)); 904 905 frame_info->selector.length = fr_length; 906 907 return (size_t) (mhd_H2_FR_HDR_BASE_SIZE + (size_t) fr_length); 908 } 909 910 911 /** 912 * Encode DATA extra header and flags. 913 * 914 * Does not write payload or trailing pad bytes. 915 * 916 * @param frame_info the DATA frame information 917 * @param[out] flags the pointer to the header flags byte to update 918 * @param out_extra_hdr_size available space in @a out_extra_hdr 919 * @param[out] out_extra_hdr the output buffer for extra header bytes 920 * @return the size of the frame basic header plus the size of the frame extra 921 * header written, 922 * or 0 if the output buffer is too small 923 */ 924 static MHD_FN_PAR_NONNULL_ALL_ 925 MHD_FN_PAR_IN_ (1) 926 MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_OUT_SIZE_ (4,3) size_t 927 frame_hdr_encode_data (const union mhd_H2FrameUnion *restrict frame_info, 928 uint8_t *restrict flags, 929 const size_t out_extra_hdr_size, 930 uint8_t *restrict out_extra_hdr) 931 { 932 const struct mhd_H2FrameDataInfo *const data = 933 &(frame_info->data); 934 size_t extra_hdr_pos; 935 936 mhd_assert (mhd_H2_FRAME_DATA_ID == data->type); 937 938 mhd_assert (0u != data->stream_id); 939 mhd_assert ((0u == data->pad_length) || 940 (data->padded)); 941 942 *flags = (uint8_t) (data->padded ? mhd_FFLAG_PADDED : 0u); 943 *flags |= (uint8_t) (data->end_stream ? mhd_FFLAG_END_STREAM : 0u); 944 945 extra_hdr_pos = 0u; 946 if (data->padded) 947 { 948 if (out_extra_hdr_size <= extra_hdr_pos) 949 return 0u; 950 out_extra_hdr[extra_hdr_pos++] = (uint8_t) data->pad_length; 951 } 952 953 mhd_assert (data->length >= (extra_hdr_pos + data->pad_length)); 954 955 return mhd_H2_FR_HDR_BASE_SIZE + extra_hdr_pos; 956 } 957 958 959 /** 960 * Encode HEADERS extra header and flags. 961 * 962 * @param frame_info the HEADERS frame information 963 * @param[out] flags the pointer to the header flags byte to update 964 * @param out_extra_hdr_size available space in @a out_extra_hdr 965 * @param[out] out_extra_hdr the output buffer for extra header bytes 966 * @return the size of the frame basic header plus the size of the frame extra 967 * header written, 968 * or 0 if the output buffer is too small 969 */ 970 static MHD_FN_PAR_NONNULL_ALL_ 971 MHD_FN_PAR_IN_ (1) 972 MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_OUT_SIZE_ (4,3) size_t 973 frame_hdr_encode_headers (const union mhd_H2FrameUnion *restrict frame_info, 974 uint8_t *restrict flags, 975 const size_t out_extra_hdr_size, 976 uint8_t *restrict out_extra_hdr) 977 { 978 const struct mhd_H2FrameHeadersInfo *const headers = 979 &(frame_info->headers); 980 size_t extra_hdr_pos; 981 982 mhd_assert (mhd_H2_FRAME_HEADERS_ID == headers->type); 983 984 mhd_assert (0u != headers->stream_id); 985 mhd_assert ((0u == headers->pad_length) || 986 (headers->padded)); 987 988 *flags = (uint8_t) (headers->priority ? mhd_FFLAG_PRIORITY : 0u); 989 *flags |= (uint8_t) (headers->padded ? mhd_FFLAG_PADDED : 0u); 990 *flags |= (uint8_t) (headers->end_headers ? mhd_FFLAG_END_HEADERS : 0u); 991 *flags |= (uint8_t) (headers->end_stream ? mhd_FFLAG_END_STREAM : 0u); 992 993 extra_hdr_pos = 0u; 994 if (headers->padded) 995 { 996 if (out_extra_hdr_size <= extra_hdr_pos) 997 return 0u; 998 out_extra_hdr[extra_hdr_pos++] = (uint8_t) headers->pad_length; 999 } 1000 1001 if (headers->priority) 1002 { 1003 uint32_t excl_n_strm_dep; 1004 if (out_extra_hdr_size < (extra_hdr_pos + 5u)) 1005 return 0u; 1006 1007 excl_n_strm_dep = (uint32_t) 1008 (headers->stream_dependency & mhd_H2_STREAM_ID_MASK); 1009 excl_n_strm_dep |= (uint32_t) 1010 (headers->exclusive ? mhd_FFLAG_HEADERS_EXCLUSIVE : 0u); 1011 mhd_PUT_32BIT_BE_UNALIGN (out_extra_hdr + extra_hdr_pos, 1012 excl_n_strm_dep); 1013 extra_hdr_pos += 4u; 1014 1015 /* Use "on-wire" 'weight' format. */ 1016 out_extra_hdr[extra_hdr_pos++] = (uint8_t) headers->weight; 1017 } 1018 1019 mhd_assert (headers->length >= (extra_hdr_pos + headers->pad_length)); 1020 1021 return mhd_H2_FR_HDR_BASE_SIZE + extra_hdr_pos; 1022 } 1023 1024 1025 /** 1026 * Encode PRIORITY payload into the extra header area. 1027 * 1028 * @param frame_info the PRIORITY frame information 1029 * @param out_extra_hdr_size available space in @a out_extra_hdr 1030 * @param[out] out_extra_hdr the output buffer for the 5-byte payload 1031 * @return the size of the frame basic header plus the size of the frame extra 1032 * header written, 1033 * or 0 if the output buffer is too small 1034 */ 1035 static MHD_FN_PAR_NONNULL_ALL_ 1036 MHD_FN_PAR_IN_ (1) 1037 MHD_FN_PAR_OUT_SIZE_ (3,2) size_t 1038 frame_hdr_encode_priority (const union mhd_H2FrameUnion *restrict frame_info, 1039 const size_t out_extra_hdr_size, 1040 uint8_t *restrict out_extra_hdr) 1041 { 1042 const struct mhd_H2FramePriorityInfo *const priority = 1043 &(frame_info->priority); 1044 uint32_t excl_n_strm_dep; 1045 size_t extra_hdr_pos; 1046 1047 mhd_assert (mhd_H2_FRAME_PRIORITY_ID == priority->type); 1048 1049 mhd_assert (0u != priority->stream_id); 1050 mhd_assert (priority->stream_id != priority->stream_dependency); 1051 1052 extra_hdr_pos = 0u; 1053 1054 if (out_extra_hdr_size < (extra_hdr_pos + 5u)) 1055 return 0u; 1056 1057 excl_n_strm_dep = (uint32_t) 1058 (priority->stream_dependency & mhd_H2_STREAM_ID_MASK); 1059 excl_n_strm_dep |= (uint32_t) 1060 (priority->exclusive ? mhd_FFLAG_HEADERS_EXCLUSIVE : 0u); 1061 mhd_PUT_32BIT_BE_UNALIGN (out_extra_hdr + extra_hdr_pos, 1062 excl_n_strm_dep); 1063 extra_hdr_pos += 4u; 1064 1065 /* Use "on-wire" 'weight' format. */ 1066 out_extra_hdr[extra_hdr_pos++] = (uint8_t) priority->weight; 1067 1068 mhd_assert (priority->length == extra_hdr_pos); 1069 mhd_assert (mhd_H2_FR_FIXED_LEN_PRIORITY == extra_hdr_pos); 1070 1071 return mhd_H2_FR_HDR_BASE_SIZE + extra_hdr_pos; 1072 } 1073 1074 1075 /** 1076 * Encode RST_STREAM payload into the extra header area. 1077 * 1078 * @param frame_info the RST_STREAM frame information 1079 * @param out_extra_hdr_size available space in @a out_extra_hdr 1080 * @param[out] out_extra_hdr the output buffer for the 4-byte payload 1081 * @return the size of the frame basic header plus the size of the frame extra 1082 * header written, 1083 * or 0 if the output buffer is too small 1084 */ 1085 static MHD_FN_PAR_NONNULL_ALL_ 1086 MHD_FN_PAR_IN_ (1) 1087 MHD_FN_PAR_OUT_SIZE_ (3,2) size_t 1088 frame_hdr_encode_rst_stream (const union mhd_H2FrameUnion *restrict frame_info, 1089 const size_t out_extra_hdr_size, 1090 uint8_t *restrict out_extra_hdr) 1091 { 1092 const struct mhd_H2FrameRstStreamInfo *const rst_stream = 1093 &(frame_info->rst_stream); 1094 size_t extra_hdr_pos; 1095 1096 mhd_assert (mhd_H2_FRAME_RST_STREAM_ID == rst_stream->type); 1097 1098 mhd_assert (0u != rst_stream->stream_id); 1099 1100 extra_hdr_pos = 0u; 1101 1102 if (out_extra_hdr_size < (extra_hdr_pos + 4u)) 1103 return 0u; 1104 1105 mhd_PUT_32BIT_BE_UNALIGN (out_extra_hdr + extra_hdr_pos, 1106 (uint32_t) rst_stream->error_code); 1107 extra_hdr_pos += 4u; 1108 1109 mhd_assert (rst_stream->length == extra_hdr_pos); 1110 mhd_assert (mhd_H2_FR_FIXED_LEN_RST_STREAM == extra_hdr_pos); 1111 1112 return mhd_H2_FR_HDR_BASE_SIZE + extra_hdr_pos; 1113 } 1114 1115 1116 /** 1117 * Encode SETTINGS header flags. 1118 * 1119 * SETTINGS has no extra header bytes. Any SETTINGS parameters belong to the 1120 * payload and are not written by this function. 1121 * 1122 * @param frame_info the SETTINGS frame information 1123 * @param[out] flags the pointer to the header flags byte to update 1124 * @return the size of the frame basic header plus the size of the frame extra 1125 * header written 1126 */ 1127 static MHD_FN_PAR_NONNULL_ALL_ 1128 MHD_FN_PAR_IN_ (1) 1129 MHD_FN_PAR_OUT_ (2) size_t 1130 frame_hdr_encode_settings (const union mhd_H2FrameUnion *restrict frame_info, 1131 uint8_t *restrict flags) 1132 { 1133 const struct mhd_H2FrameSettingsInfo *const settings = 1134 &(frame_info->settings); 1135 1136 mhd_assert (mhd_H2_FRAME_SETTINGS_ID == settings->type); 1137 1138 mhd_assert (0u == settings->stream_id); 1139 mhd_assert ((! settings->ack) || (0u == settings->length)); 1140 mhd_assert (0u == (settings->length % 6)); 1141 1142 *flags = (uint8_t) (settings->ack ? mhd_FFLAG_ACK : 0u); 1143 1144 return mhd_H2_FR_HDR_BASE_SIZE; 1145 } 1146 1147 1148 /** 1149 * Encode PUSH_PROMISE extra header and flags. 1150 * 1151 * This function does not write the header block fragment or trailing 1152 * pad bytes. 1153 * 1154 * @param frame_info the PUSH_PROMISE frame information 1155 * @param[out] flags the pointer to the header flags byte to update 1156 * @param out_extra_hdr_size available space in @a out_extra_hdr 1157 * @param[out] out_extra_hdr the output buffer for extra header bytes 1158 * @return the size of the frame basic header plus the size of the frame extra 1159 * header written, 1160 * or 0 if the output buffer is too small 1161 */ 1162 static MHD_FN_PAR_NONNULL_ALL_ 1163 MHD_FN_PAR_IN_ (1) 1164 MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_OUT_SIZE_ (4,3) size_t 1165 frame_hdr_encode_push_promise ( 1166 const union mhd_H2FrameUnion *restrict frame_info, 1167 uint8_t *restrict flags, 1168 const size_t out_extra_hdr_size, 1169 uint8_t *restrict out_extra_hdr) 1170 { 1171 const struct mhd_H2FramePushPromiseInfo *const push_promise = 1172 &(frame_info->push_promise); 1173 size_t extra_hdr_pos; 1174 1175 mhd_assert (mhd_H2_FRAME_PUSH_PROMISE_ID == push_promise->type); 1176 1177 mhd_assert (0u != push_promise->stream_id); 1178 mhd_assert ((0u == push_promise->pad_length) || 1179 (push_promise->padded)); 1180 mhd_assert (0u != push_promise->promised_stream_id); 1181 mhd_assert (push_promise->promised_stream_id == 1182 (push_promise->promised_stream_id & mhd_H2_STREAM_ID_MASK)); 1183 1184 *flags = (uint8_t) (push_promise->padded ? mhd_FFLAG_PADDED : 0u); 1185 *flags |= (uint8_t) (push_promise->end_headers ? mhd_FFLAG_END_HEADERS : 0u); 1186 1187 extra_hdr_pos = 0u; 1188 if (push_promise->padded) 1189 { 1190 if (out_extra_hdr_size <= extra_hdr_pos) 1191 return 0u; 1192 out_extra_hdr[extra_hdr_pos++] = (uint8_t) push_promise->pad_length; 1193 } 1194 1195 if (out_extra_hdr_size < (extra_hdr_pos + 4u)) 1196 return 0u; 1197 1198 mhd_PUT_32BIT_BE_UNALIGN (out_extra_hdr + extra_hdr_pos, 1199 (uint32_t) (push_promise->promised_stream_id 1200 & mhd_H2_STREAM_ID_MASK)); 1201 extra_hdr_pos += 4u; 1202 1203 mhd_assert (push_promise->length >= 1204 (extra_hdr_pos + push_promise->pad_length)); 1205 1206 return mhd_H2_FR_HDR_BASE_SIZE + extra_hdr_pos; 1207 } 1208 1209 1210 /** 1211 * Encode PING payload and flags. 1212 * 1213 * @param frame_info the PING frame information 1214 * @param[out] flags the pointer to the header flags byte to update 1215 * @param out_extra_hdr_size available space in @a out_extra_hdr 1216 * @param[out] out_extra_hdr the output buffer for the 8-byte payload 1217 * @return the size of the frame basic header plus the size of the frame extra 1218 * header written, 1219 * or 0 if the output buffer is too small 1220 */ 1221 static MHD_FN_PAR_NONNULL_ALL_ 1222 MHD_FN_PAR_IN_ (1) 1223 MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_OUT_SIZE_ (4,3) size_t 1224 frame_hdr_encode_ping (const union mhd_H2FrameUnion *restrict frame_info, 1225 uint8_t *restrict flags, 1226 const size_t out_extra_hdr_size, 1227 uint8_t *restrict out_extra_hdr) 1228 { 1229 const struct mhd_H2FramePingInfo *const ping = 1230 &(frame_info->ping); 1231 size_t extra_hdr_pos; 1232 1233 mhd_assert (mhd_H2_FRAME_PING_ID == ping->type); 1234 1235 mhd_assert (0u == ping->stream_id); 1236 1237 *flags = (uint8_t) (ping->ack ? mhd_FFLAG_ACK : 0u); 1238 1239 extra_hdr_pos = 0u; 1240 1241 if (out_extra_hdr_size < (extra_hdr_pos + 8u)) 1242 return 0u; 1243 1244 memcpy (out_extra_hdr, 1245 ping->opaque_data, 1246 8u); 1247 extra_hdr_pos += 8u; 1248 1249 mhd_assert (ping->length == extra_hdr_pos); 1250 mhd_assert (mhd_H2_FR_FIXED_LEN_PING == extra_hdr_pos); 1251 1252 return mhd_H2_FR_HDR_BASE_SIZE + extra_hdr_pos; 1253 } 1254 1255 1256 /** 1257 * Encode GOAWAY fixed fields into the extra header area. 1258 * 1259 * Optional debug data belongs to the payload and is not written by this 1260 * function. 1261 * 1262 * @param frame_info the GOAWAY frame information 1263 * @param out_extra_hdr_size available space in @a out_extra_hdr 1264 * @param[out] out_extra_hdr the output buffer for the 8-byte fixed fields 1265 * @return the size of the frame basic header plus the size of the frame extra 1266 * header written, 1267 * or 0 if the output buffer is too small 1268 */ 1269 static MHD_FN_PAR_NONNULL_ALL_ 1270 MHD_FN_PAR_IN_ (1) 1271 MHD_FN_PAR_OUT_SIZE_ (3,2) size_t 1272 frame_hdr_encode_goaway (const union mhd_H2FrameUnion *restrict frame_info, 1273 const size_t out_extra_hdr_size, 1274 uint8_t *restrict out_extra_hdr) 1275 { 1276 const struct mhd_H2FrameGoawayInfo *const goaway = 1277 &(frame_info->goaway); 1278 size_t extra_hdr_pos; 1279 1280 mhd_assert (mhd_H2_FRAME_GOAWAY_ID == goaway->type); 1281 1282 mhd_assert (0u == goaway->stream_id); 1283 1284 extra_hdr_pos = 0u; 1285 1286 if (out_extra_hdr_size < (extra_hdr_pos + 8u)) 1287 return 0u; 1288 1289 mhd_PUT_32BIT_BE_UNALIGN (out_extra_hdr + extra_hdr_pos, 1290 (uint32_t) (goaway->last_stream_id 1291 & mhd_H2_STREAM_ID_MASK)); 1292 extra_hdr_pos += 4u; 1293 1294 mhd_PUT_32BIT_BE_UNALIGN (out_extra_hdr + extra_hdr_pos, 1295 (uint32_t) goaway->error_code); 1296 extra_hdr_pos += 4u; 1297 1298 mhd_assert (goaway->length >= extra_hdr_pos); 1299 1300 return mhd_H2_FR_HDR_BASE_SIZE + extra_hdr_pos; 1301 } 1302 1303 1304 /** 1305 * Encode WINDOW_UPDATE payload into the extra header area. 1306 * 1307 * @param frame_info the WINDOW_UPDATE frame information 1308 * @param out_extra_hdr_size available space in @a out_extra_hdr 1309 * @param[out] out_extra_hdr the output buffer for the 4-byte payload 1310 * @return the size of the frame basic header plus the size of the frame extra 1311 * header written, 1312 * or 0 if the output buffer is too small 1313 */ 1314 static MHD_FN_PAR_NONNULL_ALL_ 1315 MHD_FN_PAR_IN_ (1) 1316 MHD_FN_PAR_OUT_SIZE_ (3,2) size_t 1317 frame_hdr_encode_window_update ( 1318 const union mhd_H2FrameUnion *restrict frame_info, 1319 const size_t out_extra_hdr_size, 1320 uint8_t *restrict out_extra_hdr) 1321 { 1322 const struct mhd_H2FrameWindowUpdateInfo *const window_update = 1323 &(frame_info->window_update); 1324 size_t extra_hdr_pos; 1325 1326 mhd_assert (mhd_H2_FRAME_WINDOW_UPDATE_ID == window_update->type); 1327 mhd_assert (0u != window_update->window_size_increment); 1328 mhd_assert (window_update->window_size_increment == 1329 (window_update->window_size_increment & mhd_MASK_31BITS)); 1330 1331 extra_hdr_pos = 0u; 1332 1333 if (out_extra_hdr_size < (extra_hdr_pos + 4u)) 1334 return 0u; 1335 1336 mhd_PUT_32BIT_BE_UNALIGN (out_extra_hdr + extra_hdr_pos, 1337 (uint32_t) (window_update->window_size_increment 1338 & mhd_MASK_31BITS)); 1339 extra_hdr_pos += 4u; 1340 1341 mhd_assert (window_update->length >= extra_hdr_pos); 1342 mhd_assert (mhd_H2_FR_FIXED_LEN_WINDOW_UPDATE == extra_hdr_pos); 1343 1344 return mhd_H2_FR_HDR_BASE_SIZE + extra_hdr_pos; 1345 } 1346 1347 1348 /** 1349 * Encode CONTINUATION header flags. 1350 * 1351 * CONTINUATION has no extra header bytes. 1352 * 1353 * @param frame_info the CONTINUATION frame information 1354 * @param[out] flags the pointer to the header flags byte to update 1355 * @return the size of the frame basic header plus the size of the frame extra 1356 * header written 1357 */ 1358 static MHD_FN_PAR_NONNULL_ALL_ 1359 MHD_FN_PAR_IN_ (1) 1360 MHD_FN_PAR_OUT_ (2) size_t 1361 frame_hdr_encode_continuation ( 1362 const union mhd_H2FrameUnion *restrict frame_info, 1363 uint8_t *restrict flags) 1364 { 1365 const struct mhd_H2FrameContinuationInfo *const continuation = 1366 &(frame_info->continuation); 1367 1368 mhd_assert (mhd_H2_FRAME_CONTINUATION_ID == continuation->type); 1369 1370 mhd_assert (0u != continuation->stream_id); 1371 1372 *flags = (uint8_t) (continuation->end_headers ? mhd_FFLAG_END_HEADERS : 0u); 1373 1374 return mhd_H2_FR_HDR_BASE_SIZE; 1375 } 1376 1377 1378 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 1379 MHD_FN_PAR_IN_ (1) 1380 MHD_FN_PAR_OUT_SIZE_ (3,2) size_t 1381 mhd_h2_frame_hdr_encode (const union mhd_H2FrameUnion *restrict frame_info, 1382 size_t out_buff_size, 1383 uint8_t *restrict out_buff) 1384 { 1385 uint32_t len_and_type; 1386 1387 mhd_assert (frame_info->selector.length == \ 1388 (frame_info->selector.length & mhd_H2_FR_LENGTH_MASK)); 1389 mhd_assert (frame_info->selector.type == \ 1390 (((uint_least64_t) frame_info->selector.type) & 0xFFu)); 1391 mhd_assert (frame_info->selector.stream_id == \ 1392 (frame_info->selector.stream_id & mhd_H2_STREAM_ID_MASK)); 1393 1394 if (mhd_H2_FR_SIZE_MIN > out_buff_size) 1395 return 0u; 1396 1397 len_and_type = (uint_least8_t) (frame_info->selector.type & 0xFFu); 1398 len_and_type |= 1399 (uint32_t) 1400 ((((uint_least32_t) frame_info->selector.length) << 8u) 1401 & mhd_H2_FR_LENGTH_MASK); 1402 1403 mhd_PUT_32BIT_BE_UNALIGN (out_buff + 0u, 1404 len_and_type); 1405 out_buff[4] = 0u; /* flags */ 1406 mhd_PUT_32BIT_BE_UNALIGN (out_buff + 5u, 1407 frame_info->selector.stream_id 1408 & mhd_H2_STREAM_ID_MASK); 1409 1410 switch (frame_info->selector.type) 1411 { 1412 case mhd_H2_FRAME_IDS_DATA_ID: 1413 return frame_hdr_encode_data (frame_info, 1414 out_buff + 4u, 1415 out_buff_size - mhd_H2_FR_HDR_BASE_SIZE, 1416 out_buff + mhd_H2_FR_HDR_BASE_SIZE); 1417 case mhd_H2_FRAME_IDS_HEADERS_ID: 1418 return frame_hdr_encode_headers (frame_info, 1419 out_buff + 4u, 1420 out_buff_size - mhd_H2_FR_HDR_BASE_SIZE, 1421 out_buff + mhd_H2_FR_HDR_BASE_SIZE); 1422 case mhd_H2_FRAME_IDS_PRIORITY_ID: 1423 return frame_hdr_encode_priority (frame_info, 1424 out_buff_size - mhd_H2_FR_HDR_BASE_SIZE, 1425 out_buff + mhd_H2_FR_HDR_BASE_SIZE); 1426 case mhd_H2_FRAME_IDS_RST_STREAM_ID: 1427 return frame_hdr_encode_rst_stream (frame_info, 1428 out_buff_size - mhd_H2_FR_HDR_BASE_SIZE, 1429 out_buff + mhd_H2_FR_HDR_BASE_SIZE); 1430 case mhd_H2_FRAME_IDS_SETTINGS_ID: 1431 return frame_hdr_encode_settings (frame_info, 1432 out_buff + 4u); 1433 case mhd_H2_FRAME_IDS_PUSH_PROMISE_ID: 1434 return 1435 frame_hdr_encode_push_promise (frame_info, 1436 out_buff + 4u, 1437 out_buff_size - mhd_H2_FR_HDR_BASE_SIZE, 1438 out_buff + mhd_H2_FR_HDR_BASE_SIZE); 1439 case mhd_H2_FRAME_IDS_PING_ID: 1440 return frame_hdr_encode_ping (frame_info, 1441 out_buff + 4u, 1442 out_buff_size - mhd_H2_FR_HDR_BASE_SIZE, 1443 out_buff + mhd_H2_FR_HDR_BASE_SIZE); 1444 case mhd_H2_FRAME_IDS_GOAWAY_ID: 1445 return frame_hdr_encode_goaway (frame_info, 1446 out_buff_size - mhd_H2_FR_HDR_BASE_SIZE, 1447 out_buff + mhd_H2_FR_HDR_BASE_SIZE); 1448 case mhd_H2_FRAME_IDS_WINDOW_UPDATE_ID: 1449 return 1450 frame_hdr_encode_window_update (frame_info, 1451 out_buff_size - mhd_H2_FR_HDR_BASE_SIZE, 1452 out_buff + mhd_H2_FR_HDR_BASE_SIZE); 1453 case mhd_H2_FRAME_IDS_CONTINUATION_ID: 1454 return 1455 frame_hdr_encode_continuation (frame_info, 1456 out_buff + 4u); 1457 default: 1458 break; 1459 } 1460 mhd_UNREACHABLE_D ("Unknown frame types should not be sent"); 1461 return mhd_H2_FR_HDR_BASE_SIZE; 1462 }