paivana

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

commit 7c29dd278d79fe3b3819ab0fe0172624cb675cf1
parent 356875a4f7393584ad4f90165bfdcf4bd0c69fed
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 20 Apr 2026 15:09:23 +0200

address various FIXMEs

Diffstat:
Msrc/backend/Makefile.am | 2++
Msrc/backend/paivana-httpd.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/backend/paivana-httpd.h | 8+++++++-
Msrc/backend/paivana-httpd_pay.c | 66+++++++++++++++++++++---------------------------------------------
4 files changed, 98 insertions(+), 68 deletions(-)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am @@ -12,6 +12,7 @@ bin_PROGRAMS = \ paivana_httpd_SOURCES = \ paivana-httpd.c \ paivana-httpd_cookie.c paivana-httpd_cookie.h \ + paivana-httpd_helper.c paivana-httpd_helper.h \ paivana-httpd_reverse.c paivana-httpd_reverse.h \ paivana-httpd_pay.c paivana-httpd_pay.h \ paivana_pd.c paivana_pd.h @@ -19,6 +20,7 @@ paivana_httpd_LDADD = \ $(LIBGCRYPT_LIBS) \ -ltalermerchant \ -ltalermhd \ + -ltalerutil \ -lgnunetjson \ -lgnunetcurl \ -lgnunetutil \ diff --git a/src/backend/paivana-httpd.c b/src/backend/paivana-httpd.c @@ -33,6 +33,7 @@ #include <taler/taler_mhd_lib.h> #include "paivana-httpd.h" #include "paivana-httpd_cookie.h" +#include "paivana-httpd_helper.h" #include "paivana-httpd_pay.h" #include "paivana-httpd_reverse.h" #include "paivana_pd.h" @@ -77,6 +78,8 @@ char *PH_target_server_base_url; char *PH_merchant_base_url; +char *PH_base_url; + struct GNUNET_CURL_Context *PH_ctx; @@ -194,36 +197,34 @@ create_response (void *cls, "Paivana-Cookie"); if (NULL != cookie) { - const union MHD_ConnectionInfo *ci; - const struct sockaddr *ca; - socklen_t ca_len; + void *ca; + size_t ca_len; + struct GNUNET_Buffer buf; + char *website; bool ok; - // FIXME: de-duplicate with logic in paivana-httpd_pay.c, - // and also support getting client address from HTTP - // headers instead (in case of reverse proxy). - ci = MHD_get_connection_info (con, - MHD_CONNECTION_INFO_CLIENT_ADDRESS); - GNUNET_assert (NULL != ci); - ca = ci->client_addr; - switch (ca->sa_family) + GNUNET_break (PAIVANA_HTTPD_get_client_address (con, + &ca, + &ca_len)); + if (! PAIVANA_HTTPD_get_base_url (con, + &buf)) { - case AF_INET: - ca_len = sizeof (struct sockaddr_in); - break; - case AF_INET6: - ca_len = sizeof (struct sockaddr_in6); - break; - default: GNUNET_break (0); - ca_len = 0; - break; + return TALER_MHD_reply_with_error ( + con, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, + "Host or X-Forwarded-Host required"); } - /* FIXME: url vs. full path to the website? Which should we use? */ + GNUNET_buffer_write_str (&buf, + url); + website = GNUNET_buffer_reap_str (&buf); ok = PAIVANA_HTTPD_check_cookie (cookie, - url, + website, ca_len, ca); + GNUNET_free (website); + GNUNET_free (ca); if (! ok) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -327,6 +328,7 @@ do_shutdown (void *cls) TALER_MHD_daemons_destroy (); GNUNET_free (PH_target_server_base_url); GNUNET_free (PH_merchant_base_url); + GNUNET_free (PH_base_url); if (NULL != PH_ctx) { GNUNET_CURL_fini (PH_ctx); @@ -482,6 +484,15 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } + if (! TALER_is_web_url (PH_target_server_base_url)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "paivana", + "DESTINATION_BASE_URL", + "not a web url"); + GNUNET_SCHEDULER_shutdown (); + return; + } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string ( c, @@ -495,6 +506,41 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } + if (! TALER_is_web_url (PH_merchant_base_url)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "paivana", + "MERCHANT_BACKEND_URL", + "not a web url"); + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string ( + c, + "paivana", + "BASE_URL", + &PH_base_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, + "paivana", + "BASE_URL"); + } + if (NULL != PH_base_url) + { + if (! TALER_is_web_url (PH_base_url)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "paivana", + "BASE_URL", + "not a web url"); + GNUNET_SCHEDULER_shutdown (); + return; + } + if ('/' == PH_base_url[strlen (PH_base_url) - 1]) + PH_base_url[strlen (PH_base_url) - 1] = '\0'; + } + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string ( c, diff --git a/src/backend/paivana-httpd.h b/src/backend/paivana-httpd.h @@ -48,9 +48,15 @@ extern char *PH_target_server_base_url; extern char *PH_merchant_base_url; /** + * Base URL of this site as seen by the client. If not set, + * we will try to determine it from "X-Forwarded-Host" and + * "Host" and "X-Forwarded-Port" headers. + */ +extern char *PH_base_url; + +/** * Curl context for making HTTP requests. */ extern struct GNUNET_CURL_Context *PH_ctx; - #endif diff --git a/src/backend/paivana-httpd_pay.c b/src/backend/paivana-httpd_pay.c @@ -23,11 +23,12 @@ * @file paivana-httpd_pay.c * @brief payment processing logic */ -#include <gnunet/gnunet_util_lib.h> #include <microhttpd.h> +#include <gnunet/gnunet_util_lib.h> #include <taler/taler_mhd_lib.h> #include <taler/taler_error_codes.h> #include "paivana-httpd_cookie.h" +#include "paivana-httpd_helper.h" #include "paivana-httpd_pay.h" struct PayRequest; @@ -159,16 +160,14 @@ check_contract (struct PayRequest *ph, { struct GNUNET_TIME_Timestamp max_time = GNUNET_TIME_UNIT_FOREVER_TS; - const char *regex_s = NULL; - // FIXME: expand Paivana contract template specifications with - // these fields! + const char *target = NULL; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("website_regex", - &regex_s), + GNUNET_JSON_spec_string ("fulfillment_url", + &target), NULL), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("max_time", + GNUNET_JSON_spec_timestamp ("max_pickup_time", &max_time), NULL), GNUNET_JSON_spec_end () @@ -189,28 +188,22 @@ check_contract (struct PayRequest *ph, eline); return true; } - if (NULL != regex_s) + if ( (NULL != target) && + (0 != strcmp (target, + ph->website)) ) { - bool match = true; - - // FIXME: check ph->website matches regex. - // If not: => TALER_EC_PAIVANA_WRONG_ORDER with 409! - if (! match) - { - GNUNET_break_op (0); - ph->response = TALER_MHD_make_error (TALER_EC_PAIVANA_WRONG_ORDER, - ph->order_id); - ph->response_status = MHD_HTTP_CONFLICT; - return false; - } + GNUNET_break_op (0); + ph->response = TALER_MHD_make_error (TALER_EC_PAIVANA_WRONG_ORDER, + ph->order_id); + ph->response_status = MHD_HTTP_CONFLICT; + return false; } if (GNUNET_TIME_timestamp_cmp (ph->cur_time, >, max_time)) { GNUNET_break_op (0); - // FIXME: bad EC! Introduce new one! - ph->response = TALER_MHD_make_error (TALER_EC_PAIVANA_WRONG_ORDER, + ph->response = TALER_MHD_make_error (TALER_EC_PAIVANA_TOO_LATE, ph->order_id); ph->response_status = MHD_HTTP_GONE; return false; @@ -242,39 +235,22 @@ order_status_cb (struct PayRequest *ph, } else { - const union MHD_ConnectionInfo *ci; - const struct sockaddr *ca; - socklen_t ca_len; + void *ca; + size_t ca_len; char *cookie; struct MHD_Response *resp; - // FIXME: de-duplicate with logic in paivana-httpd.c, - // and also support getting client address from HTTP - // headers instead (in case of reverse proxy). - ci = MHD_get_connection_info (ph->connection, - MHD_CONNECTION_INFO_CLIENT_ADDRESS); - GNUNET_assert (NULL != ci); - ca = ci->client_addr; - switch (ca->sa_family) - { - case AF_INET: - ca_len = sizeof (struct sockaddr_in); - break; - case AF_INET6: - ca_len = sizeof (struct sockaddr_in6); - break; - default: - GNUNET_break (0); - ca_len = 0; - break; - } if (! check_contract (ph, osr->details.ok.details.paid.contract_terms)) return; + GNUNET_break (PAIVANA_HTTPD_get_client_address (ph->connection, + &ca, + &ca_len)); cookie = PAIVANA_HTTPD_compute_cookie (ph->cur_time, ph->website, ca_len, ca); + GNUNET_free (ca); resp = MHD_create_response_from_buffer (0, NULL, MHD_RESPMEM_PERSISTENT);