mhd_parsing.c (18297B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014--2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file mhd_parsing.c 18 * @brief functions to parse incoming requests (MHD arguments and JSON snippets) 19 * @author Florian Dold 20 * @author Benedikt Mueller 21 * @author Christian Grothoff 22 */ 23 #include <gnunet/gnunet_util_lib.h> 24 #include <gnunet/gnunet_json_lib.h> 25 #include <gnunet/gnunet_mhd_lib.h> 26 #include "taler/taler_json_lib.h" 27 #include "taler/taler_mhd_lib.h" 28 29 30 /** 31 * Longest long-poll wait we grant a client, in milliseconds. While 32 * long-polling, the connection is suspended and thus not subject to 33 * MHD's connection timeout, so without a bound a client could pin the 34 * connection (with its file descriptor and database event handler) 35 * for the lifetime of the process. Clients simply poll again. 36 */ 37 #define MAX_TIMEOUT_MS (30LLU * 60 * 1000) 38 39 40 enum GNUNET_GenericReturnValue 41 TALER_MHD_parse_post_json (struct MHD_Connection *connection, 42 void **con_cls, 43 const char *upload_data, 44 size_t *upload_data_size, 45 json_t **json) 46 { 47 enum GNUNET_MHD_PostResult pr; 48 49 pr = GNUNET_MHD_post_parser (TALER_MHD_REQUEST_BUFFER_MAX, 50 connection, 51 con_cls, 52 upload_data, 53 upload_data_size, 54 json); 55 switch (pr) 56 { 57 case GNUNET_MHD_PR_OUT_OF_MEMORY: 58 GNUNET_break (NULL == *json); 59 return (MHD_NO == 60 TALER_MHD_reply_with_error ( 61 connection, 62 MHD_HTTP_INTERNAL_SERVER_ERROR, 63 TALER_EC_GENERIC_PARSER_OUT_OF_MEMORY, 64 NULL)) ? GNUNET_SYSERR : GNUNET_NO; 65 66 case GNUNET_MHD_PR_CONTINUE: 67 GNUNET_break (NULL == *json); 68 return GNUNET_YES; 69 case GNUNET_MHD_PR_REQUEST_TOO_LARGE: 70 GNUNET_break (NULL == *json); 71 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 72 "Closing connection, upload too large\n"); 73 return GNUNET_SYSERR; 74 case GNUNET_MHD_PR_JSON_INVALID: 75 GNUNET_break (NULL == *json); 76 return (MHD_YES == 77 TALER_MHD_reply_with_error (connection, 78 MHD_HTTP_BAD_REQUEST, 79 TALER_EC_GENERIC_JSON_INVALID, 80 NULL)) 81 ? GNUNET_NO : GNUNET_SYSERR; 82 case GNUNET_MHD_PR_SUCCESS: 83 GNUNET_break (NULL != *json); 84 return GNUNET_YES; 85 } 86 /* this should never happen */ 87 GNUNET_break (0); 88 return GNUNET_SYSERR; 89 } 90 91 92 void 93 TALER_MHD_parse_post_cleanup_callback (void *con_cls) 94 { 95 GNUNET_MHD_post_parser_cleanup (con_cls); 96 } 97 98 99 /** 100 * Extract fixed-size base32crockford encoded data from request. 101 * 102 * Queues an error response to the connection if the parameter is missing or 103 * invalid. 104 * 105 * @param connection the MHD connection 106 * @param param_name the name of the HTTP key with the value 107 * @param kind whether to extract from header, argument or footer 108 * @param[out] out_data pointer to store the result 109 * @param out_size expected size of @a out_data 110 * @param[out] present set to true if argument was found 111 * @return 112 * #GNUNET_YES if the the argument is present 113 * #GNUNET_NO if the argument is absent or malformed 114 * #GNUNET_SYSERR on internal error (error response could not be sent) 115 */ 116 static enum GNUNET_GenericReturnValue 117 parse_request_data ( 118 struct MHD_Connection *connection, 119 const char *param_name, 120 enum MHD_ValueKind kind, 121 void *out_data, 122 size_t out_size, 123 bool *present) 124 { 125 const char *str; 126 127 str = MHD_lookup_connection_value (connection, 128 kind, 129 param_name); 130 if (NULL == str) 131 { 132 *present = false; 133 return GNUNET_OK; 134 } 135 if (GNUNET_OK != 136 GNUNET_STRINGS_string_to_data (str, 137 strlen (str), 138 out_data, 139 out_size)) 140 { 141 GNUNET_break_op (0); 142 return (MHD_NO == 143 TALER_MHD_reply_with_error (connection, 144 MHD_HTTP_BAD_REQUEST, 145 TALER_EC_GENERIC_PARAMETER_MALFORMED, 146 param_name)) 147 ? GNUNET_SYSERR : GNUNET_NO; 148 } 149 *present = true; 150 return GNUNET_OK; 151 } 152 153 154 enum GNUNET_GenericReturnValue 155 TALER_MHD_parse_request_arg_data ( 156 struct MHD_Connection *connection, 157 const char *param_name, 158 void *out_data, 159 size_t out_size, 160 bool *present) 161 { 162 return parse_request_data (connection, 163 param_name, 164 MHD_GET_ARGUMENT_KIND, 165 out_data, 166 out_size, 167 present); 168 } 169 170 171 enum GNUNET_GenericReturnValue 172 TALER_MHD_parse_request_header_data ( 173 struct MHD_Connection *connection, 174 const char *header_name, 175 void *out_data, 176 size_t out_size, 177 bool *present) 178 { 179 return parse_request_data (connection, 180 header_name, 181 MHD_HEADER_KIND, 182 out_data, 183 out_size, 184 present); 185 } 186 187 188 enum GNUNET_GenericReturnValue 189 TALER_MHD_parse_request_arg_rel_time ( 190 struct MHD_Connection *connection, 191 const char *label, 192 struct GNUNET_TIME_Relative *duration) 193 { 194 const char *ts; 195 char dummy; 196 unsigned long long tms; 197 198 ts = MHD_lookup_connection_value (connection, 199 MHD_GET_ARGUMENT_KIND, 200 label); 201 if (NULL == ts) 202 { 203 *duration = GNUNET_TIME_UNIT_ZERO; 204 return GNUNET_OK; 205 } 206 if (1 != 207 sscanf (ts, 208 "%llu%c", 209 &tms, 210 &dummy)) 211 { 212 enum MHD_Result mret; 213 214 GNUNET_break_op (0); 215 mret = TALER_MHD_reply_with_error (connection, 216 MHD_HTTP_BAD_REQUEST, 217 TALER_EC_GENERIC_PARAMETER_MALFORMED, 218 label); 219 return (MHD_YES == mret) 220 ? GNUNET_NO 221 : GNUNET_SYSERR; 222 } 223 *duration = GNUNET_TIME_relative_saturating_multiply ( 224 GNUNET_TIME_UNIT_MILLISECONDS, 225 tms); 226 return GNUNET_OK; 227 } 228 229 230 enum GNUNET_GenericReturnValue 231 TALER_MHD_parse_request_arg_timeout ( 232 struct MHD_Connection *connection, 233 struct GNUNET_TIME_Absolute *expiration) 234 { 235 const char *ts; 236 char dummy; 237 unsigned long long tms; 238 239 ts = MHD_lookup_connection_value (connection, 240 MHD_GET_ARGUMENT_KIND, 241 "timeout_ms"); 242 if (NULL == ts) 243 { 244 *expiration = GNUNET_TIME_UNIT_ZERO_ABS; 245 return GNUNET_OK; 246 } 247 if (1 != 248 sscanf (ts, 249 "%llu%c", 250 &tms, 251 &dummy)) 252 { 253 enum MHD_Result mret; 254 255 GNUNET_break_op (0); 256 mret = TALER_MHD_reply_with_error (connection, 257 MHD_HTTP_BAD_REQUEST, 258 TALER_EC_GENERIC_PARAMETER_MALFORMED, 259 "timeout_ms"); 260 return (MHD_YES == mret) 261 ? GNUNET_NO 262 : GNUNET_SYSERR; 263 } 264 if (tms > MAX_TIMEOUT_MS) 265 tms = MAX_TIMEOUT_MS; 266 *expiration = GNUNET_TIME_relative_to_absolute ( 267 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 268 tms)); 269 return GNUNET_OK; 270 } 271 272 273 enum GNUNET_GenericReturnValue 274 TALER_MHD_parse_request_arg_timestamp ( 275 struct MHD_Connection *connection, 276 const char *fname, 277 struct GNUNET_TIME_Timestamp *ts) 278 { 279 const char *s; 280 281 s = MHD_lookup_connection_value (connection, 282 MHD_GET_ARGUMENT_KIND, 283 fname); 284 if (NULL == s) 285 return GNUNET_OK; 286 if (GNUNET_OK != 287 GNUNET_STRINGS_fancy_time_to_timestamp (s, 288 ts)) 289 { 290 enum MHD_Result mret; 291 292 GNUNET_break_op (0); 293 mret = TALER_MHD_reply_with_error ( 294 connection, 295 MHD_HTTP_BAD_REQUEST, 296 TALER_EC_GENERIC_PARAMETER_MALFORMED, 297 fname); 298 return (MHD_YES == mret) 299 ? GNUNET_NO 300 : GNUNET_SYSERR; 301 } 302 return GNUNET_OK; 303 } 304 305 306 enum GNUNET_GenericReturnValue 307 TALER_MHD_parse_request_arg_number (struct MHD_Connection *connection, 308 const char *name, 309 uint64_t *off) 310 { 311 const char *ts; 312 char dummy; 313 unsigned long long num; 314 315 ts = MHD_lookup_connection_value (connection, 316 MHD_GET_ARGUMENT_KIND, 317 name); 318 if (NULL == ts) 319 return GNUNET_OK; 320 if (1 != 321 sscanf (ts, 322 "%llu%c", 323 &num, 324 &dummy)) 325 { 326 enum MHD_Result mret; 327 328 GNUNET_break_op (0); 329 mret = TALER_MHD_reply_with_error (connection, 330 MHD_HTTP_BAD_REQUEST, 331 TALER_EC_GENERIC_PARAMETER_MALFORMED, 332 name); 333 return (MHD_YES == mret) 334 ? GNUNET_NO 335 : GNUNET_SYSERR; 336 } 337 *off = (uint64_t) num; 338 return GNUNET_OK; 339 } 340 341 342 enum GNUNET_GenericReturnValue 343 TALER_MHD_parse_request_arg_snumber (struct MHD_Connection *connection, 344 const char *name, 345 int64_t *val) 346 { 347 const char *ts; 348 char dummy; 349 long long num; 350 351 ts = MHD_lookup_connection_value (connection, 352 MHD_GET_ARGUMENT_KIND, 353 name); 354 if (NULL == ts) 355 return GNUNET_OK; 356 if (1 != 357 sscanf (ts, 358 "%lld%c", 359 &num, 360 &dummy)) 361 { 362 enum MHD_Result mret; 363 364 GNUNET_break_op (0); 365 mret = TALER_MHD_reply_with_error (connection, 366 MHD_HTTP_BAD_REQUEST, 367 TALER_EC_GENERIC_PARAMETER_MALFORMED, 368 name); 369 return (MHD_YES == mret) 370 ? GNUNET_NO 371 : GNUNET_SYSERR; 372 } 373 *val = (int64_t) num; 374 return GNUNET_OK; 375 } 376 377 378 enum GNUNET_GenericReturnValue 379 TALER_MHD_parse_request_arg_amount (struct MHD_Connection *connection, 380 const char *name, 381 struct TALER_Amount *val) 382 { 383 const char *ts; 384 385 ts = MHD_lookup_connection_value (connection, 386 MHD_GET_ARGUMENT_KIND, 387 name); 388 if (NULL == ts) 389 return GNUNET_OK; 390 if (GNUNET_OK != 391 TALER_string_to_amount (ts, 392 val)) 393 { 394 enum MHD_Result mret; 395 396 GNUNET_break_op (0); 397 mret = TALER_MHD_reply_with_error (connection, 398 MHD_HTTP_BAD_REQUEST, 399 TALER_EC_GENERIC_PARAMETER_MALFORMED, 400 name); 401 return (MHD_YES == mret) 402 ? GNUNET_NO 403 : GNUNET_SYSERR; 404 } 405 return GNUNET_OK; 406 } 407 408 409 enum GNUNET_GenericReturnValue 410 TALER_MHD_parse_json_data (struct MHD_Connection *connection, 411 const json_t *root, 412 struct GNUNET_JSON_Specification *spec) 413 { 414 enum GNUNET_GenericReturnValue ret; 415 const char *error_json_name; 416 unsigned int error_line; 417 418 ret = GNUNET_JSON_parse (root, 419 spec, 420 &error_json_name, 421 &error_line); 422 if (GNUNET_SYSERR == ret) 423 { 424 if (NULL == error_json_name) 425 error_json_name = "<no field>"; 426 ret = (MHD_YES == 427 TALER_MHD_REPLY_JSON_PACK ( 428 connection, 429 MHD_HTTP_BAD_REQUEST, 430 GNUNET_JSON_pack_string ("hint", 431 TALER_ErrorCode_get_hint ( 432 TALER_EC_GENERIC_JSON_INVALID)), 433 GNUNET_JSON_pack_uint64 ("code", 434 TALER_EC_GENERIC_JSON_INVALID), 435 GNUNET_JSON_pack_string ("field", 436 error_json_name), 437 GNUNET_JSON_pack_uint64 ("line", 438 error_line))) 439 ? GNUNET_NO : GNUNET_SYSERR; 440 return ret; 441 } 442 return GNUNET_YES; 443 } 444 445 446 enum GNUNET_GenericReturnValue 447 TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection, 448 const json_t *root, 449 struct GNUNET_JSON_Specification *spec) 450 { 451 enum GNUNET_GenericReturnValue ret; 452 const char *error_json_name; 453 unsigned int error_line; 454 455 ret = GNUNET_JSON_parse (root, 456 spec, 457 &error_json_name, 458 &error_line); 459 if (GNUNET_SYSERR == ret) 460 { 461 if (NULL == error_json_name) 462 error_json_name = "<no field>"; 463 ret = (MHD_YES == 464 TALER_MHD_REPLY_JSON_PACK ( 465 connection, 466 MHD_HTTP_INTERNAL_SERVER_ERROR, 467 GNUNET_JSON_pack_string ("hint", 468 TALER_ErrorCode_get_hint ( 469 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)), 470 GNUNET_JSON_pack_uint64 ("code", 471 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE), 472 GNUNET_JSON_pack_string ("field", 473 error_json_name), 474 GNUNET_JSON_pack_uint64 ("line", 475 error_line))) 476 ? GNUNET_NO : GNUNET_SYSERR; 477 return ret; 478 } 479 return GNUNET_YES; 480 } 481 482 483 enum GNUNET_GenericReturnValue 484 TALER_MHD_parse_json_array (struct MHD_Connection *connection, 485 const json_t *root, 486 struct GNUNET_JSON_Specification *spec, 487 ...) 488 { 489 enum GNUNET_GenericReturnValue ret; 490 const char *error_json_name; 491 unsigned int error_line; 492 va_list ap; 493 json_int_t dim; 494 495 va_start (ap, spec); 496 dim = 0; 497 while ( (-1 != (ret = va_arg (ap, int))) && 498 (NULL != root) ) 499 { 500 dim++; 501 root = json_array_get (root, ret); 502 } 503 va_end (ap); 504 if (NULL == root) 505 { 506 ret = (MHD_YES == 507 TALER_MHD_REPLY_JSON_PACK ( 508 connection, 509 MHD_HTTP_BAD_REQUEST, 510 GNUNET_JSON_pack_string ("hint", 511 TALER_ErrorCode_get_hint ( 512 TALER_EC_GENERIC_JSON_INVALID)), 513 GNUNET_JSON_pack_uint64 ("code", 514 TALER_EC_GENERIC_JSON_INVALID), 515 GNUNET_JSON_pack_string ("detail", 516 "expected array"), 517 GNUNET_JSON_pack_uint64 ("dimension", 518 dim))) 519 ? GNUNET_NO : GNUNET_SYSERR; 520 return ret; 521 } 522 ret = GNUNET_JSON_parse (root, 523 spec, 524 &error_json_name, 525 &error_line); 526 if (GNUNET_SYSERR == ret) 527 { 528 if (NULL == error_json_name) 529 error_json_name = "<no field>"; 530 ret = (MHD_YES == 531 TALER_MHD_REPLY_JSON_PACK ( 532 connection, 533 MHD_HTTP_BAD_REQUEST, 534 GNUNET_JSON_pack_string ("detail", 535 error_json_name), 536 GNUNET_JSON_pack_string ("hint", 537 TALER_ErrorCode_get_hint ( 538 TALER_EC_GENERIC_JSON_INVALID)), 539 GNUNET_JSON_pack_uint64 ("code", 540 TALER_EC_GENERIC_JSON_INVALID), 541 GNUNET_JSON_pack_uint64 ("line", 542 error_line))) 543 ? GNUNET_NO : GNUNET_SYSERR; 544 return ret; 545 } 546 return GNUNET_YES; 547 } 548 549 550 enum GNUNET_GenericReturnValue 551 TALER_MHD_check_content_length_ (struct MHD_Connection *connection, 552 unsigned long long max_len) 553 { 554 const char *cl; 555 unsigned long long cv; 556 char dummy; 557 558 /* Maybe check for maximum upload size 559 and refuse requests if they are just too big. */ 560 cl = MHD_lookup_connection_value (connection, 561 MHD_HEADER_KIND, 562 MHD_HTTP_HEADER_CONTENT_LENGTH); 563 if (NULL == cl) 564 { 565 return GNUNET_OK; 566 #if 0 567 /* wallet currently doesn't always send content-length! */ 568 GNUNET_break_op (0); 569 return (MHD_YES == 570 TALER_MHD_reply_with_error (connection, 571 MHD_HTTP_BAD_REQUEST, 572 TALER_EC_GENERIC_PARAMETER_MISSING, 573 MHD_HTTP_HEADER_CONTENT_LENGTH)) 574 ? GNUNET_NO 575 : GNUNET_SYSERR; 576 #endif 577 } 578 if (1 != sscanf (cl, 579 "%llu%c", 580 &cv, 581 &dummy)) 582 { 583 /* Not valid HTTP request, just close connection. */ 584 GNUNET_break_op (0); 585 return (MHD_YES == 586 TALER_MHD_reply_with_error (connection, 587 MHD_HTTP_BAD_REQUEST, 588 TALER_EC_GENERIC_PARAMETER_MALFORMED, 589 MHD_HTTP_HEADER_CONTENT_LENGTH)) 590 ? GNUNET_NO 591 : GNUNET_SYSERR; 592 } 593 if (cv > max_len) 594 { 595 GNUNET_break_op (0); 596 return (MHD_YES == 597 TALER_MHD_reply_request_too_large (connection)) 598 ? GNUNET_NO 599 : GNUNET_SYSERR; 600 } 601 return GNUNET_OK; 602 } 603 604 605 /* end of mhd_parsing.c */