paivana

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

commit 93b195323d4bb3ce342fc716b654ef3f3cffeb9e
parent 35a9e030e0aabfc362918dc6c022e87bb3c1cb5d
Author: Christian Grothoff <grothoff@gnunet.org>
Date:   Tue,  5 May 2026 13:34:10 +0200

add whitelisting, global cookie setting, but generally restrict payment per path

Diffstat:
Msrc/backend/paivana-httpd.c | 39+++++++++++++++++++++++++++++++++++++++
Msrc/backend/paivana-httpd.h | 20++++++++++++++++++++
Msrc/backend/paivana-httpd_cookie.c | 30+++++++++++++++++++++++++++---
Msrc/backend/paivana-httpd_daemon.c | 8++++++++
Msrc/backend/paivana-httpd_pay.c | 4++--
Msrc/backend/paivana-httpd_pay.h | 23++++++++++++++++++++++-
6 files changed, 118 insertions(+), 6 deletions(-)

diff --git a/src/backend/paivana-httpd.c b/src/backend/paivana-httpd.c @@ -61,6 +61,12 @@ unsigned long long PH_request_buffer_max = 1024 * 1024; int PH_global_ret; +int PH_global_cookie; + +regex_t PH_whitelist_ex; + +bool PH_have_whitelist_ex; + /** * Our configuration. */ @@ -232,6 +238,33 @@ run (void *cls, GNUNET_free (merchant_unix_path); } } + { + char *whitelist; + + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string ( + c, + "paivana", + "WHITELIST", + &whitelist)) + { + if (0 != regcomp (&PH_whitelist_ex, + whitelist, + REG_NOSUB | REG_EXTENDED)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "paivana", + "WHITELIST", + "Invalid regular expression"); + GNUNET_free (whitelist); + GNUNET_SCHEDULER_shutdown (); + return; + } + PH_have_whitelist_ex = true; + GNUNET_free (whitelist); + } + } + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string ( c, @@ -341,6 +374,12 @@ main (int argc, "trust X-Forwarded-For for the client address (only safe behind a trusted reverse proxy)"), &PH_respect_forwarded_headers), GNUNET_GETOPT_option_flag ( + 'g', + "global-payment", + gettext_noop ( + "disables per-page payment, useful if a single payment should grant access to the entire site"), + &PH_global_cookie), + GNUNET_GETOPT_option_flag ( 'n', "no-payment", gettext_noop ( diff --git a/src/backend/paivana-httpd.h b/src/backend/paivana-httpd.h @@ -27,6 +27,9 @@ #ifndef PAIVANA_HTTPD_H #define PAIVANA_HTTPD_H +#include <regex.h> +#include <stdbool.h> + #define PAIVANA_LOG_INFO(...) \ GNUNET_log (GNUNET_ERROR_TYPE_INFO, __VA_ARGS__) #define PAIVANA_LOG_DEBUG(...) \ @@ -66,6 +69,23 @@ extern char *PH_base_url; extern struct GNUNET_CURL_Context *PH_ctx; /** + * Pre-compiled regular expression for sites that are whitelisted + * and never paywalled. + */ +extern regex_t PH_whitelist_ex; + +/** + * True if whitelist_ex was set. + */ +extern bool PH_have_whitelist_ex; + +/** + * Set to true if the cookie applies globally to all sites + * and not per-page. + */ +extern int PH_global_cookie; + +/** * Disable paywall check. */ extern int PH_no_check; diff --git a/src/backend/paivana-httpd_cookie.c b/src/backend/paivana-httpd_cookie.c @@ -29,6 +29,7 @@ #include <gnunet/gnunet_util_lib.h> #include <taler/taler_mhd_lib.h> #include "paivana-httpd_cookie.h" +#include "paivana-httpd.h" /** @@ -63,6 +64,8 @@ compute_cookie_hash (struct GNUNET_TIME_Timestamp cur_time, TALER_b2s (ca, ca_len)); e = GNUNET_TIME_absolute_hton (cur_time.abs_time); + if (PH_global_cookie) + website = ""; GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_hkdf_gnunet ( c, /* result */ @@ -71,6 +74,8 @@ compute_cookie_hash (struct GNUNET_TIME_Timestamp cur_time, sizeof (e), &paivana_secret, /* source key material */ sizeof (paivana_secret), + GNUNET_CRYPTO_kdf_arg (website, + strlen (website) + 1), GNUNET_CRYPTO_kdf_arg (ca, ca_len))); } @@ -148,7 +153,21 @@ PAIVANA_HTTPD_compute_cookie (struct GNUNET_TIME_Timestamp cur_time, char *end; char cstr[128]; char *res; - + const char *url = "/"; + bool use_https = strncasecmp (website, + "https://", + strlen ("https://")); + struct GNUNET_TIME_Relative duration + = GNUNET_TIME_absolute_get_remaining (cur_time.abs_time); + + if (! PH_global_cookie) + { + const char *dslash = strstr (website, + "//"); + if (NULL != dslash) + url = strchr (dslash + 2, + '/'); + } compute_cookie_hash (cur_time, website, ca_len, @@ -161,9 +180,14 @@ PAIVANA_HTTPD_compute_cookie (struct GNUNET_TIME_Timestamp cur_time, *end = '\0'; GNUNET_asprintf ( &res, - "Paivana-Cookie=%llu-%s; Secure; Path=/;", + "Paivana-Cookie=%llu-%s; %sPath=%s; Max-Age=%llu;", (unsigned long long) (cur_time.abs_time.abs_value_us / 1000LLU / 1000LLU), - cstr); + cstr, + use_https + ? "Secure; " + : "", + url, + (unsigned long long) (duration.rel_value_us / 1000 / 1000)); return res; } diff --git a/src/backend/paivana-httpd_daemon.c b/src/backend/paivana-httpd_daemon.c @@ -141,6 +141,14 @@ create_response (void *cls, upload_data_size); } + if (PH_have_whitelist_ex && (! rc->do_forward)) + { + rc->do_forward = (0 == + regcomp (&PH_whitelist_ex, + url, + REG_NOSUB | REG_EXTENDED)); + } + if (rc->do_forward) goto do_forward; diff --git a/src/backend/paivana-httpd_pay.c b/src/backend/paivana-httpd_pay.c @@ -93,7 +93,7 @@ struct PayRequest struct PAIVANA_Nonce nonce; /** - * + * Expiration time of the cookie. */ struct GNUNET_TIME_Timestamp cur_time; @@ -247,7 +247,7 @@ order_status_cb (struct PayRequest *ph, &ca, &ca_len)); cookie = PAIVANA_HTTPD_compute_cookie (ph->cur_time, - ph->website, + ph->website, ca_len, ca); GNUNET_log (GNUNET_ERROR_TYPE_INFO, diff --git a/src/backend/paivana-httpd_pay.h b/src/backend/paivana-httpd_pay.h @@ -36,20 +36,41 @@ */ struct PayRequest; +/** + * The server is shutting down, clean up suspended requests. + */ void PAIVANA_HTTPD_payment_shutdown (void); +/** + * Create context for handling a request to check a payment. + * + * @param connection the request context + */ struct PayRequest * PAIVANA_HTTPD_payment_create (struct MHD_Connection *connection); +/** + * Make progress on handling the upload of the client. + * + * @param[in,out] pr payment request handle + * @param upload_data data uploaded by the client + * @param[in,out] upload_data_size number of bytes uploaded, + * set to the number of bytes of upload that were not handled + * @return MHD status code to return + */ enum MHD_Result PAIVANA_HTTPD_payment_handle (struct PayRequest *pr, const char *upload_data, size_t *upload_data_size); - +/** + * Clean up payment request handle. + * + * @param[in] ph handle to clean up + */ void PAIVANA_HTTPD_payment_destroy (struct PayRequest *ph);