Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014--2020 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 "platform.h"
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include <gnunet/gnunet_json_lib.h>
26 : #include "taler_json_lib.h"
27 : #include "taler_mhd_lib.h"
28 :
29 :
30 : /**
31 : * Maximum POST request size.
32 : */
33 : #define REQUEST_BUFFER_MAX (1024 * 1024)
34 :
35 :
36 : /**
37 : * Process a POST request containing a JSON object. This function
38 : * realizes an MHD POST processor that will (incrementally) process
39 : * JSON data uploaded to the HTTP server. It will store the required
40 : * state in the @a con_cls, which must be cleaned up using
41 : * #TALER_MHD_parse_post_cleanup_callback().
42 : *
43 : * @param connection the MHD connection
44 : * @param con_cls the closure (points to a `struct Buffer *`)
45 : * @param upload_data the POST data
46 : * @param upload_data_size number of bytes in @a upload_data
47 : * @param json the JSON object for a completed request
48 : * @return
49 : * #GNUNET_YES if json object was parsed or at least
50 : * may be parsed in the future (call again);
51 : * `*json` will be NULL if we need to be called again,
52 : * and non-NULL if we are done.
53 : * #GNUNET_NO is request incomplete or invalid
54 : * (error message was generated)
55 : * #GNUNET_SYSERR on internal error
56 : * (we could not even queue an error message,
57 : * close HTTP session with MHD_NO)
58 : */
59 : enum GNUNET_GenericReturnValue
60 108 : TALER_MHD_parse_post_json (struct MHD_Connection *connection,
61 : void **con_cls,
62 : const char *upload_data,
63 : size_t *upload_data_size,
64 : json_t **json)
65 : {
66 : enum GNUNET_JSON_PostResult pr;
67 :
68 108 : pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
69 : connection,
70 : con_cls,
71 : upload_data,
72 : upload_data_size,
73 : json);
74 108 : switch (pr)
75 : {
76 0 : case GNUNET_JSON_PR_OUT_OF_MEMORY:
77 0 : GNUNET_break (NULL == *json);
78 : return (MHD_NO ==
79 0 : TALER_MHD_reply_with_error (
80 : connection,
81 : MHD_HTTP_INTERNAL_SERVER_ERROR,
82 : TALER_EC_GENERIC_PARSER_OUT_OF_MEMORY,
83 0 : NULL)) ? GNUNET_SYSERR : GNUNET_NO;
84 :
85 72 : case GNUNET_JSON_PR_CONTINUE:
86 72 : GNUNET_break (NULL == *json);
87 72 : return GNUNET_YES;
88 0 : case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
89 0 : GNUNET_break (NULL == *json);
90 : return (MHD_NO ==
91 0 : TALER_MHD_reply_request_too_large
92 0 : (connection)) ? GNUNET_SYSERR : GNUNET_NO;
93 0 : case GNUNET_JSON_PR_JSON_INVALID:
94 0 : GNUNET_break (NULL == *json);
95 : return (MHD_YES ==
96 0 : TALER_MHD_reply_with_error (connection,
97 : MHD_HTTP_BAD_REQUEST,
98 : TALER_EC_GENERIC_JSON_INVALID,
99 : NULL))
100 0 : ? GNUNET_NO : GNUNET_SYSERR;
101 36 : case GNUNET_JSON_PR_SUCCESS:
102 36 : GNUNET_break (NULL != *json);
103 36 : return GNUNET_YES;
104 : }
105 : /* this should never happen */
106 0 : GNUNET_break (0);
107 0 : return GNUNET_SYSERR;
108 : }
109 :
110 :
111 : /**
112 : * Function called whenever we are done with a request
113 : * to clean up our state.
114 : *
115 : * @param con_cls value as it was left by
116 : * #TALER_MHD_parse_post_json(), to be cleaned up
117 : */
118 : void
119 97 : TALER_MHD_parse_post_cleanup_callback (void *con_cls)
120 : {
121 97 : GNUNET_JSON_post_parser_cleanup (con_cls);
122 97 : }
123 :
124 :
125 : /**
126 : * Extract base32crockford encoded data from request.
127 : *
128 : * Queues an error response to the connection if the parameter is
129 : * missing or invalid.
130 : *
131 : * @param connection the MHD connection
132 : * @param param_name the name of the parameter with the key
133 : * @param[out] out_data pointer to store the result
134 : * @param out_size expected size of data
135 : * @return
136 : * #GNUNET_YES if the the argument is present
137 : * #GNUNET_NO if the argument is absent or malformed
138 : * #GNUNET_SYSERR on internal error (error response could not be sent)
139 : */
140 : int
141 0 : TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
142 : const char *param_name,
143 : void *out_data,
144 : size_t out_size)
145 : {
146 : const char *str;
147 :
148 0 : str = MHD_lookup_connection_value (connection,
149 : MHD_GET_ARGUMENT_KIND,
150 : param_name);
151 0 : if (NULL == str)
152 : {
153 : return (MHD_NO ==
154 0 : TALER_MHD_reply_with_error (connection,
155 : MHD_HTTP_BAD_REQUEST,
156 : TALER_EC_GENERIC_PARAMETER_MISSING,
157 : param_name))
158 0 : ? GNUNET_SYSERR : GNUNET_NO;
159 : }
160 0 : if (GNUNET_OK !=
161 0 : GNUNET_STRINGS_string_to_data (str,
162 : strlen (str),
163 : out_data,
164 : out_size))
165 : return (MHD_NO ==
166 0 : TALER_MHD_reply_with_error (connection,
167 : MHD_HTTP_BAD_REQUEST,
168 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
169 : param_name))
170 0 : ? GNUNET_SYSERR : GNUNET_NO;
171 0 : return GNUNET_OK;
172 : }
173 :
174 :
175 : /**
176 : * Parse JSON object into components based on the given field
177 : * specification. Generates error response on parse errors.
178 : *
179 : * @param connection the connection to send an error response to
180 : * @param root the JSON node to start the navigation at.
181 : * @param[in,out] spec field specification for the parser
182 : * @return
183 : * #GNUNET_YES if navigation was successful (caller is responsible
184 : * for freeing allocated variable-size data using
185 : * GNUNET_JSON_parse_free() when done)
186 : * #GNUNET_NO if json is malformed, error response was generated
187 : * #GNUNET_SYSERR on internal error
188 : */
189 : enum GNUNET_GenericReturnValue
190 36 : TALER_MHD_parse_json_data (struct MHD_Connection *connection,
191 : const json_t *root,
192 : struct GNUNET_JSON_Specification *spec)
193 : {
194 : enum GNUNET_GenericReturnValue ret;
195 : const char *error_json_name;
196 : unsigned int error_line;
197 :
198 36 : ret = GNUNET_JSON_parse (root,
199 : spec,
200 : &error_json_name,
201 : &error_line);
202 36 : if (GNUNET_SYSERR == ret)
203 : {
204 0 : if (NULL == error_json_name)
205 0 : error_json_name = "<no field>";
206 0 : ret = (MHD_YES ==
207 0 : TALER_MHD_reply_json_pack (
208 : connection,
209 : MHD_HTTP_BAD_REQUEST,
210 : "{s:s, s:I, s:s, s:I}",
211 : "hint", TALER_ErrorCode_get_hint (TALER_EC_GENERIC_JSON_INVALID),
212 : "code", (json_int_t) TALER_EC_GENERIC_JSON_INVALID,
213 : "field", error_json_name,
214 : "line", (json_int_t) error_line))
215 0 : ? GNUNET_NO : GNUNET_SYSERR;
216 0 : return ret;
217 : }
218 36 : return GNUNET_YES;
219 : }
220 :
221 :
222 : /**
223 : * Parse JSON object that we (the server!) generated into components based on
224 : * the given field specification. The difference to
225 : * #TALER_MHD_parse_json_data() is that this function will fail
226 : * with an HTTP failure of 500 (internal server error) in case
227 : * parsing fails, instead of blaming it on the client with a
228 : * 400 (#MHD_HTTP_BAD_REQUEST).
229 : *
230 : * @param connection the connection to send an error response to
231 : * @param root the JSON node to start the navigation at.
232 : * @param spec field specification for the parser
233 : * @return
234 : * #GNUNET_YES if navigation was successful (caller is responsible
235 : * for freeing allocated variable-size data using
236 : * GNUNET_JSON_parse_free() when done)
237 : * #GNUNET_NO if json is malformed, error response was generated
238 : * #GNUNET_SYSERR on internal error
239 : */
240 : enum GNUNET_GenericReturnValue
241 0 : TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection,
242 : const json_t *root,
243 : struct GNUNET_JSON_Specification *spec)
244 : {
245 : enum GNUNET_GenericReturnValue ret;
246 : const char *error_json_name;
247 : unsigned int error_line;
248 :
249 0 : ret = GNUNET_JSON_parse (root,
250 : spec,
251 : &error_json_name,
252 : &error_line);
253 0 : if (GNUNET_SYSERR == ret)
254 : {
255 0 : if (NULL == error_json_name)
256 0 : error_json_name = "<no field>";
257 0 : ret = (MHD_YES ==
258 0 : TALER_MHD_reply_json_pack (
259 : connection,
260 : MHD_HTTP_INTERNAL_SERVER_ERROR,
261 : "{s:s, s:I, s:s, s:I}",
262 : "hint", TALER_ErrorCode_get_hint (
263 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE),
264 : "code", (json_int_t) TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
265 : "field", error_json_name,
266 : "line", (json_int_t) error_line))
267 0 : ? GNUNET_NO : GNUNET_SYSERR;
268 0 : return ret;
269 : }
270 0 : return GNUNET_YES;
271 : }
272 :
273 :
274 : /**
275 : * Parse JSON array into components based on the given field
276 : * specification. Generates error response on parse errors.
277 : *
278 : * @param connection the connection to send an error response to
279 : * @param root the JSON node to start the navigation at.
280 : * @param[in,out] spec field specification for the parser
281 : * @param ... -1-terminated list of array offsets of type 'int'
282 : * @return
283 : * #GNUNET_YES if navigation was successful (caller is responsible
284 : * for freeing allocated variable-size data using
285 : * GNUNET_JSON_parse_free() when done)
286 : * #GNUNET_NO if json is malformed, error response was generated
287 : * #GNUNET_SYSERR on internal error
288 : */
289 : enum GNUNET_GenericReturnValue
290 0 : TALER_MHD_parse_json_array (struct MHD_Connection *connection,
291 : const json_t *root,
292 : struct GNUNET_JSON_Specification *spec,
293 : ...)
294 : {
295 : enum GNUNET_GenericReturnValue ret;
296 : const char *error_json_name;
297 : unsigned int error_line;
298 : va_list ap;
299 : json_int_t dim;
300 :
301 0 : va_start (ap, spec);
302 0 : dim = 0;
303 0 : while ( (-1 != (ret = va_arg (ap, int))) &&
304 : (NULL != root) )
305 : {
306 0 : dim++;
307 0 : root = json_array_get (root, ret);
308 : }
309 0 : va_end (ap);
310 0 : if (NULL == root)
311 : {
312 0 : ret = (MHD_YES ==
313 0 : TALER_MHD_reply_json_pack (
314 : connection,
315 : MHD_HTTP_BAD_REQUEST,
316 : "{s:s, s:I, s:s, s:I}",
317 : "hint", TALER_ErrorCode_get_hint (TALER_EC_GENERIC_JSON_INVALID),
318 : "code", (json_int_t) TALER_EC_GENERIC_JSON_INVALID,
319 : "detail", "expected array",
320 : "dimension", dim))
321 0 : ? GNUNET_NO : GNUNET_SYSERR;
322 0 : return ret;
323 : }
324 0 : ret = GNUNET_JSON_parse (root,
325 : spec,
326 : &error_json_name,
327 : &error_line);
328 0 : if (GNUNET_SYSERR == ret)
329 : {
330 0 : if (NULL == error_json_name)
331 0 : error_json_name = "<no field>";
332 0 : ret = (MHD_YES ==
333 0 : TALER_MHD_reply_json_pack (
334 : connection,
335 : MHD_HTTP_BAD_REQUEST,
336 : "{s:s, s:s, s:I, s:I}",
337 : "detail", error_json_name,
338 : "hint", TALER_ErrorCode_get_hint (TALER_EC_GENERIC_JSON_INVALID),
339 : "code", (json_int_t) TALER_EC_GENERIC_JSON_INVALID,
340 : "line", (json_int_t) error_line))
341 0 : ? GNUNET_NO : GNUNET_SYSERR;
342 0 : return ret;
343 : }
344 0 : return GNUNET_YES;
345 : }
346 :
347 :
348 : /* end of mhd_parsing.c */
|