From c2c2b92ed489dfdc856a6e683564e10ef539a12d Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 25 Apr 2016 16:05:30 +0200 Subject: [PATCH] implement serving exchange via unix domain sockets --- src/exchange/exchange.conf | 8 + src/exchange/taler-exchange-httpd.c | 230 ++++++++++++++++++++++++---- 2 files changed, 208 insertions(+), 30 deletions(-) diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf index 96322d6a2..f0cd424ae 100644 --- a/src/exchange/exchange.conf +++ b/src/exchange/exchange.conf @@ -11,6 +11,13 @@ KEYDIR = ${TALER_DATA_HOME}/exchange/live-keys/ # the actual coin operations. # WIREFORMAT = test +# serve via tcp socket (on PORT) +SERVE = tcp + +# Unix domain socket to listen on, +# only effective with "SERVE = unix" +UNIXPATH = ${TALER_SOCKET_DIR}/exchange + # HTTP port the exchange listens to # PORT = 8081 @@ -25,3 +32,4 @@ DB = postgres # Where do we store the offline master private key of the exchange? MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv + diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 6efb1492e..9a687cd57 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -42,6 +42,13 @@ #include "taler_exchangedb_plugin.h" #include "taler-exchange-httpd_validation.h" + +/** + * Backlog for listen operation on unix + * domain sockets. + */ +#define UNIX_BACKLOG 500 + /** * Which currency is used by this exchange? */ @@ -93,6 +100,12 @@ static struct MHD_Daemon *mydaemon; */ static uint16_t serve_port; +/** + * Path for the unix domain socket + * to run the daemon on. + */ +static char *serve_unixpath; + /** * Function called whenever MHD is done with a request. If the @@ -453,30 +466,84 @@ exchange_serve_process_config () GNUNET_YES); } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - "exchange", - "port", - &port)) { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "port", - "port number required"); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; + const char *choices[] = {"tcp", "unix"}; + const char *serve_type; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_choice (cfg, + "exchange", + "serve", + choices, + &serve_type)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "serve", + "serve type required"); + TMH_VALIDATION_done (); + return GNUNET_SYSERR; + } + + if (0 == strcmp (serve_type, "tcp")) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + "exchange", + "port", + &port)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "port", + "port number required"); + TMH_VALIDATION_done (); + return GNUNET_SYSERR; + } + + if ( (0 == port) || + (port > UINT16_MAX) ) + { + fprintf (stderr, + "Invalid configuration (value out of range): %llu is not a valid port\n", + port); + TMH_VALIDATION_done (); + return GNUNET_SYSERR; + } + serve_port = (uint16_t) port; + } + else if (0 == strcmp (serve_type, "unix")) + { + struct sockaddr_un s_un; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + "exchange", + "unixpath", + &serve_unixpath)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "unixpath", + "unixpath required"); + TMH_VALIDATION_done (); + return GNUNET_SYSERR; + } + + if (strlen (serve_unixpath) >= sizeof (s_un.sun_path)) + { + fprintf (stderr, + "Invalid configuration: unix path too long\n"); + TMH_VALIDATION_done (); + return GNUNET_SYSERR; + } + } + else + { + // not reached + GNUNET_assert (0); + } } - if ( (0 == port) || - (port > UINT16_MAX) ) - { - fprintf (stderr, - "Invalid configuration (value out of range): %llu is not a valid port\n", - port); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - serve_port = (uint16_t) port; return GNUNET_OK; } @@ -617,6 +684,63 @@ handle_mhd_logs (void *cls, } +/** + * Make a socket non-inheritable to child processes + * + * @param fd the socket to make non-inheritable + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +socket_set_inheritable (int fd) +{ + int i; + i = fcntl (fd, F_GETFD); + if (i < 0) + return GNUNET_SYSERR; + if (i == (i | FD_CLOEXEC)) + return GNUNET_OK; + i |= FD_CLOEXEC; + if (fcntl (fd, F_SETFD, i) < 0) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + + +/** + * Set if a socket should use blocking or non-blocking IO. + * + * @param fd socket + * @param doBlock blocking mode + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +int +socket_set_blocking (int fd, + int doBlock) +{ + int flags = fcntl (fd, F_GETFL); + if (flags == -1) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (doBlock) + flags &= ~O_NONBLOCK; + + else + flags |= O_NONBLOCK; + if (0 != fcntl (fd, + F_SETFL, + flags)) + + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + /** * The main function of the taler-exchange-httpd server ("the exchange"). * @@ -673,17 +797,63 @@ main (int argc, exchange_serve_process_config ()) return 1; - mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, - serve_port, - NULL, NULL, - &handle_mhd_request, NULL, - MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL, - MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL, - MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout, + if (NULL != serve_unixpath) + { + int sock; + struct sockaddr_un *un; + + un = GNUNET_new (struct sockaddr_un); + un->sun_family = AF_UNIX; + sock = socket (AF_UNIX, SOCK_STREAM, 0); + if (-1 == sock) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "socket"); + return 1; + } + strncpy (un->sun_path, serve_unixpath, sizeof (un->sun_path) - 1); + socket_set_inheritable (sock); + socket_set_blocking (sock, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Binding to unix-domain socket '%s'\n", serve_unixpath); + if (0 != bind (sock, un, sizeof (*un))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "bind"); + return 1; + } + if (0 != listen (sock, UNIX_BACKLOG)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "listen"); + return 1; + } + mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 0, + NULL, NULL, + &handle_mhd_request, NULL, + MHD_OPTION_LISTEN_SOCKET, sock, + MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL, + MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout, #if HAVE_DEVELOPER - MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL, + MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL, #endif - MHD_OPTION_END); + MHD_OPTION_END); + } + else + { + mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + serve_port, + NULL, NULL, + &handle_mhd_request, NULL, + MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL, + MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout, +#if HAVE_DEVELOPER + MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL, +#endif + MHD_OPTION_END); + } if (NULL == mydaemon) {