/* This file is part of TALER Copyright (C) 2014--2019 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. TALER is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with TALER; see the file COPYING. If not, see */ /** * @file mhd_config.c * @brief functions to configure and setup MHD * @author Florian Dold * @author Benedikt Mueller * @author Christian Grothoff */ #include "platform.h" #include #include "taler_mhd_lib.h" /** * Backlog for listen operation on UNIX domain sockets. */ #define UNIX_BACKLOG 500 /** * Parse the configuration to determine on which port * or UNIX domain path we should run an HTTP service. * * @param cfg configuration to parse * @param section section of the configuration to parse (usually "exchange") * @param[out] rport set to the port number, or 0 for none * @param[out] unix_path set to the UNIX path, or NULL for none * @param[out] unix_mode set to the mode to be used for @a unix_path * @return #GNUNET_OK on success */ int TALER_MHD_parse_config (const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, uint16_t *rport, char **unix_path, mode_t *unix_mode) { const char *choices[] = {"tcp", "unix"}; const char *serve_type; unsigned long long port; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_choice (cfg, section, "serve", choices, &serve_type)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, section, "serve", "serve type required"); return GNUNET_SYSERR; } if (0 == strcasecmp (serve_type, "tcp")) { if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, section, "port", &port)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, section, "port", "port number required"); return GNUNET_SYSERR; } if ( (0 == port) || (port > UINT16_MAX) ) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, section, "port", "value not in [1,65535]"); return GNUNET_SYSERR; } *rport = (uint16_t) port; *unix_path = NULL; return GNUNET_OK; } if (0 == strcmp (serve_type, "unix")) { struct sockaddr_un s_un; char *modestring; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, section, "unixpath", unix_path)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, section, "unixpath", "unixpath required"); return GNUNET_SYSERR; } if (strlen (*unix_path) >= sizeof (s_un.sun_path)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "unixpath `%s' is too long\n", *unix_path); return GNUNET_SYSERR; } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, section, "UNIXPATH_MODE", &modestring)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, section, "UNIXPATH_MODE"); return GNUNET_SYSERR; } errno = 0; *unix_mode = (mode_t) strtoul (modestring, NULL, 8); if (0 != errno) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, section, "UNIXPATH_MODE", "must be octal number"); GNUNET_free (modestring); return GNUNET_SYSERR; } GNUNET_free (modestring); return GNUNET_OK; } /* not reached */ GNUNET_assert (0); return GNUNET_SYSERR; } /** * Function called for logging by MHD. * * @param cls closure, NULL * @param fm format string (`printf()`-style) * @param ap arguments to @a fm */ void TALER_MHD_handle_logs (void *cls, const char *fm, va_list ap) { static int cache; char buf[2048]; (void) cls; if (-1 == cache) return; if (0 == cache) { if (0 == GNUNET_get_log_call_status (GNUNET_ERROR_TYPE_INFO, "libmicrohttpd", __FILE__, __FUNCTION__, __LINE__)) { cache = -1; return; } } cache = 1; vsnprintf (buf, sizeof (buf), fm, ap); GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_INFO, "libmicrohttpd", "%s", buf); } /** * Open UNIX domain socket for listining at @a unix_path with * permissions @a unix_mode. * * @param unix_path where to listen * @param unix_mode access permissions to set * @return -1 on error, otherwise the listen socket */ int TALER_MHD_open_unix_path (const char *unix_path, mode_t unix_mode) { struct GNUNET_NETWORK_Handle *nh; struct sockaddr_un *un; int fd; if (sizeof (un->sun_path) <= strlen (unix_path)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "unixpath `%s' is too long\n", unix_path); return -1; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating listen socket '%s' with mode %o\n", unix_path, unix_mode); if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (unix_path)) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir", unix_path); } un = GNUNET_new (struct sockaddr_un); un->sun_family = AF_UNIX; strncpy (un->sun_path, unix_path, sizeof (un->sun_path) - 1); GNUNET_NETWORK_unix_precheck (un); if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0))) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); GNUNET_free (un); return -1; } if (GNUNET_OK != GNUNET_NETWORK_socket_bind (nh, (void *) un, sizeof (struct sockaddr_un))) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "bind", unix_path); GNUNET_free (un); GNUNET_NETWORK_socket_close (nh); return -1; } GNUNET_free (un); if (GNUNET_OK != GNUNET_NETWORK_socket_listen (nh, UNIX_BACKLOG)) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen"); GNUNET_NETWORK_socket_close (nh); return -1; } if (0 != chmod (unix_path, unix_mode)) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "chmod"); GNUNET_NETWORK_socket_close (nh); return -1; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set socket '%s' to mode %o\n", unix_path, unix_mode); fd = GNUNET_NETWORK_get_fd (nh); GNUNET_NETWORK_socket_free_memory_only_ (nh); return fd; }