paivana

HTTP paywall reverse proxy
Log | Files | Refs | README | LICENSE

commit c935fd0f80fb5758f3d28f42fbc5d3d70950e3bd
parent 5bf3e1ef50add1654ac0da42ca67412b4c6d39a9
Author: Christian Grothoff <christian@grothoff.org>
Date:   Thu, 16 Apr 2026 09:33:32 +0200

use libtalermhd to remove code duplication

Diffstat:
Mconfigure.ac | 11-----------
Msrc/backend/Makefile.am | 1+
Msrc/backend/paivana-httpd.c | 469++++++++++++++++---------------------------------------------------------------
3 files changed, 92 insertions(+), 389 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -305,17 +305,6 @@ AC_ARG_ENABLE(linker-hardening, fi]) -# logging -extra_logging=0 -AC_ARG_ENABLE([logging], - AS_HELP_STRING([--enable-logging@<:@=value@:>@],[Enable logging calls. Possible values: yes,no,verbose ('yes' is the default)]), - [AS_IF([test "x$enableval" = "xyes"], [], - [test "x$enableval" = "xno"], [AC_DEFINE([GNUNET_CULL_LOGGING],[],[Define to cull all logging calls])], - [test "x$enableval" = "xverbose"], [extra_logging=1] - [test "x$enableval" = "xveryverbose"], [extra_logging=2]) - ], []) -AC_DEFINE_UNQUOTED([GNUNET_EXTRA_LOGGING],[$extra_logging],[1 if extra logging is enabled, 2 for very verbose extra logging, 0 otherwise]) - # gcov compilation AC_MSG_CHECKING(whether to compile with support for code coverage analysis) AC_ARG_ENABLE([coverage], diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am @@ -14,6 +14,7 @@ paivana_httpd_SOURCES = \ paivana_pd.c paivana_pd.h paivana_httpd_LDADD = \ $(LIBGCRYPT_LIBS) \ + -ltalermhd \ -lmicrohttpd \ -lcurl \ -ljansson \ diff --git a/src/backend/paivana-httpd.c b/src/backend/paivana-httpd.c @@ -27,14 +27,11 @@ * @brief HTTP proxy that acts as a GNU Taler paywall */ #include "platform.h" -#include <microhttpd.h> #include <curl/curl.h> #include <gnunet/gnunet_util_lib.h> -#include <microhttpd.h> +#include <taler/taler_mhd_lib.h> #include "paivana_pd.h" -typedef enum MHD_Result MHD_RESULT; - #define PAIVANA_LOG_INFO(...) \ GNUNET_log (GNUNET_ERROR_TYPE_INFO, __VA_ARGS__) #define PAIVANA_LOG_DEBUG(...) \ @@ -258,9 +255,9 @@ static struct HttpRequest *hr_tail; static CURLM *curl_multi; /** - * The daemon handle + * Set to true if we started a daemon. */ -static struct MHD_Daemon *mhd_daemon; +static bool have_daemons; /** * Static paywall response. @@ -268,11 +265,6 @@ static struct MHD_Daemon *mhd_daemon; static struct MHD_Response *paywall; /** - * The task ID - */ -static struct GNUNET_SCHEDULER_Task *httpd_task; - -/** * Response we return on cURL failures. */ static struct MHD_Response *curl_failure_response; @@ -288,6 +280,11 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg; static int no_check; /** + * Value to return from main() + */ +static int global_ret; + +/** * Destination to which HTTP server we forward requests to. * Of the format "http://servername:PORT" */ @@ -850,7 +847,7 @@ curl_download_prepare () * @param value field value * @return #MHD_YES to continue to iterate */ -static MHD_RESULT +static enum MHD_Result con_val_iter (void *cls, enum MHD_ValueKind kind, const char *key, @@ -1022,7 +1019,7 @@ curl_task_download (void *cls) * #MHD_NO if the socket must be closed due to a serious * error while handling the request */ -static MHD_RESULT +static enum MHD_Result create_response (void *cls, struct MHD_Connection *con, const char *url, @@ -1466,130 +1463,12 @@ mhd_log_callback (void *cls, /** - * Kill the MHD daemon. - */ -static void -kill_httpd (void) -{ - MHD_stop_daemon (mhd_daemon); - mhd_daemon = NULL; - if (NULL != httpd_task) - { - GNUNET_SCHEDULER_cancel (httpd_task); - httpd_task = NULL; - } -} - - -/** - * Task run whenever HTTP server operations are pending. - * - * @param cls the `struct MhdHttpList *` - * of the daemon that is being run - */ -static void -do_httpd (void *cls); - - -/** - * Schedule MHD. This function should be called initially when an - * MHD is first getting its client socket, and will then - * automatically always be called later whenever there is work to - * be done. - */ -static void -schedule_httpd (void) -{ - fd_set rs; - fd_set ws; - fd_set es; - struct GNUNET_NETWORK_FDSet *wrs; - struct GNUNET_NETWORK_FDSet *wws; - int max; - int haveto; - MHD_UNSIGNED_LONG_LONG timeout; - struct GNUNET_TIME_Relative tv; - - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - max = -1; - if (MHD_YES != - MHD_get_fdset (mhd_daemon, - &rs, - &ws, - &es, - &max)) - { - kill_httpd (); - return; - } - haveto = MHD_get_timeout (mhd_daemon, - &timeout); - if (MHD_YES == haveto) - tv.rel_value_us = (uint64_t) timeout * 1000LL; - else - tv = GNUNET_TIME_UNIT_FOREVER_REL; - if (-1 != max) - { - wrs = GNUNET_NETWORK_fdset_create (); - wws = GNUNET_NETWORK_fdset_create (); - GNUNET_NETWORK_fdset_copy_native (wrs, - &rs, - max + 1); - GNUNET_NETWORK_fdset_copy_native (wws, - &ws, - max + 1); - } - else - { - wrs = NULL; - wws = NULL; - } - if (NULL != httpd_task) - { - GNUNET_SCHEDULER_cancel (httpd_task); - httpd_task = NULL; - } - httpd_task = - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - tv, - wrs, - wws, - &do_httpd, - NULL); - if (NULL != wrs) - GNUNET_NETWORK_fdset_destroy (wrs); - if (NULL != wws) - GNUNET_NETWORK_fdset_destroy (wws); -} - - -/** - * Task run whenever HTTP server operations are pending. - * - * @param cls NULL - */ -static void -do_httpd (void *cls) -{ - (void) cls; - httpd_task = NULL; - MHD_run (mhd_daemon); - schedule_httpd (); -} - - -/** * Run MHD now, we have extra data ready for the callback. */ static void run_mhd_now (void) { - if (NULL != httpd_task) - GNUNET_SCHEDULER_cancel (httpd_task); - httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, - NULL); + TALER_MHD_daemon_trigger (); } @@ -1607,6 +1486,7 @@ do_shutdown (void *cls) (void) cls; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down...\n"); + TALER_MHD_daemons_halt (); /* MHD requires resuming before destroying the daemons */ for (struct HttpRequest *hr = hr_head; NULL != hr; @@ -1618,7 +1498,7 @@ do_shutdown (void *cls) MHD_resume_connection (hr->con); } } - kill_httpd (); + TALER_MHD_daemons_destroy (); if (NULL != curl_multi) { curl_multi_cleanup (curl_multi); @@ -1634,202 +1514,10 @@ do_shutdown (void *cls) /** - * Connect to a unix domain socket. - * - * @param path the IPC path - * @param mode the IPC path mode - * @return the file descriptor of the connection. - */ -static int -open_unix_path (const char *path, - mode_t mode) -{ - - struct GNUNET_NETWORK_Handle *nh; - struct sockaddr_un *un; - int fd; - - if (sizeof (un->sun_path) <= strlen (path)) - { - fprintf (stderr, - "path `%s' too long\n", - path); - return -1; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Creating listen socket '%s' with mode %o\n", - path, - mode); - - if (GNUNET_OK != - GNUNET_DISK_directory_create_for_file (path)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "mkdir", - path); - } - - un = GNUNET_new (struct sockaddr_un); - un->sun_family = AF_UNIX; - - strncpy (un->sun_path, - path, - sizeof (un->sun_path) - 1); - GNUNET_NETWORK_unix_precheck (un); - - if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, - SOCK_STREAM, - 0))) - { - fprintf (stderr, - "create failed for AF_UNIX\n"); - GNUNET_free (un); - return -1; - } - if (GNUNET_OK != - GNUNET_NETWORK_socket_bind (nh, - (void *) un, - sizeof (struct sockaddr_un))) - { - fprintf (stderr, - "bind failed for AF_UNIX\n"); - GNUNET_free (un); - GNUNET_NETWORK_socket_close (nh); - return -1; - } - GNUNET_free (un); - if (GNUNET_OK != - GNUNET_NETWORK_socket_listen (nh, - UNIX_BACKLOG)) - { - fprintf (stderr, - "listen failed for AF_UNIX\n"); - GNUNET_NETWORK_socket_close (nh); - return -1; - } - - if (0 != chmod (path, - mode)) - { - fprintf (stderr, - "chmod failed: %s\n", - strerror (errno)); - GNUNET_NETWORK_socket_close (nh); - return -1; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "set socket '%s' to mode %o\n", - path, - mode); - fd = GNUNET_NETWORK_get_fd (nh); - GNUNET_NETWORK_socket_free_memory_only_ (nh); - return fd; -} - - -/** - * Crawl the configuration file and extracts the serving - * method, TCP vs IPC, and the respective details (port/path/mode) - * - * @param ccfg configuration handle. - * @param port[out] port number to use - * @param path[out] unix path for IPC. - * @param mode[out] mode string for @a path. - * @return #GNUNET_SYSERR if the parsing didn't succeed. - */ -// FIXME: replace by helper function of Taler? -static enum GNUNET_GenericReturnValue -parse_serving_mean (const struct GNUNET_CONFIGURATION_Handle *ccfg, - uint16_t *port, - char **path, - mode_t *mode) -{ - - const char *serve; - const char *choices[] = {"tcp", "unix", NULL}; - char *modestring; - unsigned long long port_ull; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_choice (ccfg, - "paivana", - "SERVE", - choices, - &serve)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "paivana", - "SERVE"); - return GNUNET_SYSERR; - } - - if (0 == strcmp ("tcp", serve)) - { - *path = NULL; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (ccfg, - "paivana", - "HTTP_PORT", - &port_ull)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "paivana", - "HTTP_PORT"); - return GNUNET_SYSERR; - } - *port = (uint16_t) port_ull; - return GNUNET_OK; - } - - /* serving via unix */ - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (ccfg, - "paivana", - "SERVE_UNIXPATH", - path)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "paivana", - "SERVE_UNIXPATH"); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (ccfg, - "paivana", - "SERVE_UNIXMODE", - &modestring)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "paivana", - "SERVE_UNIXMODE"); - return GNUNET_SYSERR; - } - - errno = 0; - *mode = (mode_t) strtoul (modestring, NULL, 8); - - if (0 != errno) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "paivana", - "SERVE_UNIXMODE", - "must be octal number"); - GNUNET_free (modestring); - return GNUNET_SYSERR; - } - GNUNET_free (modestring); - return GNUNET_OK; -} - - -/** * Try to initialize the paywall response. */ static bool -load_paywall () +load_paywall (void) { char *tpath; char *fn; @@ -1877,6 +1565,46 @@ load_paywall () /** + * Callback invoked on every listen socket to start the + * respective MHD HTTP daemon. + * + * @param cls unused + * @param lsock the listen socket + */ +static void +start_daemon (void *cls, + int lsock) +{ + struct MHD_Daemon *mhd; + + (void) cls; + GNUNET_assert (-1 != lsock); + mhd = MHD_start_daemon ( + MHD_USE_DEBUG + | MHD_ALLOW_SUSPEND_RESUME + | MHD_USE_DUAL_STACK, + 0, + NULL, NULL, + &create_response, NULL, + MHD_OPTION_LISTEN_SOCKET, + lsock, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16, + MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL, + MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL, + MHD_OPTION_END); + + if (NULL == mhd) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + have_daemons = true; + TALER_MHD_daemon_start (mhd); +} + + +/** * Main function that will be run. Main tasks are (1) init. the * curl infrastructure (curl_global_init() / curl_multi_init()), * then fetch the HTTP port where its Web service should listen at, @@ -1893,18 +1621,13 @@ run (void *cls, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *c) { - uint16_t port = 0; - int fh = -1; - char *serve_unixpath = NULL; - mode_t serve_unixmode = 0; + enum GNUNET_GenericReturnValue ret; char *secret; (void) cls; (void) args; (void) cfgfile; cfg = c; - // FIXME: initialize database logic! - if (0 != curl_global_init (CURL_GLOBAL_WIN32)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -1986,48 +1709,32 @@ run (void *cls, &paivana_secret); GNUNET_free (secret); } + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); - if (GNUNET_SYSERR == - parse_serving_mean (c, - &port, - &serve_unixpath, - &serve_unixmode)) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - - if (NULL != serve_unixpath) - { - /* Connect the 'fh' socket. */ - fh = open_unix_path (serve_unixpath, - serve_unixmode); - - GNUNET_assert (-1 != fh); - } - - /* start MHD daemon for HTTP */ - mhd_daemon = MHD_start_daemon - (MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME | MHD_USE_DUAL_STACK, - (-1 == fh) ? (uint16_t) port : 0, - NULL, NULL, - &create_response, NULL, - MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16, - MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL, - MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL, - MHD_OPTION_LISTEN_SOCKET, fh, - MHD_OPTION_END); - - if (NULL == mhd_daemon) + ret = TALER_MHD_listen_bind (c, + "paivana", + &start_daemon, + NULL); + switch (ret) { - GNUNET_break (0); + case GNUNET_SYSERR: + global_ret = EXIT_NOTCONFIGURED; GNUNET_SCHEDULER_shutdown (); return; + case GNUNET_NO: + if (! have_daemons) + { + global_ret = EXIT_NOTCONFIGURED; + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Could not open all configured listen sockets\n"); + break; + case GNUNET_OK: + break; } - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, - NULL); - run_mhd_now (); } @@ -2047,15 +1754,21 @@ main (int argc, &no_check), GNUNET_GETOPT_OPTION_END }; - - return GNUNET_PROGRAM_run (PAIVANA_project_data (), - argc, - argv, - "paivana-httpd", - "reverse proxy requesting Taler payment", - options, - &run, - NULL); + enum GNUNET_GenericReturnValue ret; + + ret = GNUNET_PROGRAM_run ( + PAIVANA_project_data (), + argc, + argv, + "paivana-httpd", + "reverse proxy requesting Taler payment", + options, + &run, NULL); + if (GNUNET_SYSERR == ret) + return EXIT_INVALIDARGUMENT; + if (GNUNET_NO == ret) + return EXIT_SUCCESS; + return global_ret; }