LCOV - code coverage report
Current view: top level - mhd - mhd_config.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 53 166 31.9 %
Date: 2025-06-22 12:09:43 Functions: 2 3 66.7 %

          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         114 : 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         114 :   if (-1 == cache)
      51           2 :     return;
      52         113 :   if (0 == cache)
      53             :   {
      54          57 :     if (0 ==
      55          57 :         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         112 :   cache = 1;
      66         112 :   vsnprintf (buf,
      67             :              sizeof (buf),
      68             :              fm,
      69             :              ap);
      70         112 :   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          57 : 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          57 :   const char *choices[] = {
     183             :     "tcp",
     184             :     "unix",
     185             :     "systemd",
     186             :     NULL
     187             :   };
     188             :   const char *serve_type;
     189          57 :   enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
     190             : 
     191          57 :   if (GNUNET_OK !=
     192          57 :       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          57 :     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 *fdns;
     224             :       char dummy;
     225             : 
     226           0 :       if (0 != strcmp (serve_type,
     227             :                        "systemd"))
     228             :       {
     229           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     230             :                     "Using systemd activation due to environment variables. Set SERVE=systemd to disable this warning!\n");
     231             :       }
     232             :       else
     233             :       {
     234           0 :         ret = GNUNET_NO;
     235             :       }
     236           0 :       if (1 != sscanf (listen_fds,
     237             :                        "%u%c",
     238             :                        &cnt,
     239             :                        &dummy))
     240             :       {
     241           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     242             :                     "Invalid number `%s' of systemd sockets specified\n",
     243             :                     listen_fds);
     244             :       }
     245             :       else
     246             :       {
     247           0 :         fdns = GNUNET_strdup (listen_fdn);
     248           0 :         for (const char *tok = strtok (fdns,
     249             :                                        ":");
     250           0 :              cnt-- > 0;
     251           0 :              tok = strtok (NULL,
     252             :                            ":"))
     253             :         {
     254           0 :           int fh = off++;
     255             :           int flags;
     256             : 
     257           0 :           flags = fcntl (fh,
     258             :                          F_GETFD);
     259           0 :           if ( (-1 == flags) &&
     260           0 :                (EBADF == errno) )
     261             :           {
     262           0 :             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     263             :                         "Bad listen socket %s (%d) passed, ignored\n",
     264             :                         tok,
     265             :                         fh);
     266           0 :             continue;
     267             :           }
     268           0 :           flags |= FD_CLOEXEC;
     269           0 :           if (0 != fcntl (fh,
     270             :                           F_SETFD,
     271             :                           flags))
     272           0 :             GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     273             :                                  "fcntl");
     274           0 :           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     275             :                       "Successfully obtained listen socket %s (#%d) from hypervisor\n",
     276             :                       tok,
     277             :                       fh);
     278           0 :           cb (cb_cls,
     279             :               fh);
     280           0 :           ret = GNUNET_OK;
     281             :         }
     282             :       }
     283           0 :       GNUNET_free (fdns);
     284             :     }
     285             :   }
     286             : 
     287             :   /* now try configuration file */
     288          57 :   if (0 == strcmp (serve_type,
     289             :                    "unix"))
     290             :   {
     291             :     char *serve_unixpath;
     292             :     mode_t unixpath_mode;
     293             :     struct sockaddr_un s_un;
     294             :     char *modestring;
     295             : 
     296           0 :     if (GNUNET_OK !=
     297           0 :         GNUNET_CONFIGURATION_get_value_filename (cfg,
     298             :                                                  section,
     299             :                                                  "UNIXPATH",
     300             :                                                  &serve_unixpath))
     301             :     {
     302           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     303             :                                  section,
     304             :                                  "UNIXPATH",
     305             :                                  "UNIXPATH value required");
     306           0 :       return GNUNET_SYSERR;
     307             :     }
     308           0 :     if (strlen (serve_unixpath) >= sizeof (s_un.sun_path))
     309             :     {
     310           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     311             :                   "unixpath `%s' is too long\n",
     312             :                   serve_unixpath);
     313           0 :       GNUNET_free (serve_unixpath);
     314           0 :       return GNUNET_SYSERR;
     315             :     }
     316             : 
     317           0 :     if (GNUNET_OK !=
     318           0 :         GNUNET_CONFIGURATION_get_value_string (cfg,
     319             :                                                section,
     320             :                                                "UNIXPATH_MODE",
     321             :                                                &modestring))
     322             :     {
     323           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     324             :                                  section,
     325             :                                  "UNIXPATH_MODE");
     326           0 :       GNUNET_free (serve_unixpath);
     327           0 :       return GNUNET_SYSERR;
     328             :     }
     329           0 :     errno = 0;
     330           0 :     unixpath_mode = (mode_t) strtoul (modestring,
     331             :                                       NULL,
     332             :                                       8);
     333           0 :     if (0 != errno)
     334             :     {
     335           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     336             :                                  section,
     337             :                                  "UNIXPATH_MODE",
     338             :                                  "must be octal number");
     339           0 :       GNUNET_free (modestring);
     340           0 :       GNUNET_free (serve_unixpath);
     341           0 :       return GNUNET_SYSERR;
     342             :     }
     343           0 :     GNUNET_free (modestring);
     344             : 
     345             :     {
     346             :       int fd;
     347             : 
     348           0 :       fd = open_unix_path (serve_unixpath,
     349             :                            unixpath_mode);
     350           0 :       GNUNET_free (serve_unixpath);
     351           0 :       if (-1 == fd)
     352           0 :         return GNUNET_NO;
     353           0 :       cb (cb_cls,
     354             :           fd);
     355           0 :       return GNUNET_OK;
     356             :     }
     357             :   }
     358             : 
     359          57 :   if (0 == strcasecmp (serve_type,
     360             :                        "tcp"))
     361             :   {
     362             :     unsigned long long lport;
     363             :     struct GNUNET_NETWORK_Handle *nh;
     364             :     char *bind_to;
     365             : 
     366          57 :     if (GNUNET_OK !=
     367          57 :         GNUNET_CONFIGURATION_get_value_number (cfg,
     368             :                                                section,
     369             :                                                "PORT",
     370             :                                                &lport))
     371             :     {
     372           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     373             :                                  section,
     374             :                                  "PORT",
     375             :                                  "port number required");
     376           0 :       return GNUNET_SYSERR;
     377             :     }
     378             : 
     379          57 :     if ( (0 == lport) ||
     380          57 :          (lport > UINT16_MAX) )
     381             :     {
     382           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     383             :                                  section,
     384             :                                  "PORT",
     385             :                                  "port number not in [1,65535]");
     386           0 :       return GNUNET_SYSERR;
     387             :     }
     388             : 
     389          57 :     if (GNUNET_OK !=
     390          57 :         GNUNET_CONFIGURATION_get_value_string (cfg,
     391             :                                                section,
     392             :                                                "BIND_TO",
     393             :                                                &bind_to))
     394          57 :       bind_to = NULL;
     395             : 
     396             :     /* let's have fun binding... */
     397             :     {
     398             :       char port_str[6];
     399             :       struct addrinfo hints;
     400             :       struct addrinfo *res;
     401             :       int ec;
     402             : 
     403          57 :       GNUNET_snprintf (port_str,
     404             :                        sizeof (port_str),
     405             :                        "%u",
     406             :                        (unsigned int) lport);
     407          57 :       memset (&hints,
     408             :               0,
     409             :               sizeof (hints));
     410          57 :       hints.ai_family = AF_UNSPEC;
     411          57 :       hints.ai_socktype = SOCK_STREAM;
     412          57 :       hints.ai_protocol = IPPROTO_TCP;
     413          57 :       hints.ai_flags = AI_PASSIVE
     414             : #ifdef AI_IDN
     415             :                        | AI_IDN
     416             : #endif
     417             :       ;
     418             : 
     419          57 :       if (0 !=
     420          57 :           (ec = getaddrinfo (bind_to,
     421             :                              port_str,
     422             :                              &hints,
     423             :                              &res)))
     424             :       {
     425           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     426             :                     "Failed to resolve BIND_TO address `%s': %s\n",
     427             :                     bind_to,
     428             :                     gai_strerror (ec));
     429           0 :         GNUNET_free (bind_to);
     430           0 :         return GNUNET_SYSERR;
     431             :       }
     432          57 :       GNUNET_free (bind_to);
     433          57 :       for (struct addrinfo *ai = res;
     434         171 :            NULL != ai;
     435         114 :            ai = ai->ai_next)
     436             :       {
     437         114 :         if (NULL == (nh = GNUNET_NETWORK_socket_create (ai->ai_family,
     438             :                                                         ai->ai_socktype,
     439             :                                                         ai->ai_protocol)))
     440             :         {
     441           0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     442             :                                "socket");
     443           0 :           freeaddrinfo (res);
     444           0 :           return GNUNET_NO;
     445             :         }
     446             :         {
     447         114 :           const int on = 1;
     448             : 
     449         114 :           if (GNUNET_OK !=
     450         114 :               GNUNET_NETWORK_socket_setsockopt (nh,
     451             :                                                 SOL_SOCKET,
     452             :                                                 SO_REUSEPORT,
     453             :                                                 &on,
     454             :                                                 sizeof(on)))
     455           0 :             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     456             :                                  "setsockopt");
     457             :         }
     458         114 :         if (GNUNET_OK !=
     459         114 :             GNUNET_NETWORK_socket_bind (nh,
     460         114 :                                         ai->ai_addr,
     461             :                                         ai->ai_addrlen))
     462             :         {
     463           0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     464             :                                "bind");
     465           0 :           freeaddrinfo (res);
     466           0 :           return GNUNET_NO;
     467             :         }
     468             : 
     469         114 :         if (GNUNET_OK !=
     470         114 :             GNUNET_NETWORK_socket_listen (nh,
     471             :                                           LISTEN_BACKLOG))
     472             :         {
     473           0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     474             :                                "listen");
     475           0 :           GNUNET_SCHEDULER_shutdown ();
     476           0 :           return GNUNET_NO;
     477             :         }
     478             : 
     479             :         /* extract and return actual socket handle from 'nh' */
     480             :         {
     481             :           int fh;
     482             : 
     483         114 :           fh = GNUNET_NETWORK_get_fd (nh);
     484         114 :           GNUNET_NETWORK_socket_free_memory_only_ (nh);
     485         114 :           if (-1 == fh)
     486             :           {
     487           0 :             GNUNET_break (0);
     488           0 :             return GNUNET_NO;
     489             :           }
     490         114 :           cb (cb_cls,
     491             :               fh);
     492             :         }
     493             :       } /* for all addrinfos */
     494          57 :       freeaddrinfo (res);
     495          57 :       return GNUNET_OK;
     496             :     } /* bind data scope */
     497             :   } /* tcp */
     498           0 :   return ret;
     499             : }

Generated by: LCOV version 1.16