h2_req_fields.c (17103B)
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_dec_fields.c 41 * @brief Implementation of HTTP/2 request fields functions 42 * @author Karlson2k (Evgeny Grin) 43 */ 44 45 #include "mhd_sys_options.h" 46 47 #include "sys_bool_type.h" 48 #include "sys_base_types.h" 49 50 #include <string.h> 51 52 #include "mhd_assert.h" 53 #include "mhd_unreachable.h" 54 #include "mhd_assume.h" 55 56 #include "mhd_constexpr.h" 57 #include "mhd_buffer.h" 58 #include "mhd_str_types.h" 59 #include "mhd_str_macros.h" 60 61 #include "mhd_connection.h" 62 #include "h2_conn_data.h" 63 64 #include "h2_stream_data.h" 65 66 #include "mhd_str.h" 67 68 #include "stream_process_request.h" 69 70 #include "h2_req_item_kinds.h" 71 #include "h2_req_item_struct.h" 72 #include "h2_req_items_funcs.h" 73 74 #include "hpack/mhd_hpack_codec.h" 75 76 #include "h2_proc_conn.h" 77 #include "h2_conn_streams.h" 78 79 #include "h2_req_fields.h" 80 81 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 82 MHD_FN_PAR_INOUT_ (1) MHD_FN_PAR_IN_ (2) 83 MHD_FN_PAR_INOUT_ (4) MHD_FN_PAR_OUT_ (5) enum mhd_H2DecFieldsResult 84 mhd_h2_req_fields_decode (struct mhd_HpackDecContext *restrict hk_dec, 85 const struct mhd_Buffer *restrict enc_data, 86 bool are_trailers, 87 struct mhd_H2ReqItemsBlock *restrict ib, 88 size_t *restrict left_unprocessed) 89 { 90 mhd_constexpr unsigned int max_no_fields = 8u; 91 const enum mhd_H2RequestItemKind field_kind = 92 are_trailers ? mhd_H2_RIK_TRAILER : mhd_H2_RIK_HEADER; 93 size_t pos; 94 unsigned int no_fields; 95 enum mhd_H2DecFieldsResult ret; 96 97 pos = 0u; 98 no_fields = 0u; 99 ret = mhd_H2_DEC_FIELDS_OK; 100 101 while (pos < enc_data->size) 102 { 103 size_t name_len; 104 size_t val_len; 105 size_t pos_incr; 106 enum mhd_HpackDecResult res; 107 struct mhd_Buffer buff; 108 109 mhd_assert (mhd_H2_DEC_FIELDS_OK == ret); 110 111 if (! mhd_h2_items_get_buff_new_item (ib, 112 &buff)) 113 return mhd_H2_DEC_FIELDS_NO_SPACE; 114 115 res = mhd_hpack_dec_data (hk_dec, 116 enc_data->size - pos, 117 (const uint8_t *) enc_data->data + pos, 118 buff.size, 119 buff.data, 120 &name_len, 121 &val_len, 122 &pos_incr); 123 124 if (! mhd_HPACK_DEC_RES_IS_ERR (res)) 125 { 126 mhd_assert (0u != pos_incr); 127 pos += pos_incr; 128 } 129 130 switch (res) 131 { 132 case mhd_HPACK_DEC_RES_NO_NEW_FIELD: 133 if (max_no_fields < ++no_fields) 134 { 135 ret = mhd_H2_DEC_FIELDS_PROT_ABUSE; 136 break; 137 } 138 mhd_h2_items_cancel_new_item_buff (ib); 139 continue; 140 case mhd_HPACK_DEC_RES_NEW_FIELD: 141 mhd_assert (buff.size >= (name_len + val_len + 2u)); 142 mhd_assert (0 == buff.data[name_len]); 143 mhd_assert (0 == buff.data[name_len + 1 + val_len]); 144 mhd_h2_items_add_new_item_buff (ib, 145 name_len, 146 val_len, 147 (':' == buff.data[0]) ? 148 mhd_H2_RIK_PSEUDOHEADER : field_kind); 149 continue; 150 case mhd_HPACK_DEC_RES_INCOMPLETE: 151 mhd_assert (mhd_H2_DEC_FIELDS_OK == ret); 152 break; 153 case mhd_HPACK_DEC_RES_ALLOC_ERR: 154 ret = mhd_H2_DEC_FIELDS_INT_ERR; 155 break; 156 case mhd_HPACK_DEC_RES_BUFFER_TOO_SMALL: // TODO: support "minimal" decoding for decoder state updates 157 case mhd_HPACK_DEC_RES_STRING_TOO_LONG: 158 ret = mhd_H2_DEC_FIELDS_NO_SPACE; 159 break; 160 case mhd_HPACK_DEC_RES_NUMBER_TOO_LONG: 161 case mhd_HPACK_DEC_RES_DYN_SIZE_UPD_TOO_LARGE: 162 ret = mhd_H2_DEC_FIELDS_PROT_ABUSE; 163 break; 164 case mhd_HPACK_DEC_RES_DYN_SIZE_UPD_MISSING: 165 case mhd_HPACK_DEC_RES_HUFFMAN_ERR: 166 case mhd_HPACK_DEC_RES_HPACK_BAD_IDX: 167 case mhd_HPACK_DEC_RES_HPACK_ERR: 168 ret = mhd_H2_DEC_FIELDS_BROKEN_DATA; 169 break; 170 case mhd_HPACK_DEC_RES_INTERNAL_ERR: 171 default: 172 mhd_UNREACHABLE_D ("Impossible value"); 173 ret = mhd_H2_DEC_FIELDS_INT_ERR; 174 break; 175 } 176 mhd_h2_items_cancel_new_item_buff (ib); 177 break; /* Break the loop */ 178 } 179 180 mhd_assert (pos <= enc_data->size); 181 *left_unprocessed = enc_data->size - pos; 182 183 return ret; 184 } 185 186 187 static inline MHD_FN_PAR_NONNULL_ALL_ 188 MHD_FN_PAR_INOUT_ (1) bool 189 req_validate_fields_chars (struct mhd_H2Stream *restrict s) 190 { 191 // TODO: implement checking all field chars for validity, RFC 9113 Section 8.2.1 192 (void) s; 193 return true; 194 } 195 196 197 static inline MHD_FN_PAR_NONNULL_ALL_ 198 MHD_FN_PAR_INOUT_ (1) 199 MHD_FN_PAR_OUT_ (2) bool 200 req_pseudoheaders_preprocess (struct mhd_H2Stream *restrict s, 201 size_t *pos) 202 { 203 static const struct MHD_String ph_method = mhd_MSTR_INIT (":method"); 204 static const struct MHD_String ph_scheme = mhd_MSTR_INIT (":scheme"); 205 static const struct MHD_String ph_authority = mhd_MSTR_INIT (":authority"); 206 static const struct MHD_String ph_path = mhd_MSTR_INIT (":path"); 207 struct mhd_H2ReqItemsBlock *const ib = s->c->h2.mem.req_ib; 208 const char *buff = mhd_h2_items_get_strings_buffc (ib); 209 bool have_method = false; 210 bool have_scheme = false; 211 bool have_authority = false; 212 bool have_path = false; 213 214 *pos = 0u; 215 while (1) 216 { 217 const struct mhd_H2ReqItem *item; 218 item = mhd_h2_items_get_item_nc (ib, 219 *pos); 220 221 if (NULL == item) 222 break; 223 else if (mhd_H2_RIK_PSEUDOHEADER != item->kind) 224 break; 225 else if ((item->name_len == ph_method.len) && 226 (0 == memcmp (buff + item->offset, 227 ph_method.cstr, 228 ph_method.len))) 229 { 230 if (have_method) 231 return mhd_h2_stream_req_problem (s, 232 mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP); 233 have_method = true; 234 s->req.pos_method = *pos; 235 s->req.method = 236 mhd_parse_http_method (item->val_len, 237 buff + item->offset + ph_method.len + 1u); 238 } 239 else if ((item->name_len == ph_path.len) && 240 (0 == memcmp (buff + item->offset, 241 ph_path.cstr, 242 ph_path.len))) 243 { 244 if (have_path) 245 return mhd_h2_stream_req_problem (s, 246 mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP); 247 have_path = true; 248 s->req.pos_path = *pos; 249 } 250 else if ((item->name_len == ph_authority.len) && 251 (0 == memcmp (buff + item->offset, 252 ph_authority.cstr, 253 ph_authority.len))) 254 { 255 if (have_authority) 256 return mhd_h2_stream_req_problem (s, 257 mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP); 258 have_authority = true; 259 s->req.pos_authority = *pos; 260 } 261 else if ((item->name_len == ph_scheme.len) && 262 (0 == memcmp (buff + item->offset, 263 ph_scheme.cstr, 264 ph_scheme.len))) 265 { 266 if (have_scheme) 267 return mhd_h2_stream_req_problem (s, 268 mhd_H2_REQ_PRBLM_PSEUDOHDR_DUP); 269 have_scheme = true; 270 } 271 mhd_assert (':' == buff[item->offset]); 272 ++(*pos); 273 } 274 275 if (! have_method) 276 return mhd_h2_stream_req_problem (s, 277 mhd_H2_REQ_PRBLM_PSEUDOHDR_MISSING); 278 279 if (mhd_HTTP_METHOD_CONNECT != s->req.method) 280 { 281 if (! have_path || ! have_scheme) 282 return mhd_h2_stream_req_problem (s, 283 mhd_H2_REQ_PRBLM_PSEUDOHDR_EXTRA); 284 } 285 else 286 { 287 if (have_path || have_scheme) 288 return mhd_h2_stream_req_problem (s, 289 mhd_H2_REQ_PRBLM_PSEUDOHDR_MISSING); 290 } 291 292 return true; 293 } 294 295 296 static inline MHD_FN_PAR_NONNULL_ALL_ 297 MHD_FN_PAR_INOUT_ (1) 298 MHD_FN_PAR_OUT_ (2) bool 299 req_headers_preprocess (struct mhd_H2Stream *restrict s, 300 size_t *pos) 301 { 302 static const struct MHD_String h_host = mhd_MSTR_INIT ("host"); 303 static const struct MHD_String h_cntn_len = mhd_MSTR_INIT ("content-length"); 304 struct mhd_H2ReqItemsBlock *const ib = s->c->h2.mem.req_ib; 305 const char *buff = mhd_h2_items_get_strings_buffc (ib); 306 307 while (1) 308 { 309 const struct mhd_H2ReqItem *item; 310 item = mhd_h2_items_get_item_nc (ib, 311 *pos); 312 313 if (NULL == item) 314 break; 315 else if (mhd_H2_RIK_PSEUDOHEADER == item->kind) 316 return mhd_h2_stream_req_problem (s, 317 mhd_H2_REQ_PRBLM_PSEUDOHDR_AFTER_HDR); 318 else if (mhd_H2_RIK_HEADER != item->kind) 319 (void) 0; /* skip */ 320 else if ((item->name_len == h_cntn_len.len) && 321 (0 == memcmp (buff + item->offset, 322 h_cntn_len.cstr, 323 h_cntn_len.len))) 324 { 325 uint_fast64_t cntnt_len; 326 327 if ((0u == item->val_len) || 328 (item->val_len != 329 mhd_str_to_uint64 (buff + item->offset + h_cntn_len.len + 1u, 330 &cntnt_len))) 331 return mhd_h2_stream_req_problem (s, 332 mhd_H2_REQ_PRBLM_CNTNT_LEN_WRONG); 333 334 if ((MHD_SIZE_UNKNOWN != s->req.cntn_size) && 335 (s->req.cntn_size != cntnt_len)) 336 return mhd_h2_stream_req_problem (s, 337 mhd_H2_REQ_PRBLM_CNTNT_LEN_WRONG); 338 else 339 s->req.cntn_size = cntnt_len; 340 } 341 else if ((item->name_len == h_host.len) && 342 (0 == memcmp (buff + item->offset, 343 h_host.cstr, 344 h_host.len))) 345 { 346 if (mhd_H2_REQ_ITEM_POS_INVALID == s->req.pos_authority) 347 s->req.pos_authority = *pos; 348 else 349 { 350 const struct mhd_H2ReqItem *item_auth = 351 mhd_h2_items_get_item_nc (ib, 352 s->req.pos_authority); 353 mhd_assert (NULL != item_auth); 354 if ((item_auth->val_len != item->val_len) || 355 (! mhd_str_equal_caseless_bin_n (buff + item->offset 356 + item->name_len + 1u, 357 buff + item_auth->offset 358 + item_auth->name_len + 1u, 359 item->val_len))) 360 return 361 mhd_h2_stream_req_problem (s, 362 mhd_H2_REQ_PRBLM_HOST_HDR_WRONG_EXTRA); 363 } 364 } 365 366 ++(*pos); 367 mhd_assert ((mhd_H2_RIK_HEADER != item->kind) || 368 (':' != buff[item->offset])); 369 } 370 371 return true; 372 } 373 374 375 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 376 MHD_FN_PAR_INOUT_ (1) bool 377 mhd_h2_req_headers_preprocess (struct mhd_H2Stream *restrict s) 378 { 379 size_t pos; 380 381 mhd_assert (mhd_h2_items_debug_get_streamid (s->c->h2.mem.req_ib) 382 == s->stream_id); 383 384 if (! req_validate_fields_chars (s)) 385 return false; 386 387 if (! req_pseudoheaders_preprocess (s, 388 &pos)) 389 return false; 390 391 mhd_assert (0u != pos); 392 mhd_assert (mhd_HTTP_METHOD_NO_METHOD != s->req.method); 393 394 if (! req_headers_preprocess (s, 395 &pos)) 396 return false; 397 398 return true; 399 } 400 401 402 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 403 MHD_FN_PAR_INOUT_ (1) bool 404 mhd_h2_req_uri_parse (struct mhd_H2Stream *restrict s) 405 { 406 struct mhd_H2ReqItemsBlock *const restrict ib = s->c->h2.mem.req_ib; 407 char *const restrict buff = mhd_h2_items_get_strings_buff (ib); 408 struct mhd_H2ReqItem *restrict item = 409 mhd_h2_items_get_item_n (ib, 410 s->req.pos_path); 411 const size_t path_start = (size_t) (item->offset + item->name_len + 1u); 412 char *questn_mark; 413 414 questn_mark = (char*) memchr (buff + path_start, 415 '?', 416 (size_t) item->val_len); 417 if (NULL == questn_mark) 418 { 419 item->val_len = 420 (uint_least32_t) 421 mhd_str_dec_norm_uri_path ((size_t) item->val_len, 422 buff + path_start); 423 } 424 else 425 { 426 const size_t path_len = (size_t) (questn_mark - (buff + path_start)); 427 const size_t uri_end = (size_t) (path_start + item->val_len); 428 size_t i = path_start + path_len + 1u; 429 430 mhd_assert (path_len < item->val_len); 431 432 buff[path_start + path_len] = '\0'; /* Zero-terminate the path */ 433 item->val_len = 434 (uint_least32_t) 435 mhd_str_dec_norm_uri_path (path_len, 436 buff + path_start); 437 438 do 439 { 440 size_t name_start; 441 size_t name_len; 442 size_t value_start; 443 size_t value_len; 444 445 value_start = 0u; 446 for (name_start = i; i < uri_end; ++i) /* Processing parameter */ 447 { 448 if ('+' == buff[i]) 449 buff[i] = ' '; 450 else if ('=' == buff[i]) 451 { 452 /* Found start of the value */ 453 for (value_start = ++i; i < uri_end; ++i) /* Processing parameter value */ 454 { 455 if ('+' == buff[i]) 456 buff[i] = ' '; 457 else if ('&' == buff[i]) /* delimiter for the next parameter */ 458 break; /* Next parameter */ 459 } 460 break; /* End of the current parameter */ 461 } 462 else if ('&' == buff[i]) 463 break; /* End of the name of the parameter without a value */ 464 } 465 466 /* PCT-decode, zero-terminate and store the found parameter */ 467 468 if (0u != value_start) /* Value cannot start at zero position */ 469 { /* Name with value */ 470 mhd_assert (name_start + 1u <= value_start); 471 name_len = value_start - name_start - 1u; 472 value_len = i - value_start; 473 } 474 else 475 { /* Name without value */ 476 name_len = i - name_start; 477 value_len = 0u; 478 } 479 name_len = mhd_str_pct_decode_lenient_n (buff + name_start, 480 name_len, 481 buff + name_start, 482 name_len, 483 NULL); // TODO: add support for broken encoding detection 484 buff[name_start + name_len] = 0; 485 486 if (0u != value_start) 487 { 488 value_len = 489 mhd_str_pct_decode_lenient_n (buff + name_start + name_len + 1u, 490 value_len, 491 buff + value_start, 492 value_len, 493 NULL); // TODO: add support for broken encoding detection 494 buff[value_start + value_len] = 0; 495 } 496 497 if (! mhd_h2_items_reserve_new_item (ib)) 498 break; // TODO: support reporting no space errors 499 500 mhd_h2_items_add_new_item_reserved (ib, 501 name_start, 502 name_len, 503 value_len, 504 (0u != value_start) 505 ? mhd_H2_RIK_URI_PARAM : 506 mhd_H2_RIK_URI_PARAM_NV); 507 508 } while (uri_end > ++i); 509 510 } 511 512 return true; 513 } 514 515 516 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 517 MHD_FN_PAR_INOUT_ (1) bool 518 mhd_h2_req_cookie_parse (struct mhd_H2Stream *restrict s) 519 { 520 // TODO: handle cookie combining 521 // TODO: Implement cookie parsing 522 return true; 523 }