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 13687 : 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 13687 : 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 13687 : 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 9140 : case GNUNET_MHD_PR_CONTINUE:
58 9140 : GNUNET_break (NULL == *json);
59 9140 : 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 4547 : case GNUNET_MHD_PR_SUCCESS:
74 4547 : GNUNET_break (NULL != *json);
75 4547 : 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 4823 : TALER_MHD_parse_post_cleanup_callback (void *con_cls)
85 : {
86 4823 : GNUNET_MHD_post_parser_cleanup (con_cls);
87 4823 : }
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 91 : 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 91 : str = MHD_lookup_connection_value (connection,
119 : kind,
120 : param_name);
121 91 : if (NULL == str)
122 : {
123 39 : *present = false;
124 39 : return GNUNET_OK;
125 : }
126 52 : if (GNUNET_OK !=
127 52 : 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 52 : *present = true;
141 52 : return GNUNET_OK;
142 : }
143 :
144 :
145 : enum GNUNET_GenericReturnValue
146 45 : 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 45 : 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 46 : 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 46 : 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 79 : TALER_MHD_parse_request_arg_timeout (
181 : struct MHD_Connection *connection,
182 : struct GNUNET_TIME_Absolute *expiration)
183 : {
184 : const char *ts;
185 : char dummy;
186 : unsigned long long tms;
187 :
188 79 : ts = MHD_lookup_connection_value (connection,
189 : MHD_GET_ARGUMENT_KIND,
190 : "timeout_ms");
191 79 : if (NULL == ts)
192 : {
193 44 : *expiration = GNUNET_TIME_UNIT_ZERO_ABS;
194 44 : return GNUNET_OK;
195 : }
196 35 : if (1 !=
197 35 : 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 : "timeout_ms");
209 : return (MHD_YES == mret)
210 : ? GNUNET_NO
211 0 : : GNUNET_SYSERR;
212 : }
213 35 : *expiration = GNUNET_TIME_relative_to_absolute (
214 : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
215 : tms));
216 35 : return GNUNET_OK;
217 : }
218 :
219 :
220 : enum GNUNET_GenericReturnValue
221 0 : TALER_MHD_parse_request_arg_timestamp (
222 : struct MHD_Connection *connection,
223 : const char *fname,
224 : struct GNUNET_TIME_Timestamp *ts)
225 : {
226 : const char *s;
227 : char dummy;
228 : unsigned long long t;
229 :
230 0 : s = MHD_lookup_connection_value (connection,
231 : MHD_GET_ARGUMENT_KIND,
232 : fname);
233 0 : if (NULL == s)
234 : {
235 0 : *ts = GNUNET_TIME_UNIT_ZERO_TS;
236 0 : return GNUNET_OK;
237 : }
238 0 : if (1 !=
239 0 : sscanf (s,
240 : "%llu%c",
241 : &t,
242 : &dummy))
243 : {
244 : MHD_RESULT mret;
245 :
246 0 : GNUNET_break_op (0);
247 0 : mret = TALER_MHD_reply_with_error (
248 : connection,
249 : MHD_HTTP_BAD_REQUEST,
250 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
251 : fname);
252 : return (MHD_YES == mret)
253 : ? GNUNET_NO
254 0 : : GNUNET_SYSERR;
255 : }
256 0 : *ts = GNUNET_TIME_timestamp_from_s (t);
257 0 : return GNUNET_OK;
258 : }
259 :
260 :
261 : enum GNUNET_GenericReturnValue
262 141 : TALER_MHD_parse_request_arg_number (struct MHD_Connection *connection,
263 : const char *name,
264 : uint64_t *off)
265 : {
266 : const char *ts;
267 : char dummy;
268 : unsigned long long num;
269 :
270 141 : ts = MHD_lookup_connection_value (connection,
271 : MHD_GET_ARGUMENT_KIND,
272 : name);
273 141 : if (NULL == ts)
274 127 : return GNUNET_OK;
275 14 : if (1 !=
276 14 : sscanf (ts,
277 : "%llu%c",
278 : &num,
279 : &dummy))
280 : {
281 : MHD_RESULT mret;
282 :
283 0 : GNUNET_break_op (0);
284 0 : mret = TALER_MHD_reply_with_error (connection,
285 : MHD_HTTP_BAD_REQUEST,
286 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
287 : name);
288 : return (MHD_YES == mret)
289 : ? GNUNET_NO
290 0 : : GNUNET_SYSERR;
291 : }
292 14 : *off = (uint64_t) num;
293 14 : return GNUNET_OK;
294 : }
295 :
296 :
297 : enum GNUNET_GenericReturnValue
298 89 : TALER_MHD_parse_request_arg_snumber (struct MHD_Connection *connection,
299 : const char *name,
300 : int64_t *val)
301 : {
302 : const char *ts;
303 : char dummy;
304 : long long num;
305 :
306 89 : ts = MHD_lookup_connection_value (connection,
307 : MHD_GET_ARGUMENT_KIND,
308 : name);
309 89 : if (NULL == ts)
310 0 : return GNUNET_OK;
311 89 : if (1 !=
312 89 : sscanf (ts,
313 : "%lld%c",
314 : &num,
315 : &dummy))
316 : {
317 : MHD_RESULT mret;
318 :
319 0 : GNUNET_break_op (0);
320 0 : mret = TALER_MHD_reply_with_error (connection,
321 : MHD_HTTP_BAD_REQUEST,
322 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
323 : name);
324 : return (MHD_YES == mret)
325 : ? GNUNET_NO
326 0 : : GNUNET_SYSERR;
327 : }
328 89 : *val = (int64_t) num;
329 89 : return GNUNET_OK;
330 : }
331 :
332 :
333 : enum GNUNET_GenericReturnValue
334 0 : TALER_MHD_parse_request_arg_amount (struct MHD_Connection *connection,
335 : const char *name,
336 : struct TALER_Amount *val)
337 : {
338 : const char *ts;
339 :
340 0 : ts = MHD_lookup_connection_value (connection,
341 : MHD_GET_ARGUMENT_KIND,
342 : name);
343 0 : if (NULL == ts)
344 0 : return GNUNET_OK;
345 0 : if (GNUNET_OK !=
346 0 : TALER_string_to_amount (ts,
347 : val))
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 0 : return GNUNET_OK;
361 : }
362 :
363 :
364 : enum GNUNET_GenericReturnValue
365 12200 : TALER_MHD_parse_json_data (struct MHD_Connection *connection,
366 : const json_t *root,
367 : struct GNUNET_JSON_Specification *spec)
368 : {
369 : enum GNUNET_GenericReturnValue ret;
370 : const char *error_json_name;
371 : unsigned int error_line;
372 :
373 12200 : ret = GNUNET_JSON_parse (root,
374 : spec,
375 : &error_json_name,
376 : &error_line);
377 12200 : if (GNUNET_SYSERR == ret)
378 : {
379 0 : if (NULL == error_json_name)
380 0 : error_json_name = "<no field>";
381 0 : ret = (MHD_YES ==
382 0 : TALER_MHD_REPLY_JSON_PACK (
383 : connection,
384 : MHD_HTTP_BAD_REQUEST,
385 : GNUNET_JSON_pack_string ("hint",
386 : TALER_ErrorCode_get_hint (
387 : TALER_EC_GENERIC_JSON_INVALID)),
388 : GNUNET_JSON_pack_uint64 ("code",
389 : TALER_EC_GENERIC_JSON_INVALID),
390 : GNUNET_JSON_pack_string ("field",
391 : error_json_name),
392 : GNUNET_JSON_pack_uint64 ("line",
393 : error_line)))
394 0 : ? GNUNET_NO : GNUNET_SYSERR;
395 0 : return ret;
396 : }
397 12200 : return GNUNET_YES;
398 : }
399 :
400 :
401 : enum GNUNET_GenericReturnValue
402 0 : TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection,
403 : const json_t *root,
404 : struct GNUNET_JSON_Specification *spec)
405 : {
406 : enum GNUNET_GenericReturnValue ret;
407 : const char *error_json_name;
408 : unsigned int error_line;
409 :
410 0 : ret = GNUNET_JSON_parse (root,
411 : spec,
412 : &error_json_name,
413 : &error_line);
414 0 : if (GNUNET_SYSERR == ret)
415 : {
416 0 : if (NULL == error_json_name)
417 0 : error_json_name = "<no field>";
418 0 : ret = (MHD_YES ==
419 0 : TALER_MHD_REPLY_JSON_PACK (
420 : connection,
421 : MHD_HTTP_INTERNAL_SERVER_ERROR,
422 : GNUNET_JSON_pack_string ("hint",
423 : TALER_ErrorCode_get_hint (
424 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)),
425 : GNUNET_JSON_pack_uint64 ("code",
426 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE),
427 : GNUNET_JSON_pack_string ("field",
428 : error_json_name),
429 : GNUNET_JSON_pack_uint64 ("line",
430 : error_line)))
431 0 : ? GNUNET_NO : GNUNET_SYSERR;
432 0 : return ret;
433 : }
434 0 : return GNUNET_YES;
435 : }
436 :
437 :
438 : enum GNUNET_GenericReturnValue
439 98 : TALER_MHD_parse_json_array (struct MHD_Connection *connection,
440 : const json_t *root,
441 : struct GNUNET_JSON_Specification *spec,
442 : ...)
443 : {
444 : enum GNUNET_GenericReturnValue ret;
445 : const char *error_json_name;
446 : unsigned int error_line;
447 : va_list ap;
448 : json_int_t dim;
449 :
450 98 : va_start (ap, spec);
451 98 : dim = 0;
452 196 : while ( (-1 != (ret = va_arg (ap, int))) &&
453 : (NULL != root) )
454 : {
455 98 : dim++;
456 98 : root = json_array_get (root, ret);
457 : }
458 98 : va_end (ap);
459 98 : if (NULL == root)
460 : {
461 0 : ret = (MHD_YES ==
462 0 : TALER_MHD_REPLY_JSON_PACK (
463 : connection,
464 : MHD_HTTP_BAD_REQUEST,
465 : GNUNET_JSON_pack_string ("hint",
466 : TALER_ErrorCode_get_hint (
467 : TALER_EC_GENERIC_JSON_INVALID)),
468 : GNUNET_JSON_pack_uint64 ("code",
469 : TALER_EC_GENERIC_JSON_INVALID),
470 : GNUNET_JSON_pack_string ("detail",
471 : "expected array"),
472 : GNUNET_JSON_pack_uint64 ("dimension",
473 : dim)))
474 0 : ? GNUNET_NO : GNUNET_SYSERR;
475 0 : return ret;
476 : }
477 98 : ret = GNUNET_JSON_parse (root,
478 : spec,
479 : &error_json_name,
480 : &error_line);
481 98 : if (GNUNET_SYSERR == ret)
482 : {
483 0 : if (NULL == error_json_name)
484 0 : error_json_name = "<no field>";
485 0 : ret = (MHD_YES ==
486 0 : TALER_MHD_REPLY_JSON_PACK (
487 : connection,
488 : MHD_HTTP_BAD_REQUEST,
489 : GNUNET_JSON_pack_string ("detail",
490 : error_json_name),
491 : GNUNET_JSON_pack_string ("hint",
492 : TALER_ErrorCode_get_hint (
493 : TALER_EC_GENERIC_JSON_INVALID)),
494 : GNUNET_JSON_pack_uint64 ("code",
495 : TALER_EC_GENERIC_JSON_INVALID),
496 : GNUNET_JSON_pack_uint64 ("line",
497 : error_line)))
498 0 : ? GNUNET_NO : GNUNET_SYSERR;
499 0 : return ret;
500 : }
501 98 : return GNUNET_YES;
502 : }
503 :
504 :
505 : enum GNUNET_GenericReturnValue
506 4540 : TALER_MHD_check_content_length_ (struct MHD_Connection *connection,
507 : unsigned long long max_len)
508 : {
509 : const char *cl;
510 : unsigned long long cv;
511 : char dummy;
512 :
513 : /* Maybe check for maximum upload size
514 : and refuse requests if they are just too big. */
515 4540 : cl = MHD_lookup_connection_value (connection,
516 : MHD_HEADER_KIND,
517 : MHD_HTTP_HEADER_CONTENT_LENGTH);
518 4540 : if (NULL == cl)
519 : {
520 20 : return GNUNET_OK;
521 : #if 0
522 : /* wallet currently doesn't always send content-length! */
523 : GNUNET_break_op (0);
524 : return (MHD_YES ==
525 : TALER_MHD_reply_with_error (connection,
526 : MHD_HTTP_BAD_REQUEST,
527 : TALER_EC_GENERIC_PARAMETER_MISSING,
528 : MHD_HTTP_HEADER_CONTENT_LENGTH))
529 : ? GNUNET_NO
530 : : GNUNET_SYSERR;
531 : #endif
532 : }
533 4520 : if (1 != sscanf (cl,
534 : "%llu%c",
535 : &cv,
536 : &dummy))
537 : {
538 : /* Not valid HTTP request, just close connection. */
539 0 : GNUNET_break_op (0);
540 : return (MHD_YES ==
541 0 : TALER_MHD_reply_with_error (connection,
542 : MHD_HTTP_BAD_REQUEST,
543 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
544 : MHD_HTTP_HEADER_CONTENT_LENGTH))
545 : ? GNUNET_NO
546 0 : : GNUNET_SYSERR;
547 : }
548 4520 : if (cv > TALER_MHD_REQUEST_BUFFER_MAX)
549 : {
550 0 : GNUNET_break_op (0);
551 : return (MHD_YES ==
552 0 : TALER_MHD_reply_request_too_large (connection))
553 : ? GNUNET_NO
554 0 : : GNUNET_SYSERR;
555 : }
556 4520 : return GNUNET_OK;
557 : }
558 :
559 :
560 : /* end of mhd_parsing.c */
|