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 : /**
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 2652 : 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 2652 : 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 2652 : switch (pr)
56 : {
57 0 : case GNUNET_MHD_PR_OUT_OF_MEMORY:
58 0 : GNUNET_break (NULL == *json);
59 : return (MHD_NO ==
60 0 : TALER_MHD_reply_with_error (
61 : connection,
62 : MHD_HTTP_INTERNAL_SERVER_ERROR,
63 : TALER_EC_GENERIC_PARSER_OUT_OF_MEMORY,
64 0 : NULL)) ? GNUNET_SYSERR : GNUNET_NO;
65 :
66 1768 : case GNUNET_MHD_PR_CONTINUE:
67 1768 : GNUNET_break (NULL == *json);
68 1768 : return GNUNET_YES;
69 0 : case GNUNET_MHD_PR_REQUEST_TOO_LARGE:
70 0 : GNUNET_break (NULL == *json);
71 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
72 : "Closing connection, upload too large\n");
73 0 : return GNUNET_SYSERR;
74 0 : case GNUNET_MHD_PR_JSON_INVALID:
75 0 : GNUNET_break (NULL == *json);
76 : return (MHD_YES ==
77 0 : TALER_MHD_reply_with_error (connection,
78 : MHD_HTTP_BAD_REQUEST,
79 : TALER_EC_GENERIC_JSON_INVALID,
80 : NULL))
81 0 : ? GNUNET_NO : GNUNET_SYSERR;
82 884 : case GNUNET_MHD_PR_SUCCESS:
83 884 : GNUNET_break (NULL != *json);
84 884 : return GNUNET_YES;
85 : }
86 : /* this should never happen */
87 0 : GNUNET_break (0);
88 0 : return GNUNET_SYSERR;
89 : }
90 :
91 :
92 : void
93 1176 : TALER_MHD_parse_post_cleanup_callback (void *con_cls)
94 : {
95 1176 : GNUNET_MHD_post_parser_cleanup (con_cls);
96 1176 : }
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 68 : 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 68 : str = MHD_lookup_connection_value (connection,
128 : kind,
129 : param_name);
130 68 : if (NULL == str)
131 : {
132 7 : *present = false;
133 7 : return GNUNET_OK;
134 : }
135 61 : if (GNUNET_OK !=
136 61 : GNUNET_STRINGS_string_to_data (str,
137 : strlen (str),
138 : out_data,
139 : out_size))
140 : {
141 0 : GNUNET_break_op (0);
142 : return (MHD_NO ==
143 0 : TALER_MHD_reply_with_error (connection,
144 : MHD_HTTP_BAD_REQUEST,
145 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
146 : param_name))
147 0 : ? GNUNET_SYSERR : GNUNET_NO;
148 : }
149 61 : *present = true;
150 61 : return GNUNET_OK;
151 : }
152 :
153 :
154 : enum GNUNET_GenericReturnValue
155 18 : 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 18 : 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 50 : 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 50 : 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 0 : 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 0 : ts = MHD_lookup_connection_value (connection,
199 : MHD_GET_ARGUMENT_KIND,
200 : label);
201 0 : if (NULL == ts)
202 : {
203 0 : *duration = GNUNET_TIME_UNIT_ZERO;
204 0 : return GNUNET_OK;
205 : }
206 0 : if (1 !=
207 0 : sscanf (ts,
208 : "%llu%c",
209 : &tms,
210 : &dummy))
211 : {
212 : enum MHD_Result mret;
213 :
214 0 : GNUNET_break_op (0);
215 0 : 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 0 : : GNUNET_SYSERR;
222 : }
223 0 : *duration = GNUNET_TIME_relative_saturating_multiply (
224 : GNUNET_TIME_UNIT_MILLISECONDS,
225 : tms);
226 0 : return GNUNET_OK;
227 : }
228 :
229 :
230 : enum GNUNET_GenericReturnValue
231 81 : 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 81 : ts = MHD_lookup_connection_value (connection,
240 : MHD_GET_ARGUMENT_KIND,
241 : "timeout_ms");
242 81 : if (NULL == ts)
243 : {
244 50 : *expiration = GNUNET_TIME_UNIT_ZERO_ABS;
245 50 : return GNUNET_OK;
246 : }
247 31 : if (1 !=
248 31 : sscanf (ts,
249 : "%llu%c",
250 : &tms,
251 : &dummy))
252 : {
253 : enum MHD_Result mret;
254 :
255 0 : GNUNET_break_op (0);
256 0 : 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 0 : : GNUNET_SYSERR;
263 : }
264 31 : if (tms > MAX_TIMEOUT_MS)
265 0 : tms = MAX_TIMEOUT_MS;
266 31 : *expiration = GNUNET_TIME_relative_to_absolute (
267 : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
268 : tms));
269 31 : return GNUNET_OK;
270 : }
271 :
272 :
273 : enum GNUNET_GenericReturnValue
274 0 : 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 0 : s = MHD_lookup_connection_value (connection,
282 : MHD_GET_ARGUMENT_KIND,
283 : fname);
284 0 : if (NULL == s)
285 0 : return GNUNET_OK;
286 0 : if (GNUNET_OK !=
287 0 : GNUNET_STRINGS_fancy_time_to_timestamp (s,
288 : ts))
289 : {
290 : enum MHD_Result mret;
291 :
292 0 : GNUNET_break_op (0);
293 0 : 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 0 : : GNUNET_SYSERR;
301 : }
302 0 : return GNUNET_OK;
303 : }
304 :
305 :
306 : enum GNUNET_GenericReturnValue
307 192 : 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 192 : ts = MHD_lookup_connection_value (connection,
316 : MHD_GET_ARGUMENT_KIND,
317 : name);
318 192 : if (NULL == ts)
319 178 : return GNUNET_OK;
320 14 : if (1 !=
321 14 : sscanf (ts,
322 : "%llu%c",
323 : &num,
324 : &dummy))
325 : {
326 : enum MHD_Result mret;
327 :
328 0 : GNUNET_break_op (0);
329 0 : 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 0 : : GNUNET_SYSERR;
336 : }
337 14 : *off = (uint64_t) num;
338 14 : return GNUNET_OK;
339 : }
340 :
341 :
342 : enum GNUNET_GenericReturnValue
343 136 : 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 136 : ts = MHD_lookup_connection_value (connection,
352 : MHD_GET_ARGUMENT_KIND,
353 : name);
354 136 : if (NULL == ts)
355 132 : return GNUNET_OK;
356 4 : if (1 !=
357 4 : sscanf (ts,
358 : "%lld%c",
359 : &num,
360 : &dummy))
361 : {
362 : enum MHD_Result mret;
363 :
364 0 : GNUNET_break_op (0);
365 0 : 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 0 : : GNUNET_SYSERR;
372 : }
373 4 : *val = (int64_t) num;
374 4 : return GNUNET_OK;
375 : }
376 :
377 :
378 : enum GNUNET_GenericReturnValue
379 0 : 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 0 : ts = MHD_lookup_connection_value (connection,
386 : MHD_GET_ARGUMENT_KIND,
387 : name);
388 0 : if (NULL == ts)
389 0 : return GNUNET_OK;
390 0 : if (GNUNET_OK !=
391 0 : TALER_string_to_amount (ts,
392 : val))
393 : {
394 : enum MHD_Result mret;
395 :
396 0 : GNUNET_break_op (0);
397 0 : 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 0 : : GNUNET_SYSERR;
404 : }
405 0 : return GNUNET_OK;
406 : }
407 :
408 :
409 : enum GNUNET_GenericReturnValue
410 2860 : 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 2860 : ret = GNUNET_JSON_parse (root,
419 : spec,
420 : &error_json_name,
421 : &error_line);
422 2860 : if (GNUNET_SYSERR == ret)
423 : {
424 0 : if (NULL == error_json_name)
425 0 : error_json_name = "<no field>";
426 0 : ret = (MHD_YES ==
427 0 : 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 0 : ? GNUNET_NO : GNUNET_SYSERR;
440 0 : return ret;
441 : }
442 2860 : return GNUNET_YES;
443 : }
444 :
445 :
446 : enum GNUNET_GenericReturnValue
447 0 : 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 0 : ret = GNUNET_JSON_parse (root,
456 : spec,
457 : &error_json_name,
458 : &error_line);
459 0 : if (GNUNET_SYSERR == ret)
460 : {
461 0 : if (NULL == error_json_name)
462 0 : error_json_name = "<no field>";
463 0 : ret = (MHD_YES ==
464 0 : 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 0 : ? GNUNET_NO : GNUNET_SYSERR;
477 0 : return ret;
478 : }
479 0 : return GNUNET_YES;
480 : }
481 :
482 :
483 : enum GNUNET_GenericReturnValue
484 132 : 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 132 : va_start (ap, spec);
496 132 : dim = 0;
497 264 : while ( (-1 != (ret = va_arg (ap, int))) &&
498 : (NULL != root) )
499 : {
500 132 : dim++;
501 132 : root = json_array_get (root, ret);
502 : }
503 132 : va_end (ap);
504 132 : if (NULL == root)
505 : {
506 0 : ret = (MHD_YES ==
507 0 : 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 0 : ? GNUNET_NO : GNUNET_SYSERR;
520 0 : return ret;
521 : }
522 132 : ret = GNUNET_JSON_parse (root,
523 : spec,
524 : &error_json_name,
525 : &error_line);
526 132 : if (GNUNET_SYSERR == ret)
527 : {
528 0 : if (NULL == error_json_name)
529 0 : error_json_name = "<no field>";
530 0 : ret = (MHD_YES ==
531 0 : 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 0 : ? GNUNET_NO : GNUNET_SYSERR;
544 0 : return ret;
545 : }
546 132 : return GNUNET_YES;
547 : }
548 :
549 :
550 : enum GNUNET_GenericReturnValue
551 884 : 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 884 : cl = MHD_lookup_connection_value (connection,
561 : MHD_HEADER_KIND,
562 : MHD_HTTP_HEADER_CONTENT_LENGTH);
563 884 : if (NULL == cl)
564 : {
565 0 : 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 884 : if (1 != sscanf (cl,
579 : "%llu%c",
580 : &cv,
581 : &dummy))
582 : {
583 : /* Not valid HTTP request, just close connection. */
584 0 : GNUNET_break_op (0);
585 : return (MHD_YES ==
586 0 : 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 0 : : GNUNET_SYSERR;
592 : }
593 884 : if (cv > max_len)
594 : {
595 0 : GNUNET_break_op (0);
596 : return (MHD_YES ==
597 0 : TALER_MHD_reply_request_too_large (connection))
598 : ? GNUNET_NO
599 0 : : GNUNET_SYSERR;
600 : }
601 884 : return GNUNET_OK;
602 : }
603 :
604 :
605 : /* end of mhd_parsing.c */
|