exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

mhd_config.c (14737B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014--2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file mhd_config.c
     18  * @brief functions to configure and setup MHD
     19  * @author Florian Dold
     20  * @author Benedikt Mueller
     21  * @author Christian Grothoff
     22  */
     23 #include "taler/platform.h"
     24 #include <gnunet/gnunet_util_lib.h>
     25 #include "taler/taler_mhd_lib.h"
     26 
     27 
     28 /**
     29  * Backlog for listen operation.
     30  */
     31 #define LISTEN_BACKLOG 500
     32 
     33 
     34 /**
     35  * Function called for logging by MHD.
     36  *
     37  * @param cls closure, NULL
     38  * @param fm format string (`printf()`-style)
     39  * @param ap arguments to @a fm
     40  */
     41 void
     42 TALER_MHD_handle_logs (void *cls,
     43                        const char *fm,
     44                        va_list ap)
     45 {
     46   static int cache;
     47   char buf[2048];
     48 
     49   (void) cls;
     50   if (-1 == cache)
     51     return;
     52   if (0 == cache)
     53   {
     54     if (0 ==
     55         GNUNET_get_log_call_status (GNUNET_ERROR_TYPE_INFO,
     56                                     "libmicrohttpd",
     57                                     __FILE__,
     58                                     __FUNCTION__,
     59                                     __LINE__))
     60     {
     61       cache = -1;
     62       return;
     63     }
     64   }
     65   cache = 1;
     66   vsnprintf (buf,
     67              sizeof (buf),
     68              fm,
     69              ap);
     70   GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_INFO,
     71                            "libmicrohttpd",
     72                            "%s",
     73                            buf);
     74 }
     75 
     76 
     77 /**
     78  * Open UNIX domain socket for listining at @a unix_path with
     79  * permissions @a unix_mode.
     80  *
     81  * @param unix_path where to listen
     82  * @param unix_mode access permissions to set
     83  * @return -1 on error, otherwise the listen socket
     84  */
     85 static int
     86 open_unix_path (const char *unix_path,
     87                 mode_t unix_mode)
     88 {
     89   struct GNUNET_NETWORK_Handle *nh;
     90   struct sockaddr_un *un;
     91 
     92   if (sizeof (un->sun_path) <= strlen (unix_path))
     93   {
     94     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     95                 "unixpath `%s' is too long\n",
     96                 unix_path);
     97     return -1;
     98   }
     99   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    100               "Creating listen socket '%s' with mode %o\n",
    101               unix_path,
    102               unix_mode);
    103 
    104   if (GNUNET_OK !=
    105       GNUNET_DISK_directory_create_for_file (unix_path))
    106   {
    107     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    108                               "mkdir",
    109                               unix_path);
    110   }
    111 
    112   un = GNUNET_new (struct sockaddr_un);
    113   un->sun_family = AF_UNIX;
    114   strncpy (un->sun_path,
    115            unix_path,
    116            sizeof (un->sun_path) - 1);
    117   GNUNET_NETWORK_unix_precheck (un);
    118 
    119   if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX,
    120                                                   SOCK_STREAM,
    121                                                   0)))
    122   {
    123     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    124                          "socket");
    125     GNUNET_free (un);
    126     return -1;
    127   }
    128 
    129   if (GNUNET_OK !=
    130       GNUNET_NETWORK_socket_bind (nh,
    131                                   (void *) un,
    132                                   sizeof (struct sockaddr_un)))
    133   {
    134     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    135                               "bind",
    136                               unix_path);
    137     GNUNET_free (un);
    138     GNUNET_NETWORK_socket_close (nh);
    139     return -1;
    140   }
    141   GNUNET_free (un);
    142   if (GNUNET_OK !=
    143       GNUNET_NETWORK_socket_listen (nh,
    144                                     LISTEN_BACKLOG))
    145   {
    146     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    147                          "listen");
    148     GNUNET_NETWORK_socket_close (nh);
    149     return -1;
    150   }
    151 
    152   if (0 != chmod (unix_path,
    153                   unix_mode))
    154   {
    155     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    156                          "chmod");
    157     GNUNET_NETWORK_socket_close (nh);
    158     return -1;
    159   }
    160   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    161               "set socket '%s' to mode %o\n",
    162               unix_path,
    163               unix_mode);
    164 
    165   /* extract and return actual socket handle from 'nh' */
    166   {
    167     int fd;
    168 
    169     fd = GNUNET_NETWORK_get_fd (nh);
    170     GNUNET_NETWORK_socket_free_memory_only_ (nh);
    171     return fd;
    172   }
    173 }
    174 
    175 
    176 enum GNUNET_GenericReturnValue
    177 TALER_MHD_listen_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
    178                        const char *section,
    179                        TALER_MHD_ListenSocketCallback cb,
    180                        void *cb_cls)
    181 {
    182   const char *choices[] = {
    183     "tcp",
    184     "unix",
    185     "systemd",
    186     NULL
    187   };
    188   const char *serve_type;
    189   enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
    190 
    191   if (GNUNET_OK !=
    192       GNUNET_CONFIGURATION_get_value_choice (cfg,
    193                                              section,
    194                                              "SERVE",
    195                                              choices,
    196                                              &serve_type))
    197   {
    198     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    199                                section,
    200                                "SERVE",
    201                                "serve type (tcp, unix or systemd) required");
    202     return GNUNET_SYSERR;
    203   }
    204 
    205 
    206   /* try systemd passing always first */
    207   {
    208     const char *listen_pid;
    209     const char *listen_fds;
    210     const char *listen_fdn;
    211 
    212     /* check for systemd-style FD passing */
    213     if ( (NULL != (listen_pid = getenv ("LISTEN_PID"))) &&
    214          (getpid () ==
    215           strtol (listen_pid,
    216                   NULL,
    217                   10)) &&
    218          (NULL != (listen_fds = getenv ("LISTEN_FDS"))) &&
    219          (NULL != (listen_fdn = getenv ("LISTEN_FDNAMES"))) )
    220     {
    221       int off = 3;
    222       unsigned int cnt;
    223       char dummy;
    224 
    225       if (0 != strcmp (serve_type,
    226                        "systemd"))
    227       {
    228         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    229                     "Using systemd activation due to environment variables. Set SERVE=systemd to disable this warning!\n");
    230       }
    231       else
    232       {
    233         ret = GNUNET_NO;
    234       }
    235       if (1 != sscanf (listen_fds,
    236                        "%u%c",
    237                        &cnt,
    238                        &dummy))
    239       {
    240         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    241                     "Invalid number `%s' of systemd sockets specified\n",
    242                     listen_fds);
    243       }
    244       else
    245       {
    246         char *fdns;
    247 
    248         fdns = GNUNET_strdup (listen_fdn);
    249         for (const char *tok = strtok (fdns,
    250                                        ":");
    251              cnt-- > 0;
    252              tok = strtok (NULL,
    253                            ":"))
    254         {
    255           int fh = off++;
    256           int flags;
    257 
    258           flags = fcntl (fh,
    259                          F_GETFD);
    260           if ( (-1 == flags) &&
    261                (EBADF == errno) )
    262           {
    263             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    264                         "Bad listen socket %s (%d) passed, ignored\n",
    265                         tok,
    266                         fh);
    267             continue;
    268           }
    269           flags |= FD_CLOEXEC;
    270           if (0 != fcntl (fh,
    271                           F_SETFD,
    272                           flags))
    273             GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    274                                  "fcntl");
    275           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    276                       "Successfully obtained listen socket %s (#%d) from hypervisor\n",
    277                       tok,
    278                       fh);
    279           cb (cb_cls,
    280               fh);
    281           ret = GNUNET_OK;
    282         }
    283         GNUNET_free (fdns);
    284       }
    285     }
    286   }
    287 
    288   /* now try configuration file */
    289   if (0 == strcmp (serve_type,
    290                    "unix"))
    291   {
    292     char *serve_unixpath;
    293     mode_t unixpath_mode;
    294     struct sockaddr_un s_un;
    295     char *modestring;
    296 
    297     if (GNUNET_OK !=
    298         GNUNET_CONFIGURATION_get_value_filename (cfg,
    299                                                  section,
    300                                                  "UNIXPATH",
    301                                                  &serve_unixpath))
    302     {
    303       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    304                                  section,
    305                                  "UNIXPATH",
    306                                  "UNIXPATH value required");
    307       return GNUNET_SYSERR;
    308     }
    309     if (strlen (serve_unixpath) >= sizeof (s_un.sun_path))
    310     {
    311       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    312                   "unixpath `%s' is too long\n",
    313                   serve_unixpath);
    314       GNUNET_free (serve_unixpath);
    315       return GNUNET_SYSERR;
    316     }
    317 
    318     if (GNUNET_OK !=
    319         GNUNET_CONFIGURATION_get_value_string (cfg,
    320                                                section,
    321                                                "UNIXPATH_MODE",
    322                                                &modestring))
    323     {
    324       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    325                                  section,
    326                                  "UNIXPATH_MODE");
    327       GNUNET_free (serve_unixpath);
    328       return GNUNET_SYSERR;
    329     }
    330     errno = 0;
    331     unixpath_mode = (mode_t) strtoul (modestring,
    332                                       NULL,
    333                                       8);
    334     if (0 != errno)
    335     {
    336       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    337                                  section,
    338                                  "UNIXPATH_MODE",
    339                                  "must be octal number");
    340       GNUNET_free (modestring);
    341       GNUNET_free (serve_unixpath);
    342       return GNUNET_SYSERR;
    343     }
    344     GNUNET_free (modestring);
    345 
    346     {
    347       int fd;
    348 
    349       fd = open_unix_path (serve_unixpath,
    350                            unixpath_mode);
    351       GNUNET_free (serve_unixpath);
    352       if (-1 == fd)
    353         return GNUNET_NO;
    354       cb (cb_cls,
    355           fd);
    356       return GNUNET_OK;
    357     }
    358   }
    359 
    360   if (0 == strcasecmp (serve_type,
    361                        "tcp"))
    362   {
    363     unsigned long long lport;
    364     struct GNUNET_NETWORK_Handle *nh;
    365     char *bind_to;
    366 
    367     if (GNUNET_OK !=
    368         GNUNET_CONFIGURATION_get_value_number (cfg,
    369                                                section,
    370                                                "PORT",
    371                                                &lport))
    372     {
    373       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    374                                  section,
    375                                  "PORT",
    376                                  "port number required");
    377       return GNUNET_SYSERR;
    378     }
    379 
    380     if ( (0 == lport) ||
    381          (lport > UINT16_MAX) )
    382     {
    383       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    384                                  section,
    385                                  "PORT",
    386                                  "port number not in [1,65535]");
    387       return GNUNET_SYSERR;
    388     }
    389 
    390     if (GNUNET_OK !=
    391         GNUNET_CONFIGURATION_get_value_string (cfg,
    392                                                section,
    393                                                "BIND_TO",
    394                                                &bind_to))
    395       bind_to = NULL;
    396 
    397     /* let's have fun binding... */
    398     {
    399       char port_str[6];
    400       struct addrinfo hints;
    401       struct addrinfo *res;
    402       int ec;
    403 
    404       GNUNET_snprintf (port_str,
    405                        sizeof (port_str),
    406                        "%u",
    407                        (unsigned int) lport);
    408       memset (&hints,
    409               0,
    410               sizeof (hints));
    411       hints.ai_family = AF_UNSPEC;
    412       hints.ai_socktype = SOCK_STREAM;
    413       hints.ai_protocol = IPPROTO_TCP;
    414       hints.ai_flags = AI_PASSIVE
    415 #ifdef AI_IDN
    416                        | AI_IDN
    417 #endif
    418       ;
    419 
    420       if (0 !=
    421           (ec = getaddrinfo (bind_to,
    422                              port_str,
    423                              &hints,
    424                              &res)))
    425       {
    426         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    427                     "Failed to resolve BIND_TO address `%s': %s\n",
    428                     bind_to,
    429                     gai_strerror (ec));
    430         GNUNET_free (bind_to);
    431         return GNUNET_SYSERR;
    432       }
    433       GNUNET_free (bind_to);
    434       for (struct addrinfo *ai = res;
    435            NULL != ai;
    436            ai = ai->ai_next)
    437       {
    438         if (NULL == (nh = GNUNET_NETWORK_socket_create (ai->ai_family,
    439                                                         ai->ai_socktype,
    440                                                         ai->ai_protocol)))
    441         {
    442           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    443                                "socket");
    444           freeaddrinfo (res);
    445           return GNUNET_NO;
    446         }
    447         {
    448           const int on = 1;
    449 
    450           if (GNUNET_OK !=
    451               GNUNET_NETWORK_socket_setsockopt (nh,
    452                                                 SOL_SOCKET,
    453                                                 SO_REUSEPORT,
    454                                                 &on,
    455                                                 sizeof(on)))
    456             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    457                                  "setsockopt");
    458         }
    459         if (GNUNET_OK !=
    460             GNUNET_NETWORK_socket_bind (nh,
    461                                         ai->ai_addr,
    462                                         ai->ai_addrlen))
    463         {
    464           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    465                                "bind");
    466           GNUNET_break (GNUNET_OK ==
    467                         GNUNET_NETWORK_socket_close (nh));
    468           freeaddrinfo (res);
    469           return GNUNET_NO;
    470         }
    471 
    472         if (GNUNET_OK !=
    473             GNUNET_NETWORK_socket_listen (nh,
    474                                           LISTEN_BACKLOG))
    475         {
    476           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    477                                "listen");
    478           GNUNET_SCHEDULER_shutdown ();
    479           GNUNET_break (GNUNET_OK ==
    480                         GNUNET_NETWORK_socket_close (nh));
    481           freeaddrinfo (res);
    482           return GNUNET_NO;
    483         }
    484 
    485         /* extract and return actual socket handle from 'nh' */
    486         {
    487           int fh;
    488 
    489           fh = GNUNET_NETWORK_get_fd (nh);
    490           GNUNET_NETWORK_socket_free_memory_only_ (nh);
    491           if (-1 == fh)
    492           {
    493             GNUNET_break (0);
    494             return GNUNET_NO;
    495           }
    496           cb (cb_cls,
    497               fh);
    498         }
    499       } /* for all addrinfos */
    500       freeaddrinfo (res);
    501       return GNUNET_OK;
    502     } /* bind data scope */
    503   } /* tcp */
    504   return ret;
    505 }