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 (15100B)


      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 "platform.h"  /* UNNECESSARY? */
     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     {
    331       char *endp;
    332       unsigned long mode;
    333 
    334       errno = 0;
    335       mode = strtoul (modestring,
    336                       &endp,
    337                       8);
    338       /* strtoul() only sets errno on overflow, so we must
    339          also check that we actually consumed the entire value */
    340       if ( (0 != errno) ||
    341            (endp == modestring) ||
    342            ('\0' != *endp) ||
    343            (mode != (unsigned long) (mode_t) mode) )
    344       {
    345         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    346                                    section,
    347                                    "UNIXPATH_MODE",
    348                                    "must be octal number");
    349         GNUNET_free (modestring);
    350         GNUNET_free (serve_unixpath);
    351         return GNUNET_SYSERR;
    352       }
    353       unixpath_mode = (mode_t) mode;
    354     }
    355     GNUNET_free (modestring);
    356 
    357     {
    358       int fd;
    359 
    360       fd = open_unix_path (serve_unixpath,
    361                            unixpath_mode);
    362       GNUNET_free (serve_unixpath);
    363       if (-1 == fd)
    364         return GNUNET_NO;
    365       cb (cb_cls,
    366           fd);
    367       return GNUNET_OK;
    368     }
    369   }
    370 
    371   if (0 == strcasecmp (serve_type,
    372                        "tcp"))
    373   {
    374     unsigned long long lport;
    375     struct GNUNET_NETWORK_Handle *nh;
    376     char *bind_to;
    377 
    378     if (GNUNET_OK !=
    379         GNUNET_CONFIGURATION_get_value_number (cfg,
    380                                                section,
    381                                                "PORT",
    382                                                &lport))
    383     {
    384       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    385                                  section,
    386                                  "PORT",
    387                                  "port number required");
    388       return GNUNET_SYSERR;
    389     }
    390 
    391     if ( (0 == lport) ||
    392          (lport > UINT16_MAX) )
    393     {
    394       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    395                                  section,
    396                                  "PORT",
    397                                  "port number not in [1,65535]");
    398       return GNUNET_SYSERR;
    399     }
    400 
    401     if (GNUNET_OK !=
    402         GNUNET_CONFIGURATION_get_value_string (cfg,
    403                                                section,
    404                                                "BIND_TO",
    405                                                &bind_to))
    406       bind_to = NULL;
    407 
    408     /* let's have fun binding... */
    409     {
    410       char port_str[6];
    411       struct addrinfo hints;
    412       struct addrinfo *res;
    413       int ec;
    414 
    415       GNUNET_snprintf (port_str,
    416                        sizeof (port_str),
    417                        "%u",
    418                        (unsigned int) lport);
    419       memset (&hints,
    420               0,
    421               sizeof (hints));
    422       hints.ai_family = AF_UNSPEC;
    423       hints.ai_socktype = SOCK_STREAM;
    424       hints.ai_protocol = IPPROTO_TCP;
    425       hints.ai_flags = AI_PASSIVE
    426 #ifdef AI_IDN
    427                        | AI_IDN
    428 #endif
    429       ;
    430 
    431       if (0 !=
    432           (ec = getaddrinfo (bind_to,
    433                              port_str,
    434                              &hints,
    435                              &res)))
    436       {
    437         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    438                     "Failed to resolve BIND_TO address `%s': %s\n",
    439                     bind_to,
    440                     gai_strerror (ec));
    441         GNUNET_free (bind_to);
    442         return GNUNET_SYSERR;
    443       }
    444       GNUNET_free (bind_to);
    445       for (struct addrinfo *ai = res;
    446            NULL != ai;
    447            ai = ai->ai_next)
    448       {
    449         if (NULL == (nh = GNUNET_NETWORK_socket_create (ai->ai_family,
    450                                                         ai->ai_socktype,
    451                                                         ai->ai_protocol)))
    452         {
    453           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    454                                "socket");
    455           freeaddrinfo (res);
    456           return GNUNET_NO;
    457         }
    458         {
    459           const int on = 1;
    460 
    461           if (GNUNET_OK !=
    462               GNUNET_NETWORK_socket_setsockopt (nh,
    463                                                 SOL_SOCKET,
    464                                                 SO_REUSEPORT,
    465                                                 &on,
    466                                                 sizeof(on)))
    467             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    468                                  "setsockopt");
    469         }
    470         if (GNUNET_OK !=
    471             GNUNET_NETWORK_socket_bind (nh,
    472                                         ai->ai_addr,
    473                                         ai->ai_addrlen))
    474         {
    475           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    476                                "bind");
    477           GNUNET_break (GNUNET_OK ==
    478                         GNUNET_NETWORK_socket_close (nh));
    479           freeaddrinfo (res);
    480           return GNUNET_NO;
    481         }
    482 
    483         if (GNUNET_OK !=
    484             GNUNET_NETWORK_socket_listen (nh,
    485                                           LISTEN_BACKLOG))
    486         {
    487           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    488                                "listen");
    489           GNUNET_SCHEDULER_shutdown ();
    490           GNUNET_break (GNUNET_OK ==
    491                         GNUNET_NETWORK_socket_close (nh));
    492           freeaddrinfo (res);
    493           return GNUNET_NO;
    494         }
    495 
    496         /* extract and return actual socket handle from 'nh' */
    497         {
    498           int fh;
    499 
    500           fh = GNUNET_NETWORK_get_fd (nh);
    501           GNUNET_NETWORK_socket_free_memory_only_ (nh);
    502           if (-1 == fh)
    503           {
    504             GNUNET_break (0);
    505             freeaddrinfo (res);
    506             return GNUNET_NO;
    507           }
    508           cb (cb_cls,
    509               fh);
    510         }
    511       } /* for all addrinfos */
    512       freeaddrinfo (res);
    513       return GNUNET_OK;
    514     } /* bind data scope */
    515   } /* tcp */
    516   return ret;
    517 }