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