LCOV - code coverage report
Current view: top level - mhd - mhd_config.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 53 169 31.4 %
Date: 2025-08-30 09:28:00 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          59 : 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          59 :   const char *choices[] = {
     183             :     "tcp",
     184             :     "unix",
     185             :     "systemd",
     186             :     NULL
     187             :   };
     188             :   const char *serve_type;
     189          59 :   enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
     190             : 
     191          59 :   if (GNUNET_OK !=
     192          59 :       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          59 :     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          59 :   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          59 :   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          59 :     if (GNUNET_OK !=
     368          59 :         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          59 :     if ( (0 == lport) ||
     381          59 :          (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          59 :     if (GNUNET_OK !=
     391          59 :         GNUNET_CONFIGURATION_get_value_string (cfg,
     392             :                                                section,
     393             :                                                "BIND_TO",
     394             :                                                &bind_to))
     395          59 :       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          59 :       GNUNET_snprintf (port_str,
     405             :                        sizeof (port_str),
     406             :                        "%u",
     407             :                        (unsigned int) lport);
     408          59 :       memset (&hints,
     409             :               0,
     410             :               sizeof (hints));
     411          59 :       hints.ai_family = AF_UNSPEC;
     412          59 :       hints.ai_socktype = SOCK_STREAM;
     413          59 :       hints.ai_protocol = IPPROTO_TCP;
     414          59 :       hints.ai_flags = AI_PASSIVE
     415             : #ifdef AI_IDN
     416             :                        | AI_IDN
     417             : #endif
     418             :       ;
     419             : 
     420          59 :       if (0 !=
     421          59 :           (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          59 :       GNUNET_free (bind_to);
     434          59 :       for (struct addrinfo *ai = res;
     435         177 :            NULL != ai;
     436         118 :            ai = ai->ai_next)
     437             :       {
     438         118 :         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         118 :           const int on = 1;
     449             : 
     450         118 :           if (GNUNET_OK !=
     451         118 :               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         118 :         if (GNUNET_OK !=
     460         118 :             GNUNET_NETWORK_socket_bind (nh,
     461         118 :                                         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         118 :         if (GNUNET_OK !=
     473         118 :             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         118 :           fh = GNUNET_NETWORK_get_fd (nh);
     490         118 :           GNUNET_NETWORK_socket_free_memory_only_ (nh);
     491         118 :           if (-1 == fh)
     492             :           {
     493           0 :             GNUNET_break (0);
     494           0 :             return GNUNET_NO;
     495             :           }
     496         118 :           cb (cb_cls,
     497             :               fh);
     498             :         }
     499             :       } /* for all addrinfos */
     500          59 :       freeaddrinfo (res);
     501          59 :       return GNUNET_OK;
     502             :     } /* bind data scope */
     503             :   } /* tcp */
     504           0 :   return ret;
     505             : }

Generated by: LCOV version 1.16