aboutsummaryrefslogtreecommitdiff
path: root/src/lib/json/json_mhd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/json/json_mhd.c')
-rw-r--r--src/lib/json/json_mhd.c379
1 files changed, 379 insertions, 0 deletions
diff --git a/src/lib/json/json_mhd.c b/src/lib/json/json_mhd.c
new file mode 100644
index 000000000..0b0fa0538
--- /dev/null
+++ b/src/lib/json/json_mhd.c
@@ -0,0 +1,379 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file json/json_mhd.c
22 * @brief functions to parse JSON snippets we receive via MHD
23 * @author Florian Dold
24 * @author Benedikt Mueller
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_json_lib.h"
29#include <zlib.h>
30
31
32/**
33 * Initial size for POST request buffers. Should be big enough to
34 * usually not require a reallocation, but not so big that it hurts in
35 * terms of memory use.
36 */
37#define REQUEST_BUFFER_INITIAL (2 * 1024)
38
39
40/**
41 * Buffer for POST requests.
42 */
43struct Buffer
44{
45 /**
46 * Allocated memory
47 */
48 char *data;
49
50 /**
51 * Number of valid bytes in buffer.
52 */
53 size_t fill;
54
55 /**
56 * Number of allocated bytes in buffer.
57 */
58 size_t alloc;
59
60 /**
61 * Maximum buffer size allowed.
62 */
63 size_t max;
64};
65
66
67/**
68 * Initialize a buffer.
69 *
70 * @param buf the buffer to initialize
71 * @param data the initial data
72 * @param data_size size of the initial data
73 * @param alloc_size size of the buffer
74 * @param max_size maximum size that the buffer can grow to
75 * @return a GNUnet result code
76 */
77static int
78buffer_init (struct Buffer *buf,
79 const void *data,
80 size_t data_size,
81 size_t alloc_size,
82 size_t max_size)
83{
84 if ((data_size > max_size) || (alloc_size > max_size))
85 return GNUNET_SYSERR;
86 if (data_size > alloc_size)
87 alloc_size = data_size;
88 buf->data = GNUNET_malloc (alloc_size);
89 buf->alloc = alloc_size;
90 GNUNET_memcpy (buf->data, data, data_size);
91 buf->fill = data_size;
92 buf->max = max_size;
93 return GNUNET_OK;
94}
95
96
97/**
98 * Free the data in a buffer. Does *not* free
99 * the buffer object itself.
100 *
101 * @param buf buffer to de-initialize
102 */
103static void
104buffer_deinit (struct Buffer *buf)
105{
106 GNUNET_free (buf->data);
107 buf->data = NULL;
108}
109
110
111/**
112 * Append data to a buffer, growing the buffer if necessary.
113 *
114 * @param buf the buffer to append to
115 * @param data the data to append
116 * @param data_size the size of @a data
117 * @param max_size maximum size that the buffer can grow to
118 * @return #GNUNET_OK on success,
119 * #GNUNET_NO if the buffer can't accommodate for the new data
120 */
121static int
122buffer_append (struct Buffer *buf,
123 const void *data,
124 size_t data_size,
125 size_t max_size)
126{
127 if (buf->fill + data_size > max_size)
128 return GNUNET_NO;
129 if (buf->fill + data_size > buf->alloc)
130 {
131 char *new_buf;
132 size_t new_size = buf->alloc;
133 while (new_size < buf->fill + data_size)
134 new_size += 2;
135 if (new_size > max_size)
136 return GNUNET_NO;
137 new_buf = GNUNET_malloc (new_size);
138 GNUNET_memcpy (new_buf, buf->data, buf->fill);
139 GNUNET_free (buf->data);
140 buf->data = new_buf;
141 buf->alloc = new_size;
142 }
143 GNUNET_memcpy (buf->data + buf->fill, data, data_size);
144 buf->fill += data_size;
145 return GNUNET_OK;
146}
147
148
149/**
150 * Decompress data in @a buf.
151 *
152 * @param buf input data to inflate
153 * @return result code indicating the status of the operation
154 */
155static enum GNUNET_JSON_PostResult
156inflate_data (struct Buffer *buf)
157{
158 z_stream z;
159 char *tmp;
160 size_t tmp_size;
161 int ret;
162
163 memset (&z, 0, sizeof(z));
164 z.next_in = (Bytef *) buf->data;
165 z.avail_in = buf->fill;
166 tmp_size = GNUNET_MIN (buf->max, buf->fill * 4);
167 tmp = GNUNET_malloc (tmp_size);
168 z.next_out = (Bytef *) tmp;
169 z.avail_out = tmp_size;
170 ret = inflateInit (&z);
171 switch (ret)
172 {
173 case Z_MEM_ERROR:
174 GNUNET_break (0);
175 return GNUNET_JSON_PR_OUT_OF_MEMORY;
176
177 case Z_STREAM_ERROR:
178 GNUNET_break_op (0);
179 return GNUNET_JSON_PR_JSON_INVALID;
180
181 case Z_OK:
182 break;
183 }
184 while (1)
185 {
186 ret = inflate (&z, 0);
187 switch (ret)
188 {
189 case Z_BUF_ERROR:
190 GNUNET_break_op (0);
191 GNUNET_break (Z_OK == inflateEnd (&z));
192 GNUNET_free (tmp);
193 return GNUNET_JSON_PR_JSON_INVALID;
194 case Z_MEM_ERROR:
195 GNUNET_break (0);
196 GNUNET_break (Z_OK == inflateEnd (&z));
197 GNUNET_free (tmp);
198 return GNUNET_JSON_PR_OUT_OF_MEMORY;
199 case Z_DATA_ERROR:
200 GNUNET_break_op (0);
201 GNUNET_break (Z_OK == inflateEnd (&z));
202 GNUNET_free (tmp);
203 return GNUNET_JSON_PR_JSON_INVALID;
204 case Z_NEED_DICT:
205 GNUNET_break_op (0);
206 GNUNET_break (Z_OK == inflateEnd (&z));
207 GNUNET_free (tmp);
208 return GNUNET_JSON_PR_JSON_INVALID;
209 case Z_OK:
210 if ((0 < z.avail_out) && (0 == z.avail_in))
211 {
212 /* truncated input stream */
213 GNUNET_break (0);
214 GNUNET_break (Z_OK == inflateEnd (&z));
215 GNUNET_free (tmp);
216 return GNUNET_JSON_PR_JSON_INVALID;
217 }
218 if (0 < z.avail_out)
219 continue; /* just call it again */
220 /* output buffer full, can we grow it? */
221 if (tmp_size == buf->max)
222 {
223 /* already at max */
224 GNUNET_break (0);
225 GNUNET_break (Z_OK == inflateEnd (&z));
226 GNUNET_free (tmp);
227 return GNUNET_JSON_PR_OUT_OF_MEMORY;
228 }
229 if (tmp_size * 2 < tmp_size)
230 tmp_size = buf->max;
231 else
232 tmp_size = GNUNET_MIN (buf->max, tmp_size * 2);
233 tmp = GNUNET_realloc (tmp, tmp_size);
234 z.next_out = (Bytef *) &tmp[z.total_out];
235 continue;
236 case Z_STREAM_END:
237 /* decompression successful, make 'tmp' the new 'data' */
238 GNUNET_free (buf->data);
239 buf->data = tmp;
240 buf->alloc = tmp_size;
241 buf->fill = z.total_out;
242 GNUNET_break (Z_OK == inflateEnd (&z));
243 return GNUNET_JSON_PR_SUCCESS; /* at least for now */
244 }
245 } /* while (1) */
246}
247
248
249/**
250 * Process a POST request containing a JSON object. This function
251 * realizes an MHD POST processor that will (incrementally) process
252 * JSON data uploaded to the HTTP server. It will store the required
253 * state in the @a con_cls, which must be cleaned up using
254 * #GNUNET_JSON_post_parser_callback().
255 *
256 * @param buffer_max maximum allowed size for the buffer
257 * @param connection MHD connection handle (for meta data about the upload)
258 * @param con_cls the closure (will point to a `struct Buffer *`)
259 * @param upload_data the POST data
260 * @param upload_data_size number of bytes in @a upload_data
261 * @param json the JSON object for a completed request
262 * @return result code indicating the status of the operation
263 */
264enum GNUNET_JSON_PostResult
265GNUNET_JSON_post_parser (size_t buffer_max,
266 struct MHD_Connection *connection,
267 void **con_cls,
268 const char *upload_data,
269 size_t *upload_data_size,
270 json_t **json)
271{
272 struct Buffer *r = *con_cls;
273 const char *ce;
274 int ret;
275
276 *json = NULL;
277 if (NULL == *con_cls)
278 {
279 /* We are seeing a fresh POST request. */
280 r = GNUNET_new (struct Buffer);
281 if (GNUNET_OK != buffer_init (r,
282 upload_data,
283 *upload_data_size,
284 REQUEST_BUFFER_INITIAL,
285 buffer_max))
286 {
287 *con_cls = NULL;
288 buffer_deinit (r);
289 GNUNET_free (r);
290 return GNUNET_JSON_PR_OUT_OF_MEMORY;
291 }
292 /* everything OK, wait for more POST data */
293 *upload_data_size = 0;
294 *con_cls = r;
295 return GNUNET_JSON_PR_CONTINUE;
296 }
297 if (0 != *upload_data_size)
298 {
299 /* We are seeing an old request with more data available. */
300
301 if (GNUNET_OK !=
302 buffer_append (r, upload_data, *upload_data_size, buffer_max))
303 {
304 /* Request too long */
305 *con_cls = NULL;
306 buffer_deinit (r);
307 GNUNET_free (r);
308 return GNUNET_JSON_PR_REQUEST_TOO_LARGE;
309 }
310 /* everything OK, wait for more POST data */
311 *upload_data_size = 0;
312 return GNUNET_JSON_PR_CONTINUE;
313 }
314
315 /* We have seen the whole request. */
316 ce = MHD_lookup_connection_value (connection,
317 MHD_HEADER_KIND,
318 MHD_HTTP_HEADER_CONTENT_ENCODING);
319 if ((NULL != ce) && (0 == strcasecmp ("deflate", ce)))
320 {
321 ret = inflate_data (r);
322 if (GNUNET_JSON_PR_SUCCESS != ret)
323 {
324 buffer_deinit (r);
325 GNUNET_free (r);
326 *con_cls = NULL;
327 return ret;
328 }
329 }
330
331 {
332 json_error_t err;
333
334 *json = json_loadb (r->data,
335 r->fill,
336 0,
337 &err);
338 if (NULL == *json)
339 {
340 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
341 "Failed to parse JSON request body of %u byte at offset %d: %s\n",
342 (unsigned int) r->fill,
343 err.position,
344 err.text);
345 buffer_deinit (r);
346 GNUNET_free (r);
347 *con_cls = NULL;
348 return GNUNET_JSON_PR_JSON_INVALID;
349 }
350 }
351 buffer_deinit (r);
352 GNUNET_free (r);
353 *con_cls = NULL;
354
355 return GNUNET_JSON_PR_SUCCESS;
356}
357
358
359/**
360 * Function called whenever we are done with a request
361 * to clean up our state.
362 *
363 * @param con_cls value as it was left by
364 * #GNUNET_JSON_post_parser(), to be cleaned up
365 */
366void
367GNUNET_JSON_post_parser_cleanup (void *con_cls)
368{
369 struct Buffer *r = con_cls;
370
371 if (NULL != r)
372 {
373 buffer_deinit (r);
374 GNUNET_free (r);
375 }
376}
377
378
379/* end of mhd_json.c */