From 4c1a2c030761fc64773d035df6cdd01db0204d13 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 4 May 2023 14:42:06 +0200 Subject: more shared logic for argument/header parsing --- src/mhd/mhd_parsing.c | 112 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 100 insertions(+), 12 deletions(-) (limited to 'src/mhd/mhd_parsing.c') diff --git a/src/mhd/mhd_parsing.c b/src/mhd/mhd_parsing.c index ee647f4b..e7645083 100644 --- a/src/mhd/mhd_parsing.c +++ b/src/mhd/mhd_parsing.c @@ -86,25 +86,40 @@ TALER_MHD_parse_post_cleanup_callback (void *con_cls) } -enum GNUNET_GenericReturnValue -TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection, - const char *param_name, - void *out_data, - size_t out_size) +/** + * Extract fixed-size base32crockford encoded data from request. + * + * Queues an error response to the connection if the parameter is missing or + * invalid. + * + * @param connection the MHD connection + * @param param_name the name of the HTTP key with the value + * @param kind whether to extract from header, argument or footer + * @param[out] out_data pointer to store the result + * @param out_size expected size of @a out_data + * @param[out] present set to true if argument was found + * @return + * #GNUNET_YES if the the argument is present + * #GNUNET_NO if the argument is absent or malformed + * #GNUNET_SYSERR on internal error (error response could not be sent) + */ +static enum GNUNET_GenericReturnValue +parse_request_data (struct MHD_Connection *connection, + const char *param_name, + enum MHD_ValueKind kind, + void *out_data, + size_t out_size, + bool *present) { const char *str; str = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, + kind, param_name); if (NULL == str) { - return (MHD_NO == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MISSING, - param_name)) - ? GNUNET_SYSERR : GNUNET_NO; + *present = false; + return GNUNET_OK; } if (GNUNET_OK != GNUNET_STRINGS_string_to_data (str, @@ -117,6 +132,79 @@ TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection, TALER_EC_GENERIC_PARAMETER_MALFORMED, param_name)) ? GNUNET_SYSERR : GNUNET_NO; + *present = true; + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection, + const char *param_name, + void *out_data, + size_t out_size, + bool *present) +{ + return parse_request_data (connection, + param_name, + MHD_GET_ARGUMENT_KIND, + out_data, + out_size, + present); +} + + +enum GNUNET_GenericReturnValue +TALER_MHD_parse_request_header_data (struct MHD_Connection *connection, + const char *header_name, + void *out_data, + size_t out_size, + bool *present) +{ + return parse_request_data (connection, + header_name, + MHD_HEADER_KIND, + out_data, + out_size, + present); +} + + +enum GNUNET_GenericReturnValue +TALER_MHD_parse_request_arg_timeout (struct MHD_Connection *connection, + struct GNUNET_TIME_Absolute *expiration) +{ + const char *ts; + char dummy; + unsigned long long tms; + + ts = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "timeout_ms"); + if (NULL == ts) + { + *expiration = GNUNET_TIME_UNIT_ZERO_ABS; + return GNUNET_OK; + } + if (1 != + sscanf (ts, + "%llu%c", + &tms, + &dummy)) + { + MHD_RESULT mret; + + GNUNET_break_op (0); + mret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "timeout_ms"); + return (MHD_YES == mret) + ? GNUNET_NO + : GNUNET_SYSERR; + } + *expiration = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, + tms)); return GNUNET_OK; } -- cgit v1.2.3 From 1f9427e1d9672b93577aea4c9d5a63575ee0b525 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 6 May 2023 19:43:17 +0200 Subject: add convenience function for content-length limiation --- contrib/gana | 2 +- src/exchange/taler-exchange-httpd.c | 29 ++---------------- src/include/taler_mhd_lib.h | 59 ++++++++++++++++++++++++++++++++----- src/mhd/mhd_parsing.c | 42 ++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 36 deletions(-) (limited to 'src/mhd/mhd_parsing.c') diff --git a/contrib/gana b/contrib/gana index 85736484..4654d82b 160000 --- a/contrib/gana +++ b/contrib/gana @@ -1 +1 @@ -Subproject commit 85736484cb0da26aded705ebb1e944e8bb1b8504 +Subproject commit 4654d82b143cd69dfe7a7bf2f816f6f91f6052e2 diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index ac3eae27..97cf54c8 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -1617,33 +1617,8 @@ handle_mhd_request (void *cls, if (0 == strcasecmp (method, MHD_HTTP_METHOD_POST)) { - const char *cl; - - /* Maybe check for maximum upload size - and refuse requests if they are just too big. */ - cl = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_CONTENT_LENGTH); - if (NULL != cl) - { - unsigned long long cv; - char dummy; - - if (1 != sscanf (cl, - "%llu%c", - &cv, - &dummy)) - { - /* Not valid HTTP request, just close connection. */ - GNUNET_break_op (0); - return MHD_NO; - } - if (cv > TALER_MHD_REQUEST_BUFFER_MAX) - { - GNUNET_break_op (0); - return TALER_MHD_reply_request_too_large (connection); - } - } + TALER_MHD_check_content_length (connection, + TALER_MHD_REQUEST_BUFFER_MAX); } } diff --git a/src/include/taler_mhd_lib.h b/src/include/taler_mhd_lib.h index 9c7c06d9..e4aa916e 100644 --- a/src/include/taler_mhd_lib.h +++ b/src/include/taler_mhd_lib.h @@ -596,14 +596,14 @@ TALER_MHD_parse_request_header_data (struct MHD_Connection *connection, bool p; \ switch (TALER_MHD_parse_request_header_data (connection, name, \ val, sizeof (*val), &p)) \ - { \ - case GNUNET_SYSERR: \ - GNUNET_break (0); \ - return MHD_NO; \ - case GNUNET_NO: \ - GNUNET_break_op (0); \ - return MHD_YES; \ - case GNUNET_OK: \ + { \ + case GNUNET_SYSERR: \ + GNUNET_break (0); \ + return MHD_NO; \ + case GNUNET_NO: \ + GNUNET_break_op (0); \ + return MHD_YES; \ + case GNUNET_OK: \ if (required & (! p)) \ return TALER_MHD_reply_with_error ( \ connection, \ @@ -634,6 +634,49 @@ TALER_MHD_parse_request_header_data (struct MHD_Connection *connection, } while (0) +/** + * Check that the 'Content-Length' header is giving + * a length below @a max_len. If not, return an + * appropriate error response and return the + * correct #MHD_YES/#MHD_NO value from this function. + * + * @param connection the MHD connection + * @param max_len maximum allowed content length + * @return + * #GNUNET_YES if the the argument is present + * #GNUNET_NO if the argument is absent or malformed + * #GNUNET_SYSERR on internal error (error response could not be sent) + */ +enum GNUNET_GenericReturnValue +TALER_MHD_check_content_length_ (struct MHD_Connection *connection, + unsigned long long max_len); + + +/** + * Check that the 'Content-Length' header is giving + * a length below @a max_len. If not, return an + * appropriate error response and return the + * correct #MHD_YES/#MHD_NO value from this function. + * + * @param connection the MHD connection + * @param max_len maximum allowed content length + */ +#define TALER_MHD_check_content_length(connection,max_len) \ + do { \ + switch (TALER_MHD_check_content_length_ (connection, max_len)) \ + { \ + case GNUNET_SYSERR: \ + GNUNET_break (0); \ + return MHD_NO; \ + case GNUNET_NO: \ + GNUNET_break_op (0); \ + return MHD_YES; \ + case GNUNET_OK: \ + break; \ + } \ + } while (0) + + /** * Parse the configuration to determine on which port * or UNIX domain path we should run an HTTP service. diff --git a/src/mhd/mhd_parsing.c b/src/mhd/mhd_parsing.c index e7645083..b047df7d 100644 --- a/src/mhd/mhd_parsing.c +++ b/src/mhd/mhd_parsing.c @@ -350,4 +350,46 @@ TALER_MHD_parse_json_array (struct MHD_Connection *connection, } +enum GNUNET_GenericReturnValue +TALER_MHD_check_content_length_ (struct MHD_Connection *connection, + unsigned long long max_len) +{ + const char *cl; + unsigned long long cv; + char dummy; + + /* Maybe check for maximum upload size + and refuse requests if they are just too big. */ + cl = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_CONTENT_LENGTH); + if (NULL == cl) + return GNUNET_OK; + if (1 != sscanf (cl, + "%llu%c", + &cv, + &dummy)) + { + /* Not valid HTTP request, just close connection. */ + GNUNET_break_op (0); + return (MHD_YES == + TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + MHD_HTTP_HEADER_CONTENT_LENGTH)) + ? GNUNET_NO + : GNUNET_SYSERR; + } + if (cv > TALER_MHD_REQUEST_BUFFER_MAX) + { + GNUNET_break_op (0); + return (MHD_YES == + TALER_MHD_reply_request_too_large (connection)) + ? GNUNET_NO + : GNUNET_SYSERR; + } + return GNUNET_OK; +} + + /* end of mhd_parsing.c */ -- cgit v1.2.3 From 404b2b78f187e3da2fedee5748b9bfcdfa4a105c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 6 May 2023 20:55:40 +0200 Subject: add convenience function TALER_TEMPLATING_reply_error --- src/include/taler_templating_lib.h | 20 +++++++++++++++++++ src/mhd/mhd_parsing.c | 11 ++++++++++- src/templating/templating_api.c | 39 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) (limited to 'src/mhd/mhd_parsing.c') diff --git a/src/include/taler_templating_lib.h b/src/include/taler_templating_lib.h index 53946b9c..ebda2ecf 100644 --- a/src/include/taler_templating_lib.h +++ b/src/include/taler_templating_lib.h @@ -91,6 +91,26 @@ TALER_TEMPLATING_reply (struct MHD_Connection *connection, const char *taler_uri, const json_t *root); + +/** + * Load a @a template and substitute an error message based on @a ec and @a + * detail, returning the result to the @a connection with the given @a + * http_status code. + * + * @param connection the connection we act upon + * @param template basename of the template to load + * @param http_status code to use on success + * @param ec error code to return + * @param detail optional text to add to the template + * @return #MHD_YES on success, #MHD_NO to just close the connection + */ +MHD_RESULT +TALER_TEMPLATING_reply_error (struct MHD_Connection *connection, + const char *template_basename, + unsigned int http_status, + enum TALER_ErrorCode ec, + const char *detail); + /** * Preload templates. * diff --git a/src/mhd/mhd_parsing.c b/src/mhd/mhd_parsing.c index b047df7d..9e3cb571 100644 --- a/src/mhd/mhd_parsing.c +++ b/src/mhd/mhd_parsing.c @@ -364,7 +364,16 @@ TALER_MHD_check_content_length_ (struct MHD_Connection *connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); if (NULL == cl) - return GNUNET_OK; + { + GNUNET_break_op (0); + return (MHD_YES == + TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MISSING, + MHD_HTTP_HEADER_CONTENT_LENGTH)) + ? GNUNET_NO + : GNUNET_SYSERR; + } if (1 != sscanf (cl, "%llu%c", &cv, diff --git a/src/templating/templating_api.c b/src/templating/templating_api.c index 324e199e..dba042e5 100644 --- a/src/templating/templating_api.c +++ b/src/templating/templating_api.c @@ -428,6 +428,45 @@ load_template (void *cls, } +MHD_RESULT +TALER_TEMPLATING_reply_error (struct MHD_Connection *connection, + const char *template_basename, + unsigned int http_status, + enum TALER_ErrorCode ec, + const char *detail) +{ + json_t *data; + enum GNUNET_GenericReturnValue ret; + + data = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_uint64 ("ec", + ec), + GNUNET_JSON_pack_string ("hint", + TALER_ErrorCode_get_hint (ec)), + GNUNET_JSON_pack_string ("detail", + detail) + ); + ret = TALER_TEMPLATING_reply (connection, + http_status, + template_basename, + NULL, + NULL, + data); + json_decref (data); + switch (ret) + { + case GNUNET_OK: + return MHD_YES; + case GNUNET_NO: + return MHD_YES; + case GNUNET_SYSERR: + return MHD_NO; + } + GNUNET_assert (0); + return MHD_NO; +} + + enum GNUNET_GenericReturnValue TALER_TEMPLATING_init (const char *subsystem) { -- cgit v1.2.3 From f009e0bd1261fe5b73ae7294faee3ac3e20ce802 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 9 May 2023 14:07:13 +0200 Subject: -make content-length optional again --- src/mhd/mhd_parsing.c | 4 ++++ src/testing/testing_api_helpers_bank.c | 23 +++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) (limited to 'src/mhd/mhd_parsing.c') diff --git a/src/mhd/mhd_parsing.c b/src/mhd/mhd_parsing.c index 9e3cb571..b1f8417e 100644 --- a/src/mhd/mhd_parsing.c +++ b/src/mhd/mhd_parsing.c @@ -365,6 +365,9 @@ TALER_MHD_check_content_length_ (struct MHD_Connection *connection, MHD_HTTP_HEADER_CONTENT_LENGTH); if (NULL == cl) { + return GNUNET_OK; +#if 0 + /* wallet currently doesn't always send content-length! */ GNUNET_break_op (0); return (MHD_YES == TALER_MHD_reply_with_error (connection, @@ -373,6 +376,7 @@ TALER_MHD_check_content_length_ (struct MHD_Connection *connection, MHD_HTTP_HEADER_CONTENT_LENGTH)) ? GNUNET_NO : GNUNET_SYSERR; +#endif } if (1 != sscanf (cl, "%llu%c", diff --git a/src/testing/testing_api_helpers_bank.c b/src/testing/testing_api_helpers_bank.c index 2507a87e..f2f92956 100644 --- a/src/testing/testing_api_helpers_bank.c +++ b/src/testing/testing_api_helpers_bank.c @@ -30,6 +30,10 @@ #define BANK_FAIL() \ do {GNUNET_break (0); return NULL; } while (0) +#define JDBC_TALERCHECK \ + "jdbc:postgresql://localhost/talercheck?socketFactory=org.newsclub.net.unix." \ + "AFUNIXSocketFactory$FactoryArg&socketFactoryArg" \ + "=/var/run/postgresql/.s.PGSQL.5432" struct TALER_FAKEBANK_Handle * TALER_TESTING_run_fakebank (const char *bank_url, @@ -95,7 +99,7 @@ TALER_TESTING_run_libeufin (const struct TALER_TESTING_BankConfiguration *bc) setenv ( "LIBEUFIN_NEXUS_DB_CONNECTION", - "jdbc:sqlite:/tmp/libeufin-exchange-test-nexusdb.sqlite3", + JDBC_TALERCHECK, 1); // not overwriting any potentially existing DB. nexus_proc = GNUNET_OS_start_process ( @@ -145,7 +149,7 @@ TALER_TESTING_run_libeufin (const struct TALER_TESTING_BankConfiguration *bc) fprintf (stderr, "\n"); setenv ( "LIBEUFIN_SANDBOX_DB_CONNECTION", - "jdbc:sqlite:/tmp/libeufin-exchange-test-sandboxdb.sqlite3", + JDBC_TALERCHECK, 1); // not overwriting any potentially existing DB. setenv ( "LIBEUFIN_SANDBOX_ADMIN_PASSWORD", @@ -366,7 +370,12 @@ TALER_TESTING_prepare_libeufin (const char *config_filename, /* DB preparation */ if (reset_db) { - if (0 != system ("rm -f /tmp/libeufin-exchange-test-nexusdb.sqlite3")) + setenv ( + "LIBEUFIN_NEXUS_DB_CONNECTION", + JDBC_TALERCHECK, + 1); // not overwriting any potentially existing DB. + + if (0 != system ("libeufin-nexus reset-tables")) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to invoke db-removal command on nexusdb.\n"); @@ -374,7 +383,13 @@ TALER_TESTING_prepare_libeufin (const char *config_filename, GNUNET_CONFIGURATION_destroy (cfg); return GNUNET_SYSERR; } - if (0 != system ("rm -f /tmp/libeufin-exchange-test-sandboxdb.sqlite3")) + + setenv ( + "LIBEUFIN_SANDBOX_DB_CONNECTION", + JDBC_TALERCHECK, + 1); // not overwriting any potentially existing DB. + + if (0 != system ("libeufin-sandbox reset-tables")) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to invoke db-removal command on sandboxdb.\n"); -- cgit v1.2.3