sync

Backup service to store encrypted wallet databases (experimental)
Log | Files | Refs | Submodules | README | LICENSE

mhd2_config.c (14168B)


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