LCOV - code coverage report
Current view: top level - mhd - mhd_config.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 30.5 % 174 53
Test Date: 2026-09-23 23:08:06 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 "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           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              :     {
     331              :       char *endp;
     332              :       unsigned long mode;
     333              : 
     334            0 :       errno = 0;
     335            0 :       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            0 :       if ( (0 != errno) ||
     341            0 :            (endp == modestring) ||
     342            0 :            ('\0' != *endp) ||
     343            0 :            (mode != (unsigned long) (mode_t) mode) )
     344              :       {
     345            0 :         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     346              :                                    section,
     347              :                                    "UNIXPATH_MODE",
     348              :                                    "must be octal number");
     349            0 :         GNUNET_free (modestring);
     350            0 :         GNUNET_free (serve_unixpath);
     351            0 :         return GNUNET_SYSERR;
     352              :       }
     353            0 :       unixpath_mode = (mode_t) mode;
     354              :     }
     355            0 :     GNUNET_free (modestring);
     356              : 
     357              :     {
     358              :       int fd;
     359              : 
     360            0 :       fd = open_unix_path (serve_unixpath,
     361              :                            unixpath_mode);
     362            0 :       GNUNET_free (serve_unixpath);
     363            0 :       if (-1 == fd)
     364            0 :         return GNUNET_NO;
     365            0 :       cb (cb_cls,
     366              :           fd);
     367            0 :       return GNUNET_OK;
     368              :     }
     369              :   }
     370              : 
     371           23 :   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           23 :     if (GNUNET_OK !=
     379           23 :         GNUNET_CONFIGURATION_get_value_number (cfg,
     380              :                                                section,
     381              :                                                "PORT",
     382              :                                                &lport))
     383              :     {
     384            0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     385              :                                  section,
     386              :                                  "PORT",
     387              :                                  "port number required");
     388            0 :       return GNUNET_SYSERR;
     389              :     }
     390              : 
     391           23 :     if ( (0 == lport) ||
     392           23 :          (lport > UINT16_MAX) )
     393              :     {
     394            0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     395              :                                  section,
     396              :                                  "PORT",
     397              :                                  "port number not in [1,65535]");
     398            0 :       return GNUNET_SYSERR;
     399              :     }
     400              : 
     401           23 :     if (GNUNET_OK !=
     402           23 :         GNUNET_CONFIGURATION_get_value_string (cfg,
     403              :                                                section,
     404              :                                                "BIND_TO",
     405              :                                                &bind_to))
     406           23 :       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           23 :       GNUNET_snprintf (port_str,
     416              :                        sizeof (port_str),
     417              :                        "%u",
     418              :                        (unsigned int) lport);
     419           23 :       memset (&hints,
     420              :               0,
     421              :               sizeof (hints));
     422           23 :       hints.ai_family = AF_UNSPEC;
     423           23 :       hints.ai_socktype = SOCK_STREAM;
     424           23 :       hints.ai_protocol = IPPROTO_TCP;
     425           23 :       hints.ai_flags = AI_PASSIVE
     426              : #ifdef AI_IDN
     427              :                        | AI_IDN
     428              : #endif
     429              :       ;
     430              : 
     431           23 :       if (0 !=
     432           23 :           (ec = getaddrinfo (bind_to,
     433              :                              port_str,
     434              :                              &hints,
     435              :                              &res)))
     436              :       {
     437            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     438              :                     "Failed to resolve BIND_TO address `%s': %s\n",
     439              :                     bind_to,
     440              :                     gai_strerror (ec));
     441            0 :         GNUNET_free (bind_to);
     442            0 :         return GNUNET_SYSERR;
     443              :       }
     444           23 :       GNUNET_free (bind_to);
     445           23 :       for (struct addrinfo *ai = res;
     446           69 :            NULL != ai;
     447           46 :            ai = ai->ai_next)
     448              :       {
     449           46 :         if (NULL == (nh = GNUNET_NETWORK_socket_create (ai->ai_family,
     450              :                                                         ai->ai_socktype,
     451              :                                                         ai->ai_protocol)))
     452              :         {
     453            0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     454              :                                "socket");
     455            0 :           freeaddrinfo (res);
     456            0 :           return GNUNET_NO;
     457              :         }
     458              :         {
     459           46 :           const int on = 1;
     460              : 
     461           46 :           if (GNUNET_OK !=
     462           46 :               GNUNET_NETWORK_socket_setsockopt (nh,
     463              :                                                 SOL_SOCKET,
     464              :                                                 SO_REUSEPORT,
     465              :                                                 &on,
     466              :                                                 sizeof(on)))
     467            0 :             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     468              :                                  "setsockopt");
     469              :         }
     470           46 :         if (GNUNET_OK !=
     471           46 :             GNUNET_NETWORK_socket_bind (nh,
     472           46 :                                         ai->ai_addr,
     473              :                                         ai->ai_addrlen))
     474              :         {
     475            0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     476              :                                "bind");
     477            0 :           GNUNET_break (GNUNET_OK ==
     478              :                         GNUNET_NETWORK_socket_close (nh));
     479            0 :           freeaddrinfo (res);
     480            0 :           return GNUNET_NO;
     481              :         }
     482              : 
     483           46 :         if (GNUNET_OK !=
     484           46 :             GNUNET_NETWORK_socket_listen (nh,
     485              :                                           LISTEN_BACKLOG))
     486              :         {
     487            0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     488              :                                "listen");
     489            0 :           GNUNET_SCHEDULER_shutdown ();
     490            0 :           GNUNET_break (GNUNET_OK ==
     491              :                         GNUNET_NETWORK_socket_close (nh));
     492            0 :           freeaddrinfo (res);
     493            0 :           return GNUNET_NO;
     494              :         }
     495              : 
     496              :         /* extract and return actual socket handle from 'nh' */
     497              :         {
     498              :           int fh;
     499              : 
     500           46 :           fh = GNUNET_NETWORK_get_fd (nh);
     501           46 :           GNUNET_NETWORK_socket_free_memory_only_ (nh);
     502           46 :           if (-1 == fh)
     503              :           {
     504            0 :             GNUNET_break (0);
     505            0 :             freeaddrinfo (res);
     506            0 :             return GNUNET_NO;
     507              :           }
     508           46 :           cb (cb_cls,
     509              :               fh);
     510              :         }
     511              :       } /* for all addrinfos */
     512           23 :       freeaddrinfo (res);
     513           23 :       return GNUNET_OK;
     514              :     } /* bind data scope */
     515              :   } /* tcp */
     516            0 :   return ret;
     517              : }
        

Generated by: LCOV version 2.0-1