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