123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- /*
- This file is part of GNUnet
- Copyright (C) 2014, 2015, 2016 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file json/json_mhd.c
- * @brief functions to parse JSON snippets we receive via MHD
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
- #include "platform.h"
- #include "gnunet_json_lib.h"
- #include <zlib.h>
- /**
- * Initial size for POST request buffers. Should be big enough to
- * usually not require a reallocation, but not so big that it hurts in
- * terms of memory use.
- */
- #define REQUEST_BUFFER_INITIAL (2 * 1024)
- /**
- * Buffer for POST requests.
- */
- struct Buffer
- {
- /**
- * Allocated memory
- */
- char *data;
- /**
- * Number of valid bytes in buffer.
- */
- size_t fill;
- /**
- * Number of allocated bytes in buffer.
- */
- size_t alloc;
- /**
- * Maximum buffer size allowed.
- */
- size_t max;
- };
- /**
- * Initialize a buffer.
- *
- * @param buf the buffer to initialize
- * @param data the initial data
- * @param data_size size of the initial data
- * @param alloc_size size of the buffer
- * @param max_size maximum size that the buffer can grow to
- * @return a GNUnet result code
- */
- static int
- buffer_init (struct Buffer *buf,
- const void *data,
- size_t data_size,
- size_t alloc_size,
- size_t max_size)
- {
- if ((data_size > max_size) || (alloc_size > max_size))
- return GNUNET_SYSERR;
- if (data_size > alloc_size)
- alloc_size = data_size;
- buf->data = GNUNET_malloc (alloc_size);
- buf->alloc = alloc_size;
- GNUNET_memcpy (buf->data, data, data_size);
- buf->fill = data_size;
- buf->max = max_size;
- return GNUNET_OK;
- }
- /**
- * Free the data in a buffer. Does *not* free
- * the buffer object itself.
- *
- * @param buf buffer to de-initialize
- */
- static void
- buffer_deinit (struct Buffer *buf)
- {
- GNUNET_free (buf->data);
- buf->data = NULL;
- }
- /**
- * Append data to a buffer, growing the buffer if necessary.
- *
- * @param buf the buffer to append to
- * @param data the data to append
- * @param data_size the size of @a data
- * @param max_size maximum size that the buffer can grow to
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if the buffer can't accomodate for the new data
- */
- static int
- buffer_append (struct Buffer *buf,
- const void *data,
- size_t data_size,
- size_t max_size)
- {
- if (buf->fill + data_size > max_size)
- return GNUNET_NO;
- if (buf->fill + data_size > buf->alloc)
- {
- char *new_buf;
- size_t new_size = buf->alloc;
- while (new_size < buf->fill + data_size)
- new_size += 2;
- if (new_size > max_size)
- return GNUNET_NO;
- new_buf = GNUNET_malloc (new_size);
- GNUNET_memcpy (new_buf, buf->data, buf->fill);
- GNUNET_free (buf->data);
- buf->data = new_buf;
- buf->alloc = new_size;
- }
- GNUNET_memcpy (buf->data + buf->fill, data, data_size);
- buf->fill += data_size;
- return GNUNET_OK;
- }
- /**
- * Decompress data in @a buf.
- *
- * @param buf input data to inflate
- * @return result code indicating the status of the operation
- */
- static enum GNUNET_JSON_PostResult
- inflate_data (struct Buffer *buf)
- {
- z_stream z;
- char *tmp;
- size_t tmp_size;
- int ret;
- memset (&z, 0, sizeof(z));
- z.next_in = (Bytef *) buf->data;
- z.avail_in = buf->fill;
- tmp_size = GNUNET_MIN (buf->max, buf->fill * 4);
- tmp = GNUNET_malloc (tmp_size);
- z.next_out = (Bytef *) tmp;
- z.avail_out = tmp_size;
- ret = inflateInit (&z);
- switch (ret)
- {
- case Z_MEM_ERROR:
- GNUNET_break (0);
- return GNUNET_JSON_PR_OUT_OF_MEMORY;
- case Z_STREAM_ERROR:
- GNUNET_break_op (0);
- return GNUNET_JSON_PR_JSON_INVALID;
- case Z_OK:
- break;
- }
- while (1)
- {
- ret = inflate (&z, 0);
- switch (ret)
- {
- case Z_MEM_ERROR:
- GNUNET_break (0);
- GNUNET_break (Z_OK == inflateEnd (&z));
- GNUNET_free (tmp);
- return GNUNET_JSON_PR_OUT_OF_MEMORY;
- case Z_DATA_ERROR:
- GNUNET_break (0);
- GNUNET_break (Z_OK == inflateEnd (&z));
- GNUNET_free (tmp);
- return GNUNET_JSON_PR_JSON_INVALID;
- case Z_NEED_DICT:
- GNUNET_break (0);
- GNUNET_break (Z_OK == inflateEnd (&z));
- GNUNET_free (tmp);
- return GNUNET_JSON_PR_JSON_INVALID;
- case Z_OK:
- if ((0 < z.avail_out) && (0 == z.avail_in))
- {
- /* truncated input stream */
- GNUNET_break (0);
- GNUNET_break (Z_OK == inflateEnd (&z));
- GNUNET_free (tmp);
- return GNUNET_JSON_PR_JSON_INVALID;
- }
- if (0 < z.avail_out)
- continue; /* just call it again */
- /* output buffer full, can we grow it? */
- if (tmp_size == buf->max)
- {
- /* already at max */
- GNUNET_break (0);
- GNUNET_break (Z_OK == inflateEnd (&z));
- GNUNET_free (tmp);
- return GNUNET_JSON_PR_OUT_OF_MEMORY;
- }
- if (tmp_size * 2 < tmp_size)
- tmp_size = buf->max;
- else
- tmp_size = GNUNET_MIN (buf->max, tmp_size * 2);
- tmp = GNUNET_realloc (tmp, tmp_size);
- z.next_out = (Bytef *) &tmp[z.total_out];
- continue;
- case Z_STREAM_END:
- /* decompression successful, make 'tmp' the new 'data' */
- GNUNET_free (buf->data);
- buf->data = tmp;
- buf->alloc = tmp_size;
- buf->fill = z.total_out;
- GNUNET_break (Z_OK == inflateEnd (&z));
- return GNUNET_JSON_PR_SUCCESS; /* at least for now */
- }
- } /* while (1) */
- }
- /**
- * Process a POST request containing a JSON object. This function
- * realizes an MHD POST processor that will (incrementally) process
- * JSON data uploaded to the HTTP server. It will store the required
- * state in the @a con_cls, which must be cleaned up using
- * #GNUNET_JSON_post_parser_callback().
- *
- * @param buffer_max maximum allowed size for the buffer
- * @param connection MHD connection handle (for meta data about the upload)
- * @param con_cls the closure (will point to a `struct Buffer *`)
- * @param upload_data the POST data
- * @param upload_data_size number of bytes in @a upload_data
- * @param json the JSON object for a completed request
- * @return result code indicating the status of the operation
- */
- enum GNUNET_JSON_PostResult
- GNUNET_JSON_post_parser (size_t buffer_max,
- struct MHD_Connection *connection,
- void **con_cls,
- const char *upload_data,
- size_t *upload_data_size,
- json_t **json)
- {
- struct Buffer *r = *con_cls;
- const char *ce;
- int ret;
- *json = NULL;
- if (NULL == *con_cls)
- {
- /* We are seeing a fresh POST request. */
- r = GNUNET_new (struct Buffer);
- if (GNUNET_OK != buffer_init (r,
- upload_data,
- *upload_data_size,
- REQUEST_BUFFER_INITIAL,
- buffer_max))
- {
- *con_cls = NULL;
- buffer_deinit (r);
- GNUNET_free (r);
- return GNUNET_JSON_PR_OUT_OF_MEMORY;
- }
- /* everything OK, wait for more POST data */
- *upload_data_size = 0;
- *con_cls = r;
- return GNUNET_JSON_PR_CONTINUE;
- }
- if (0 != *upload_data_size)
- {
- /* We are seeing an old request with more data available. */
- if (GNUNET_OK !=
- buffer_append (r, upload_data, *upload_data_size, buffer_max))
- {
- /* Request too long */
- *con_cls = NULL;
- buffer_deinit (r);
- GNUNET_free (r);
- return GNUNET_JSON_PR_REQUEST_TOO_LARGE;
- }
- /* everything OK, wait for more POST data */
- *upload_data_size = 0;
- return GNUNET_JSON_PR_CONTINUE;
- }
- /* We have seen the whole request. */
- ce = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_CONTENT_ENCODING);
- if ((NULL != ce) && (0 == strcasecmp ("deflate", ce)))
- {
- ret = inflate_data (r);
- if (GNUNET_JSON_PR_SUCCESS != ret)
- {
- buffer_deinit (r);
- GNUNET_free (r);
- *con_cls = NULL;
- return ret;
- }
- }
- *json = json_loadb (r->data, r->fill, 0, NULL);
- if (NULL == *json)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to parse JSON request body\n");
- buffer_deinit (r);
- GNUNET_free (r);
- *con_cls = NULL;
- return GNUNET_JSON_PR_JSON_INVALID;
- }
- buffer_deinit (r);
- GNUNET_free (r);
- *con_cls = NULL;
- return GNUNET_JSON_PR_SUCCESS;
- }
- /**
- * Function called whenever we are done with a request
- * to clean up our state.
- *
- * @param con_cls value as it was left by
- * #GNUNET_JSON_post_parser(), to be cleaned up
- */
- void
- GNUNET_JSON_post_parser_cleanup (void *con_cls)
- {
- struct Buffer *r = con_cls;
- if (NULL != r)
- {
- buffer_deinit (r);
- GNUNET_free (r);
- }
- }
- /* end of mhd_json.c */
|