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