LCOV - code coverage report
Current view: top level - mhd - mhd_config.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 31.4 % 169 53
Test Date: 2025-12-22 22:38:17 Functions: 66.7 % 3 2

            Line data    Source code
       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           46 : 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           46 :   if (-1 == cache)
      51            2 :     return;
      52           45 :   if (0 == cache)
      53              :   {
      54           23 :     if (0 ==
      55           23 :         GNUNET_get_log_call_status (GNUNET_ERROR_TYPE_INFO,
      56              :                                     "libmicrohttpd",
      57              :                                     __FILE__,
      58              :                                     __FUNCTION__,
      59              :                                     __LINE__))
      60              :     {
      61            1 :       cache = -1;
      62            1 :       return;
      63              :     }
      64              :   }
      65           44 :   cache = 1;
      66           44 :   vsnprintf (buf,
      67              :              sizeof (buf),
      68              :              fm,
      69              :              ap);
      70           44 :   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            0 : 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            0 :   if (sizeof (un->sun_path) <= strlen (unix_path))
      93              :   {
      94            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      95              :                 "unixpath `%s' is too long\n",
      96              :                 unix_path);
      97            0 :     return -1;
      98              :   }
      99            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     100              :               "Creating listen socket '%s' with mode %o\n",
     101              :               unix_path,
     102              :               unix_mode);
     103              : 
     104            0 :   if (GNUNET_OK !=
     105            0 :       GNUNET_DISK_directory_create_for_file (unix_path))
     106              :   {
     107            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     108              :                               "mkdir",
     109              :                               unix_path);
     110              :   }
     111              : 
     112            0 :   un = GNUNET_new (struct sockaddr_un);
     113            0 :   un->sun_family = AF_UNIX;
     114            0 :   strncpy (un->sun_path,
     115              :            unix_path,
     116              :            sizeof (un->sun_path) - 1);
     117            0 :   GNUNET_NETWORK_unix_precheck (un);
     118              : 
     119            0 :   if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX,
     120              :                                                   SOCK_STREAM,
     121              :                                                   0)))
     122              :   {
     123            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     124              :                          "socket");
     125            0 :     GNUNET_free (un);
     126            0 :     return -1;
     127              :   }
     128              : 
     129            0 :   if (GNUNET_OK !=
     130            0 :       GNUNET_NETWORK_socket_bind (nh,
     131              :                                   (void *) un,
     132              :                                   sizeof (struct sockaddr_un)))
     133              :   {
     134            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     135              :                               "bind",
     136              :                               unix_path);
     137            0 :     GNUNET_free (un);
     138            0 :     GNUNET_NETWORK_socket_close (nh);
     139            0 :     return -1;
     140              :   }
     141            0 :   GNUNET_free (un);
     142            0 :   if (GNUNET_OK !=
     143            0 :       GNUNET_NETWORK_socket_listen (nh,
     144              :                                     LISTEN_BACKLOG))
     145              :   {
     146            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     147              :                          "listen");
     148            0 :     GNUNET_NETWORK_socket_close (nh);
     149            0 :     return -1;
     150              :   }
     151              : 
     152            0 :   if (0 != chmod (unix_path,
     153              :                   unix_mode))
     154              :   {
     155            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     156              :                          "chmod");
     157            0 :     GNUNET_NETWORK_socket_close (nh);
     158            0 :     return -1;
     159              :   }
     160            0 :   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            0 :     fd = GNUNET_NETWORK_get_fd (nh);
     170            0 :     GNUNET_NETWORK_socket_free_memory_only_ (nh);
     171            0 :     return fd;
     172              :   }
     173              : }
     174              : 
     175              : 
     176              : enum GNUNET_GenericReturnValue
     177           23 : 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           23 :   const char *choices[] = {
     183              :     "tcp",
     184              :     "unix",
     185              :     "systemd",
     186              :     NULL
     187              :   };
     188              :   const char *serve_type;
     189           23 :   enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
     190              : 
     191           23 :   if (GNUNET_OK !=
     192           23 :       GNUNET_CONFIGURATION_get_value_choice (cfg,
     193              :                                              section,
     194              :                                              "SERVE",
     195              :                                              choices,
     196              :                                              &serve_type))
     197              :   {
     198            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     199              :                                section,
     200              :                                "SERVE",
     201              :                                "serve type (tcp, unix or systemd) required");
     202            0 :     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           23 :     if ( (NULL != (listen_pid = getenv ("LISTEN_PID"))) &&
     214            0 :          (getpid () ==
     215            0 :           strtol (listen_pid,
     216              :                   NULL,
     217            0 :                   10)) &&
     218            0 :          (NULL != (listen_fds = getenv ("LISTEN_FDS"))) &&
     219            0 :          (NULL != (listen_fdn = getenv ("LISTEN_FDNAMES"))) )
     220              :     {
     221            0 :       int off = 3;
     222              :       unsigned int cnt;
     223              :       char dummy;
     224              : 
     225            0 :       if (0 != strcmp (serve_type,
     226              :                        "systemd"))
     227              :       {
     228            0 :         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            0 :         ret = GNUNET_NO;
     234              :       }
     235            0 :       if (1 != sscanf (listen_fds,
     236              :                        "%u%c",
     237              :                        &cnt,
     238              :                        &dummy))
     239              :       {
     240            0 :         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            0 :         fdns = GNUNET_strdup (listen_fdn);
     249            0 :         for (const char *tok = strtok (fdns,
     250              :                                        ":");
     251            0 :              cnt-- > 0;
     252            0 :              tok = strtok (NULL,
     253              :                            ":"))
     254              :         {
     255            0 :           int fh = off++;
     256              :           int flags;
     257              : 
     258            0 :           flags = fcntl (fh,
     259              :                          F_GETFD);
     260            0 :           if ( (-1 == flags) &&
     261            0 :                (EBADF == errno) )
     262              :           {
     263            0 :             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     264              :                         "Bad listen socket %s (%d) passed, ignored\n",
     265              :                         tok,
     266              :                         fh);
     267            0 :             continue;
     268              :           }
     269            0 :           flags |= FD_CLOEXEC;
     270            0 :           if (0 != fcntl (fh,
     271              :                           F_SETFD,
     272              :                           flags))
     273            0 :             GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     274              :                                  "fcntl");
     275            0 :           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     276              :                       "Successfully obtained listen socket %s (#%d) from hypervisor\n",
     277              :                       tok,
     278              :                       fh);
     279            0 :           cb (cb_cls,
     280              :               fh);
     281            0 :           ret = GNUNET_OK;
     282              :         }
     283            0 :         GNUNET_free (fdns);
     284              :       }
     285              :     }
     286              :   }
     287              : 
     288              :   /* now try configuration file */
     289           23 :   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            0 :     if (GNUNET_OK !=
     298            0 :         GNUNET_CONFIGURATION_get_value_filename (cfg,
     299              :                                                  section,
     300              :                                                  "UNIXPATH",
     301              :                                                  &serve_unixpath))
     302              :     {
     303            0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     304              :                                  section,
     305              :                                  "UNIXPATH",
     306              :                                  "UNIXPATH value required");
     307            0 :       return GNUNET_SYSERR;
     308              :     }
     309            0 :     if (strlen (serve_unixpath) >= sizeof (s_un.sun_path))
     310              :     {
     311            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     312              :                   "unixpath `%s' is too long\n",
     313              :                   serve_unixpath);
     314            0 :       GNUNET_free (serve_unixpath);
     315            0 :       return GNUNET_SYSERR;
     316              :     }
     317              : 
     318            0 :     if (GNUNET_OK !=
     319            0 :         GNUNET_CONFIGURATION_get_value_string (cfg,
     320              :                                                section,
     321              :                                                "UNIXPATH_MODE",
     322              :                                                &modestring))
     323              :     {
     324            0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     325              :                                  section,
     326              :                                  "UNIXPATH_MODE");
     327            0 :       GNUNET_free (serve_unixpath);
     328            0 :       return GNUNET_SYSERR;
     329              :     }
     330            0 :     errno = 0;
     331            0 :     unixpath_mode = (mode_t) strtoul (modestring,
     332              :                                       NULL,
     333              :                                       8);
     334            0 :     if (0 != errno)
     335              :     {
     336            0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     337              :                                  section,
     338              :                                  "UNIXPATH_MODE",
     339              :                                  "must be octal number");
     340            0 :       GNUNET_free (modestring);
     341            0 :       GNUNET_free (serve_unixpath);
     342            0 :       return GNUNET_SYSERR;
     343              :     }
     344            0 :     GNUNET_free (modestring);
     345              : 
     346              :     {
     347              :       int fd;
     348              : 
     349            0 :       fd = open_unix_path (serve_unixpath,
     350              :                            unixpath_mode);
     351            0 :       GNUNET_free (serve_unixpath);
     352            0 :       if (-1 == fd)
     353            0 :         return GNUNET_NO;
     354            0 :       cb (cb_cls,
     355              :           fd);
     356            0 :       return GNUNET_OK;
     357              :     }
     358              :   }
     359              : 
     360           23 :   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           23 :     if (GNUNET_OK !=
     368           23 :         GNUNET_CONFIGURATION_get_value_number (cfg,
     369              :                                                section,
     370              :                                                "PORT",
     371              :                                                &lport))
     372              :     {
     373            0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     374              :                                  section,
     375              :                                  "PORT",
     376              :                                  "port number required");
     377            0 :       return GNUNET_SYSERR;
     378              :     }
     379              : 
     380           23 :     if ( (0 == lport) ||
     381           23 :          (lport > UINT16_MAX) )
     382              :     {
     383            0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     384              :                                  section,
     385              :                                  "PORT",
     386              :                                  "port number not in [1,65535]");
     387            0 :       return GNUNET_SYSERR;
     388              :     }
     389              : 
     390           23 :     if (GNUNET_OK !=
     391           23 :         GNUNET_CONFIGURATION_get_value_string (cfg,
     392              :                                                section,
     393              :                                                "BIND_TO",
     394              :                                                &bind_to))
     395           23 :       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           23 :       GNUNET_snprintf (port_str,
     405              :                        sizeof (port_str),
     406              :                        "%u",
     407              :                        (unsigned int) lport);
     408           23 :       memset (&hints,
     409              :               0,
     410              :               sizeof (hints));
     411           23 :       hints.ai_family = AF_UNSPEC;
     412           23 :       hints.ai_socktype = SOCK_STREAM;
     413           23 :       hints.ai_protocol = IPPROTO_TCP;
     414           23 :       hints.ai_flags = AI_PASSIVE
     415              : #ifdef AI_IDN
     416              :                        | AI_IDN
     417              : #endif
     418              :       ;
     419              : 
     420           23 :       if (0 !=
     421           23 :           (ec = getaddrinfo (bind_to,
     422              :                              port_str,
     423              :                              &hints,
     424              :                              &res)))
     425              :       {
     426            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     427              :                     "Failed to resolve BIND_TO address `%s': %s\n",
     428              :                     bind_to,
     429              :                     gai_strerror (ec));
     430            0 :         GNUNET_free (bind_to);
     431            0 :         return GNUNET_SYSERR;
     432              :       }
     433           23 :       GNUNET_free (bind_to);
     434           23 :       for (struct addrinfo *ai = res;
     435           69 :            NULL != ai;
     436           46 :            ai = ai->ai_next)
     437              :       {
     438           46 :         if (NULL == (nh = GNUNET_NETWORK_socket_create (ai->ai_family,
     439              :                                                         ai->ai_socktype,
     440              :                                                         ai->ai_protocol)))
     441              :         {
     442            0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     443              :                                "socket");
     444            0 :           freeaddrinfo (res);
     445            0 :           return GNUNET_NO;
     446              :         }
     447              :         {
     448           46 :           const int on = 1;
     449              : 
     450           46 :           if (GNUNET_OK !=
     451           46 :               GNUNET_NETWORK_socket_setsockopt (nh,
     452              :                                                 SOL_SOCKET,
     453              :                                                 SO_REUSEPORT,
     454              :                                                 &on,
     455              :                                                 sizeof(on)))
     456            0 :             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     457              :                                  "setsockopt");
     458              :         }
     459           46 :         if (GNUNET_OK !=
     460           46 :             GNUNET_NETWORK_socket_bind (nh,
     461           46 :                                         ai->ai_addr,
     462              :                                         ai->ai_addrlen))
     463              :         {
     464            0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     465              :                                "bind");
     466            0 :           GNUNET_break (GNUNET_OK ==
     467              :                         GNUNET_NETWORK_socket_close (nh));
     468            0 :           freeaddrinfo (res);
     469            0 :           return GNUNET_NO;
     470              :         }
     471              : 
     472           46 :         if (GNUNET_OK !=
     473           46 :             GNUNET_NETWORK_socket_listen (nh,
     474              :                                           LISTEN_BACKLOG))
     475              :         {
     476            0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     477              :                                "listen");
     478            0 :           GNUNET_SCHEDULER_shutdown ();
     479            0 :           GNUNET_break (GNUNET_OK ==
     480              :                         GNUNET_NETWORK_socket_close (nh));
     481            0 :           freeaddrinfo (res);
     482            0 :           return GNUNET_NO;
     483              :         }
     484              : 
     485              :         /* extract and return actual socket handle from 'nh' */
     486              :         {
     487              :           int fh;
     488              : 
     489           46 :           fh = GNUNET_NETWORK_get_fd (nh);
     490           46 :           GNUNET_NETWORK_socket_free_memory_only_ (nh);
     491           46 :           if (-1 == fh)
     492              :           {
     493            0 :             GNUNET_break (0);
     494            0 :             return GNUNET_NO;
     495              :           }
     496           46 :           cb (cb_cls,
     497              :               fh);
     498              :         }
     499              :       } /* for all addrinfos */
     500           23 :       freeaddrinfo (res);
     501           23 :       return GNUNET_OK;
     502              :     } /* bind data scope */
     503              :   } /* tcp */
     504            0 :   return ret;
     505              : }
        

Generated by: LCOV version 2.0-1