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