libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

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 }