Line data Source code
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 10996 : 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 10996 : 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 10996 : switch (pr)
47 : {
48 0 : case GNUNET_MHD_PR_OUT_OF_MEMORY:
49 0 : GNUNET_break (NULL == *json);
50 : return (MHD_NO ==
51 0 : TALER_MHD_reply_with_error (
52 : connection,
53 : MHD_HTTP_INTERNAL_SERVER_ERROR,
54 : TALER_EC_GENERIC_PARSER_OUT_OF_MEMORY,
55 0 : NULL)) ? GNUNET_SYSERR : GNUNET_NO;
56 :
57 7344 : case GNUNET_MHD_PR_CONTINUE:
58 7344 : GNUNET_break (NULL == *json);
59 7344 : return GNUNET_YES;
60 0 : case GNUNET_MHD_PR_REQUEST_TOO_LARGE:
61 0 : GNUNET_break (NULL == *json);
62 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
63 : "Closing connection, upload too large\n");
64 0 : return GNUNET_SYSERR;
65 0 : case GNUNET_MHD_PR_JSON_INVALID:
66 0 : GNUNET_break (NULL == *json);
67 : return (MHD_YES ==
68 0 : TALER_MHD_reply_with_error (connection,
69 : MHD_HTTP_BAD_REQUEST,
70 : TALER_EC_GENERIC_JSON_INVALID,
71 : NULL))
72 0 : ? GNUNET_NO : GNUNET_SYSERR;
73 3652 : case GNUNET_MHD_PR_SUCCESS:
74 3652 : GNUNET_break (NULL != *json);
75 3652 : return GNUNET_YES;
76 : }
77 : /* this should never happen */
78 0 : GNUNET_break (0);
79 0 : return GNUNET_SYSERR;
80 : }
81 :
82 :
83 : void
84 3907 : TALER_MHD_parse_post_cleanup_callback (void *con_cls)
85 : {
86 3907 : GNUNET_MHD_post_parser_cleanup (con_cls);
87 3907 : }
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 65 : 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 65 : str = MHD_lookup_connection_value (connection,
119 : kind,
120 : param_name);
121 65 : if (NULL == str)
122 : {
123 15 : *present = false;
124 15 : return GNUNET_OK;
125 : }
126 50 : if (GNUNET_OK !=
127 50 : GNUNET_STRINGS_string_to_data (str,
128 : strlen (str),
129 : out_data,
130 : out_size))
131 : {
132 0 : GNUNET_break_op (0);
133 : return (MHD_NO ==
134 0 : TALER_MHD_reply_with_error (connection,
135 : MHD_HTTP_BAD_REQUEST,
136 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
137 : param_name))
138 0 : ? GNUNET_SYSERR : GNUNET_NO;
139 : }
140 50 : *present = true;
141 50 : return GNUNET_OK;
142 : }
143 :
144 :
145 : enum GNUNET_GenericReturnValue
146 23 : 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 23 : 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 42 : 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 42 : 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 0 : 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 0 : ts = MHD_lookup_connection_value (connection,
190 : MHD_GET_ARGUMENT_KIND,
191 : label);
192 0 : if (NULL == ts)
193 : {
194 0 : *duration = GNUNET_TIME_UNIT_ZERO;
195 0 : return GNUNET_OK;
196 : }
197 0 : if (1 !=
198 0 : sscanf (ts,
199 : "%llu%c",
200 : &tms,
201 : &dummy))
202 : {
203 : MHD_RESULT mret;
204 :
205 0 : GNUNET_break_op (0);
206 0 : 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 0 : : GNUNET_SYSERR;
213 : }
214 0 : *duration = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
215 : tms);
216 0 : return GNUNET_OK;
217 : }
218 :
219 :
220 : enum GNUNET_GenericReturnValue
221 73 : 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 73 : ts = MHD_lookup_connection_value (connection,
230 : MHD_GET_ARGUMENT_KIND,
231 : "timeout_ms");
232 73 : if (NULL == ts)
233 : {
234 42 : *expiration = GNUNET_TIME_UNIT_ZERO_ABS;
235 42 : return GNUNET_OK;
236 : }
237 31 : if (1 !=
238 31 : sscanf (ts,
239 : "%llu%c",
240 : &tms,
241 : &dummy))
242 : {
243 : MHD_RESULT mret;
244 :
245 0 : GNUNET_break_op (0);
246 0 : 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 0 : : GNUNET_SYSERR;
253 : }
254 31 : *expiration = GNUNET_TIME_relative_to_absolute (
255 : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
256 : tms));
257 31 : return GNUNET_OK;
258 : }
259 :
260 :
261 : enum GNUNET_GenericReturnValue
262 0 : 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 0 : s = MHD_lookup_connection_value (connection,
270 : MHD_GET_ARGUMENT_KIND,
271 : fname);
272 0 : if (NULL == s)
273 0 : return GNUNET_OK;
274 0 : if (GNUNET_OK !=
275 0 : GNUNET_STRINGS_fancy_time_to_timestamp (s,
276 : ts))
277 : {
278 : MHD_RESULT mret;
279 :
280 0 : GNUNET_break_op (0);
281 0 : 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 0 : : GNUNET_SYSERR;
289 : }
290 0 : return GNUNET_OK;
291 : }
292 :
293 :
294 : enum GNUNET_GenericReturnValue
295 52 : 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 52 : ts = MHD_lookup_connection_value (connection,
304 : MHD_GET_ARGUMENT_KIND,
305 : name);
306 52 : if (NULL == ts)
307 38 : return GNUNET_OK;
308 14 : if (1 !=
309 14 : sscanf (ts,
310 : "%llu%c",
311 : &num,
312 : &dummy))
313 : {
314 : MHD_RESULT mret;
315 :
316 0 : GNUNET_break_op (0);
317 0 : 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 0 : : GNUNET_SYSERR;
324 : }
325 14 : *off = (uint64_t) num;
326 14 : return GNUNET_OK;
327 : }
328 :
329 :
330 : enum GNUNET_GenericReturnValue
331 4 : 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 4 : ts = MHD_lookup_connection_value (connection,
340 : MHD_GET_ARGUMENT_KIND,
341 : name);
342 4 : if (NULL == ts)
343 0 : return GNUNET_OK;
344 4 : if (1 !=
345 4 : sscanf (ts,
346 : "%lld%c",
347 : &num,
348 : &dummy))
349 : {
350 : MHD_RESULT mret;
351 :
352 0 : GNUNET_break_op (0);
353 0 : 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 0 : : GNUNET_SYSERR;
360 : }
361 4 : *val = (int64_t) num;
362 4 : return GNUNET_OK;
363 : }
364 :
365 :
366 : enum GNUNET_GenericReturnValue
367 0 : 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 0 : ts = MHD_lookup_connection_value (connection,
374 : MHD_GET_ARGUMENT_KIND,
375 : name);
376 0 : if (NULL == ts)
377 0 : return GNUNET_OK;
378 0 : if (GNUNET_OK !=
379 0 : TALER_string_to_amount (ts,
380 : val))
381 : {
382 : MHD_RESULT mret;
383 :
384 0 : GNUNET_break_op (0);
385 0 : 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 0 : : GNUNET_SYSERR;
392 : }
393 0 : return GNUNET_OK;
394 : }
395 :
396 :
397 : enum GNUNET_GenericReturnValue
398 10087 : 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 10087 : ret = GNUNET_JSON_parse (root,
407 : spec,
408 : &error_json_name,
409 : &error_line);
410 10087 : if (GNUNET_SYSERR == ret)
411 : {
412 0 : if (NULL == error_json_name)
413 0 : error_json_name = "<no field>";
414 0 : ret = (MHD_YES ==
415 0 : 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 0 : ? GNUNET_NO : GNUNET_SYSERR;
428 0 : return ret;
429 : }
430 10087 : return GNUNET_YES;
431 : }
432 :
433 :
434 : enum GNUNET_GenericReturnValue
435 0 : 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 0 : ret = GNUNET_JSON_parse (root,
444 : spec,
445 : &error_json_name,
446 : &error_line);
447 0 : if (GNUNET_SYSERR == ret)
448 : {
449 0 : if (NULL == error_json_name)
450 0 : error_json_name = "<no field>";
451 0 : ret = (MHD_YES ==
452 0 : 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 0 : ? GNUNET_NO : GNUNET_SYSERR;
465 0 : return ret;
466 : }
467 0 : return GNUNET_YES;
468 : }
469 :
470 :
471 : enum GNUNET_GenericReturnValue
472 98 : 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 98 : va_start (ap, spec);
484 98 : dim = 0;
485 196 : while ( (-1 != (ret = va_arg (ap, int))) &&
486 : (NULL != root) )
487 : {
488 98 : dim++;
489 98 : root = json_array_get (root, ret);
490 : }
491 98 : va_end (ap);
492 98 : if (NULL == root)
493 : {
494 0 : ret = (MHD_YES ==
495 0 : 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 0 : ? GNUNET_NO : GNUNET_SYSERR;
508 0 : return ret;
509 : }
510 98 : ret = GNUNET_JSON_parse (root,
511 : spec,
512 : &error_json_name,
513 : &error_line);
514 98 : if (GNUNET_SYSERR == ret)
515 : {
516 0 : if (NULL == error_json_name)
517 0 : error_json_name = "<no field>";
518 0 : ret = (MHD_YES ==
519 0 : 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 0 : ? GNUNET_NO : GNUNET_SYSERR;
532 0 : return ret;
533 : }
534 98 : return GNUNET_YES;
535 : }
536 :
537 :
538 : enum GNUNET_GenericReturnValue
539 3653 : 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 3653 : cl = MHD_lookup_connection_value (connection,
549 : MHD_HEADER_KIND,
550 : MHD_HTTP_HEADER_CONTENT_LENGTH);
551 3653 : if (NULL == cl)
552 : {
553 0 : 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 3653 : if (1 != sscanf (cl,
567 : "%llu%c",
568 : &cv,
569 : &dummy))
570 : {
571 : /* Not valid HTTP request, just close connection. */
572 0 : GNUNET_break_op (0);
573 : return (MHD_YES ==
574 0 : 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 0 : : GNUNET_SYSERR;
580 : }
581 3653 : if (cv > TALER_MHD_REQUEST_BUFFER_MAX)
582 : {
583 0 : GNUNET_break_op (0);
584 : return (MHD_YES ==
585 0 : TALER_MHD_reply_request_too_large (connection))
586 : ? GNUNET_NO
587 0 : : GNUNET_SYSERR;
588 : }
589 3653 : return GNUNET_OK;
590 : }
591 :
592 :
593 : /* end of mhd_parsing.c */
|