paivana

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

paivana-httpd.c (9035B)


      1 /*
      2   This file is part of GNU Taler
      3   Copyright (C) 2012-2014 GNUnet e.V.
      4   Copyright (C) 2018, 2025, 2026 Taler Systems SA
      5 
      6   GNU Taler is free software; you can redistribute it and/or
      7   modify it under the terms of the GNU General Public License
      8   as published by the Free Software Foundation; either version
      9   3, or (at your option) any later version.
     10 
     11   GNU Taler is distributed in the hope that it will be useful, but
     12   WITHOUT ANY WARRANTY; without even the implied warranty of
     13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14   GNU General Public License for more details.
     15 
     16   You should have received a copy of the GNU General Public
     17   License along with GNU Taler; see the file COPYING.  If not,
     18   write to the Free Software Foundation, Inc., 51 Franklin
     19   Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @author Martin Schanzenbach
     24  * @author Christian Grothoff
     25  * @author Marcello Stanisci
     26  * @file src/backend/paivana-httpd.c
     27  * @brief HTTP proxy that acts as a GNU Taler paywall
     28  */
     29 #include "platform.h"
     30 #include <curl/curl.h>
     31 #include <gnunet/gnunet_util_lib.h>
     32 #include <gnunet/gnunet_curl_lib.h>
     33 #include <taler/taler_mhd_lib.h>
     34 #include <taler/taler_templating_lib.h>
     35 #include "paivana-httpd.h"
     36 #include "paivana-httpd_cookie.h"
     37 #include "paivana-httpd_daemon.h"
     38 #include "paivana-httpd_helper.h"
     39 #include "paivana-httpd_pay.h"
     40 #include "paivana-httpd_reverse.h"
     41 #include "paivana-httpd_templates.h"
     42 #include "paivana_pd.h"
     43 
     44 
     45 char *PH_target_server_base_url;
     46 
     47 char *PH_merchant_base_url;
     48 
     49 char *PH_base_url;
     50 
     51 struct GNUNET_CURL_Context *PH_ctx;
     52 
     53 int PH_no_check;
     54 
     55 unsigned long long PH_request_buffer_max = 1024 * 1024;
     56 
     57 int PH_global_ret;
     58 
     59 /**
     60  * Our configuration.
     61  */
     62 const struct GNUNET_CONFIGURATION_Handle *PH_cfg;
     63 
     64 
     65 /**
     66  * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
     67  */
     68 static struct GNUNET_CURL_RescheduleContext *ctx_rc;
     69 
     70 
     71 /* *************** General / main code *************** */
     72 
     73 
     74 /**
     75  * Task run on shutdown
     76  *
     77  * @param cls closure
     78  */
     79 static void
     80 do_shutdown (void *cls)
     81 {
     82   (void) cls;
     83   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     84               "Shutting down...\n");
     85   TALER_MHD_daemons_halt ();
     86   PAIVANA_HTTPD_payment_shutdown ();
     87   PAIVANA_HTTPD_reverse_shutdown ();
     88   TALER_MHD_daemons_destroy ();
     89   PAIVANA_HTTPD_unload_templates ();
     90   TALER_TEMPLATING_done ();
     91   GNUNET_free (PH_target_server_base_url);
     92   GNUNET_free (PH_merchant_base_url);
     93   GNUNET_free (PH_base_url);
     94   if (NULL != PH_ctx)
     95   {
     96     GNUNET_CURL_fini (PH_ctx);
     97     PH_ctx = NULL;
     98   }
     99   if (NULL != ctx_rc)
    100   {
    101     GNUNET_CURL_gnunet_rc_destroy (ctx_rc);
    102     ctx_rc = NULL;
    103   }
    104 }
    105 
    106 
    107 /**
    108  * Main function that will be run.  Main tasks are (1) init. the
    109  * curl infrastructure (curl_global_init() / curl_multi_init()),
    110  * then fetch the HTTP port where its Web service should listen at,
    111  * and finally start MHD on that port.
    112  *
    113  * @param cls closure
    114  * @param args remaining command-line arguments
    115  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    116  * @param c configuration
    117  */
    118 static void
    119 run (void *cls,
    120      char *const *args,
    121      const char *cfgfile,
    122      const struct GNUNET_CONFIGURATION_Handle *c)
    123 {
    124   char *secret;
    125 
    126   (void) cls;
    127   (void) args;
    128   (void) cfgfile;
    129   PH_cfg = c;
    130 
    131   if (! PH_no_check)
    132   {
    133     if (GNUNET_OK !=
    134         TALER_TEMPLATING_init (PAIVANA_project_data ()))
    135     {
    136       GNUNET_break (0);
    137       GNUNET_SCHEDULER_shutdown ();
    138       return;
    139     }
    140   }
    141   if (! PAIVANA_HTTPD_reverse_init ())
    142   {
    143     GNUNET_break (0);
    144     GNUNET_SCHEDULER_shutdown ();
    145     return;
    146   }
    147 
    148   /* No need to check return value.  If given, we take,
    149    * otherwise it stays zero.  */
    150   if (GNUNET_OK !=
    151       GNUNET_CONFIGURATION_get_value_string (
    152         c,
    153         "paivana",
    154         "DESTINATION_BASE_URL",
    155         &PH_target_server_base_url))
    156   {
    157     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    158                                "paivana",
    159                                "DESTINATION_BASE_URL");
    160     GNUNET_SCHEDULER_shutdown ();
    161     return;
    162   }
    163   if (! TALER_is_web_url (PH_target_server_base_url))
    164   {
    165     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    166                                "paivana",
    167                                "DESTINATION_BASE_URL",
    168                                "not a web url");
    169     GNUNET_SCHEDULER_shutdown ();
    170     return;
    171   }
    172   {
    173     size_t tlen = strlen (PH_target_server_base_url);
    174 
    175     if ( (tlen > 0) &&
    176          ('/' == PH_target_server_base_url[tlen - 1]) )
    177       PH_target_server_base_url[tlen - 1] = '\0';
    178   }
    179   if (! PH_no_check)
    180   {
    181     if (GNUNET_OK !=
    182         GNUNET_CONFIGURATION_get_value_string (
    183           c,
    184           "paivana",
    185           "MERCHANT_BACKEND_URL",
    186           &PH_merchant_base_url))
    187     {
    188       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    189                                  "paivana",
    190                                  "MERCHANT_BACKEND_URL");
    191       GNUNET_SCHEDULER_shutdown ();
    192       return;
    193     }
    194     if (! TALER_is_web_url (PH_merchant_base_url))
    195     {
    196       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    197                                  "paivana",
    198                                  "MERCHANT_BACKEND_URL",
    199                                  "not a web url");
    200       GNUNET_SCHEDULER_shutdown ();
    201       return;
    202     }
    203   }
    204   if (GNUNET_OK !=
    205       GNUNET_CONFIGURATION_get_value_string (
    206         c,
    207         "paivana",
    208         "BASE_URL",
    209         &PH_base_url))
    210   {
    211     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
    212                                "paivana",
    213                                "BASE_URL");
    214   }
    215   if (NULL != PH_base_url)
    216   {
    217     if (! TALER_is_web_url (PH_base_url))
    218     {
    219       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    220                                  "paivana",
    221                                  "BASE_URL",
    222                                  "not a web url");
    223       GNUNET_SCHEDULER_shutdown ();
    224       return;
    225     }
    226     if ('/' == PH_base_url[strlen (PH_base_url) - 1])
    227       PH_base_url[strlen (PH_base_url) - 1] = '\0';
    228   }
    229 
    230   if (GNUNET_OK !=
    231       GNUNET_CONFIGURATION_get_value_string (
    232         c,
    233         "paivana",
    234         "SECRET",
    235         &secret))
    236   {
    237     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    238                                "paivana",
    239                                "SECRET");
    240     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
    241                                 &paivana_secret,
    242                                 sizeof (paivana_secret));
    243   }
    244   else
    245   {
    246     GNUNET_CRYPTO_hash (secret,
    247                         strlen (secret),
    248                         &paivana_secret);
    249     GNUNET_free (secret);
    250   }
    251   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    252                                  NULL);
    253   PH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    254                              &ctx_rc);
    255   if (! PH_no_check)
    256   {
    257     char *merchant_access_token;
    258     char *auth_header;
    259 
    260     if (GNUNET_OK !=
    261         GNUNET_CONFIGURATION_get_value_string (
    262           c,
    263           "paivana",
    264           "MERCHANT_ACCESS_TOKEN",
    265           &merchant_access_token))
    266     {
    267       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    268                                  "paivana",
    269                                  "MERCHANT_ACCESS_TOKEN");
    270       GNUNET_SCHEDULER_shutdown ();
    271       return;
    272     }
    273     GNUNET_asprintf (&auth_header,
    274                      "%s: Bearer %s",
    275                      MHD_HTTP_HEADER_AUTHORIZATION,
    276                      merchant_access_token);
    277     GNUNET_free (merchant_access_token);
    278     GNUNET_assert (GNUNET_OK ==
    279                    GNUNET_CURL_append_header (PH_ctx,
    280                                               auth_header));
    281     GNUNET_free (auth_header);
    282   }
    283   ctx_rc = GNUNET_CURL_gnunet_rc_create (PH_ctx);
    284   /* Once templates are done loading, this will
    285      start the daemon as well.  In -n (no-payment) mode we skip
    286      the merchant round-trip entirely. */
    287   if (PH_no_check)
    288   {
    289     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    290                 "Paywall disabled (-n), skipping template load\n");
    291     PAIVANA_HTTPD_serve_requests ();
    292     return;
    293   }
    294   PAIVANA_HTTPD_load_templates ();
    295 }
    296 
    297 
    298 /**
    299  * Main function.
    300  */
    301 int
    302 main (int argc,
    303       char *const *argv)
    304 {
    305   struct GNUNET_GETOPT_CommandLineOption options[] = {
    306     GNUNET_GETOPT_option_flag (
    307       'n',
    308       "no-payment",
    309       gettext_noop (
    310         "disables payment, useful for testing reverse-proxy only"),
    311       &PH_no_check),
    312     GNUNET_GETOPT_option_ulong (
    313       'u',
    314       "max-upload",
    315       "BYTES",
    316       gettext_noop (
    317         "maximum request body size to buffer before forwarding (default: 1048576)"),
    318       &PH_request_buffer_max),
    319     GNUNET_GETOPT_OPTION_END
    320   };
    321   enum GNUNET_GenericReturnValue ret;
    322 
    323   ret = GNUNET_PROGRAM_run (
    324     PAIVANA_project_data (),
    325     argc,
    326     argv,
    327     "paivana-httpd",
    328     "reverse proxy requesting Taler payment",
    329     options,
    330     &run, NULL);
    331   if (GNUNET_SYSERR == ret)
    332     return EXIT_INVALIDARGUMENT;
    333   if (GNUNET_NO == ret)
    334     return EXIT_SUCCESS;
    335   return PH_global_ret;
    336 }
    337 
    338 
    339 /* end of paivana-httpd.c */