commit 7c735289338a3cd9bfd7afa4131153a9d113b7ea
parent 70201b593e8746b576418b81ee081c6fec9103f3
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date: Mon, 21 Dec 2020 13:28:32 +0300
Fixed: avoided SIGPIPE if possiible
Added blocking of SIGPIPE on daemon threads.
Added tracking of SIGPIPE suppressions on sockets.
Added fallback to normal send() instead of sendfile() and writev() if
SIGPIPE is not blocked.
Diffstat:
6 files changed, 182 insertions(+), 34 deletions(-)
diff --git a/configure.ac b/configure.ac
@@ -556,11 +556,30 @@ AS_CASE([[$with_threads]],
)
# Check for posix threads support, regardless of configure parameters as
-# testsuite use only posix threads.
+# testsuite uses only posix threads.
AX_PTHREAD(
[
mhd_have_posix_threads='yes'
AC_DEFINE([[HAVE_PTHREAD_H]],[[1]],[Define to 1 if you have the <pthread.h> header file.])
+ AC_CACHE_CHECK([[whether pthread_sigmask(3) is available]],
+ [[mhd_cv_func_pthread_sigmask]], [dnl
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <signal.h>]],
+ [[
+ sigset_t nset, oset;
+ sigemptyset (&nset);
+ sigaddset (&nset, SIGPIPE);
+ if (0 != pthread_sigmask(SIG_BLOCK, &nset, &oset)) return 1;
+ ]])],
+ [[mhd_cv_func_pthread_sigmask="yes"]],[[mhd_cv_func_pthread_sigmask="no"]])
+ LIBS="${save_LIBS}"
+ CFLAGS="${save_CFLAGS}"
+ ])
+ AS_VAR_IF([mhd_cv_func_pthread_sigmask],["yes"],
+ [AC_DEFINE([[HAVE_PTHREAD_SIGMASK]],[[1]],[Define to 1 if you have the pthread_sigmask(3) function.])])
],[[mhd_have_posix_threads='no']])
AM_CONDITIONAL([HAVE_POSIX_THREADS],[test "x$mhd_have_posix_threads" = "xyes"])
@@ -966,6 +985,7 @@ AC_CHECK_HEADERS_ONCE([fcntl.h math.h errno.h limits.h stdio.h locale.h sys/stat
AC_CHECK_HEADERS([sys/types.h sys/time.h sys/msg.h time.h sys/mman.h sys/ioctl.h \
sys/socket.h sys/select.h netdb.h netinet/in.h netinet/ip.h netinet/tcp.h arpa/inet.h \
endian.h machine/endian.h sys/endian.h sys/param.h sys/machine.h sys/byteorder.h machine/param.h sys/isa_defs.h \
+ signal.h \
inttypes.h stddef.h unistd.h \
sockLib.h inetLib.h net/if.h], [], [], [AC_INCLUDES_DEFAULT])
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
@@ -3964,7 +3964,11 @@ MHD_queue_response (struct MHD_Connection *connection,
#if defined(_MHD_HAVE_SENDFILE)
if ( (response->fd == -1) ||
(response->is_pipe) ||
- (0 != (connection->daemon->options & MHD_USE_TLS)) )
+ (0 != (connection->daemon->options & MHD_USE_TLS))
+#if ! defined(MHD_WINSOCK_SOCKETS) && defined(HAVE_SEND_SIGPIPE_SUPPRESS)
+ || (! daemon->sigpipe_blocked && ! connection->sk_spipe_suppress)
+#endif /* ! MHD_WINSOCK_SOCKETS && ! HAVE_SEND_SIGPIPE_SUPPRESS */
+ )
connection->resp_sender = MHD_resp_sender_std;
else
connection->resp_sender = MHD_resp_sender_sendfile;
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
@@ -64,6 +64,12 @@
#include <windows.h>
#endif
+#ifdef MHD_USE_POSIX_THREADS
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif /* HAVE_SIGNAL_H */
+#endif /* MHD_USE_POSIX_THREADS */
+
/**
* Default connection limit.
*/
@@ -2359,6 +2365,8 @@ psk_gnutls_adapter (gnutls_session_t session,
* @param addrlen number of bytes in @a addr
* @param external_add indicate that socket has been added externally
* @param non_blck indicate that socket in non-blocking mode
+ * @param sk_spipe_supprs indicate that the @a client_socket has
+ * set SIGPIPE suppression
* @return pointer to the connection on success, NULL if this daemon could
* not handle the connection (i.e. malloc failed, etc).
* The socket will be closed in case of error; 'errno' is
@@ -2370,30 +2378,12 @@ new_connection_prepare_ (struct MHD_Daemon *daemon,
const struct sockaddr *addr,
socklen_t addrlen,
bool external_add,
- bool non_blck)
+ bool non_blck,
+ bool sk_spipe_supprs)
{
struct MHD_Connection *connection;
int eno = 0;
-#ifdef MHD_socket_nosignal_
- if (! MHD_socket_nosignal_ (client_socket))
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _ ("Failed to set SO_NOSIGPIPE on accepted socket: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
-#ifndef MSG_NOSIGNAL
- /* Cannot use socket as it can produce SIGPIPE. */
-#ifdef ENOTSOCK
- errno = ENOTSOCK;
-#endif /* ENOTSOCK */
- return NULL;
-#endif /* ! MSG_NOSIGNAL */
- }
-#endif /* MHD_socket_nosignal_ */
-
-
#ifdef HAVE_MESSAGES
#if _MHD_DEBUG_CONNECT
MHD_DLOG (daemon,
@@ -2491,6 +2481,7 @@ new_connection_prepare_ (struct MHD_Daemon *daemon,
connection->addr_len = addrlen;
connection->socket_fd = client_socket;
connection->sk_nonblck = non_blck;
+ connection->sk_spipe_suppress = sk_spipe_supprs;
connection->daemon = daemon;
connection->last_activity = MHD_monotonic_sec_counter ();
@@ -2852,6 +2843,8 @@ cleanup:
* @param external_add perform additional operations needed due
* to the application calling us directly
* @param non_blck indicate that socket in non-blocking mode
+ * @param sk_spipe_supprs indicate that the @a client_socket has
+ * set SIGPIPE suppression
* @return #MHD_YES on success, #MHD_NO if this daemon could
* not handle the connection (i.e. malloc failed, etc).
* The socket will be closed in any case; 'errno' is
@@ -2863,7 +2856,8 @@ internal_add_connection (struct MHD_Daemon *daemon,
const struct sockaddr *addr,
socklen_t addrlen,
bool external_add,
- bool non_blck)
+ bool non_blck,
+ bool sk_spipe_supprs)
{
struct MHD_Connection *connection;
@@ -2904,7 +2898,8 @@ internal_add_connection (struct MHD_Daemon *daemon,
}
connection = new_connection_prepare_ (daemon, client_socket, addr, addrlen,
- external_add, non_blck);
+ external_add, non_blck,
+ sk_spipe_supprs);
if (NULL == connection)
return MHD_NO;
@@ -3327,6 +3322,7 @@ MHD_add_connection (struct MHD_Daemon *daemon,
socklen_t addrlen)
{
bool sk_nonbl;
+ bool sk_spipe_supprs;
/* NOT thread safe with internal thread. TODO: fix thread safety. */
if ((0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) &&
@@ -3357,6 +3353,38 @@ MHD_add_connection (struct MHD_Daemon *daemon,
else
sk_nonbl = true;
+#ifndef MHD_WINSOCK_SOCKETS
+ sk_spipe_supprs = false;
+#else /* MHD_WINSOCK_SOCKETS */
+ sk_spipe_supprs = true; /* Nothing to suppress on W32 */
+#endif /* MHD_WINSOCK_SOCKETS */
+#if defined(MHD_socket_nosignal_)
+ if (! sk_spipe_supprs && ! MHD_socket_nosignal_ (s))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _ (
+ "Failed to suppress SIGPIPE on new client socket: %s\n"),
+ MHD_socket_last_strerr_ ());
+#else /* ! HAVE_MESSAGES */
+ (void) 0; /* Mute compiler warning */
+#endif /* ! HAVE_MESSAGES */
+#ifndef MSG_NOSIGNAL
+ /* Application expects that SIGPIPE will be suppressed,
+ * but suppression failed and SIGPIPE cannot be suppressed with send(). */
+ if (! daemon->sigpipe_blocked)
+ {
+ int err = MHD_socket_get_error_ ();
+ MHD_socket_close_ (s);
+ MHD_socket_fset_error_ (err);
+ return MHD_NO;
+ }
+#endif /* MSG_NOSIGNAL */
+ }
+ else
+ sk_spipe_supprs = true;
+#endif /* MHD_socket_nosignal_ */
+
if ( (0 != (daemon->options & MHD_USE_TURBO)) &&
(! MHD_socket_noninheritable_ (client_socket)) )
{
@@ -3383,7 +3411,8 @@ MHD_add_connection (struct MHD_Daemon *daemon,
addr,
addrlen,
true,
- sk_nonbl);
+ sk_nonbl,
+ sk_spipe_supprs);
}
/* all pools are at their connection limit, must refuse */
MHD_socket_close_chk_ (client_socket);
@@ -3399,7 +3428,8 @@ MHD_add_connection (struct MHD_Daemon *daemon,
addr,
addrlen,
true,
- sk_nonbl);
+ sk_nonbl,
+ sk_spipe_supprs);
}
@@ -3430,6 +3460,7 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
MHD_socket s;
MHD_socket fd;
bool sk_nonbl;
+ bool sk_spipe_supprs;
mhd_assert ( (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) || \
MHD_thread_ID_match_current_ (daemon->pid) );
@@ -3448,11 +3479,21 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
SOCK_CLOEXEC_OR_ZERO | SOCK_NONBLOCK_OR_ZERO
| SOCK_NOSIGPIPE_OR_ZERO);
sk_nonbl = (SOCK_NONBLOCK_OR_ZERO != 0);
+#ifndef MHD_WINSOCK_SOCKETS
+ sk_spipe_supprs = (SOCK_NOSIGPIPE_OR_ZERO != 0);
+#else /* MHD_WINSOCK_SOCKETS */
+ sk_spipe_supprs = true; /* Nothing to suppress on W32 */
+#endif /* MHD_WINSOCK_SOCKETS */
#else /* ! USE_ACCEPT4 */
s = accept (fd,
addr,
&addrlen);
sk_nonbl = false;
+#ifndef MHD_WINSOCK_SOCKETS
+ sk_spipe_supprs = false;
+#else /* MHD_WINSOCK_SOCKETS */
+ sk_spipe_supprs = true; /* Nothing to suppress on W32 */
+#endif /* MHD_WINSOCK_SOCKETS */
#endif /* ! USE_ACCEPT4 */
if ( (MHD_INVALID_SOCKET == s) ||
(addrlen <= 0) )
@@ -3531,6 +3572,30 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
#endif
}
#endif /* !USE_ACCEPT4 || !SOCK_CLOEXEC */
+#if defined(MHD_socket_nosignal_)
+ if (! sk_spipe_supprs && ! MHD_socket_nosignal_ (s))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _ (
+ "Failed to suppress SIGPIPE on incoming connection socket: %s\n"),
+ MHD_socket_last_strerr_ ());
+#else /* ! HAVE_MESSAGES */
+ (void) 0; /* Mute compiler warning */
+#endif /* ! HAVE_MESSAGES */
+#ifndef MSG_NOSIGNAL
+ /* Application expects that SIGPIPE will be suppressed,
+ * but suppression failed and SIGPIPE cannot be suppressed with send(). */
+ if (! daemon->sigpipe_blocked)
+ {
+ MHD_socket_close_ (s);
+ return MHD_NO;
+ }
+#endif /* MSG_NOSIGNAL */
+ }
+ else
+ sk_spipe_supprs = true;
+#endif /* MHD_socket_nosignal_ */
#ifdef HAVE_MESSAGES
#if _MHD_DEBUG_CONNECT
MHD_DLOG (daemon,
@@ -3543,7 +3608,8 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
addr,
addrlen,
false,
- sk_nonbl);
+ sk_nonbl,
+ sk_spipe_supprs);
return MHD_YES;
}
@@ -5022,8 +5088,29 @@ static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
MHD_polling_thread (void *cls)
{
struct MHD_Daemon *daemon = cls;
+#ifdef HAVE_PTHREAD_SIGMASK
+ sigset_t s_mask;
+ int err;
+#endif /* HAVE_PTHREAD_SIGMASK */
MHD_thread_init_ (&(daemon->pid));
+#ifdef HAVE_PTHREAD_SIGMASK
+ if ((0 == sigemptyset (&s_mask)) &&
+ (0 == sigaddset (&s_mask, SIGPIPE)))
+ {
+ err = pthread_sigmask (SIG_BLOCK, &s_mask, NULL);
+ }
+ else
+ err = errno;
+ if (0 == err)
+ daemon->sigpipe_blocked = true;
+#ifdef HAVE_MESSAGES
+ else
+ MHD_DLOG (daemon,
+ _ ("Failed to block SIGPIPE on daemon thread: %s\n"),
+ MHD_strerror_ (errno));
+#endif /* HAVE_MESSAGES */
+#endif /* HAVE_PTHREAD_SIGMASK */
while (! daemon->shutdown)
{
if (0 != (daemon->options & MHD_USE_POLL))
@@ -6116,6 +6203,13 @@ MHD_start_daemon_va (unsigned int flags,
daemon->custom_error_log = &MHD_default_logger_;
daemon->custom_error_log_cls = stderr;
#endif
+#ifndef MHD_WINSOCK_SOCKETS
+ daemon->sigpipe_blocked = false;
+#else /* MHD_WINSOCK_SOCKETS */
+ /* There is no SIGPIPE on W32, nothing to block. */
+ daemon->sigpipe_blocked = true;
+#endif /* _WIN32 && ! __CYGWIN__ */
+
if ( (0 != (*pflags & MHD_USE_THREAD_PER_CONNECTION)) &&
(0 == (*pflags & MHD_USE_INTERNAL_POLLING_THREAD)) )
{
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
@@ -929,6 +929,11 @@ struct MHD_Connection
bool sk_nonblck;
/**
+ * true if connection socket has set SIGPIPE suppression
+ */
+ bool sk_spipe_suppress;
+
+ /**
* Tracks TCP_CORK / TCP_NOPUSH of the connection socket.
*/
enum MHD_tristate sk_corked;
@@ -1697,6 +1702,11 @@ struct MHD_Daemon
*/
int strict_for_client;
+ /**
+ * True if SIGPIPE is blocked
+ */
+ bool sigpipe_blocked;
+
#ifdef HTTPS_SUPPORT
#ifdef UPGRADE_SUPPORT
/**
diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c
@@ -815,11 +815,19 @@ MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
uint32_t vec_sent;
int err;
#endif /* _WIN32 */
+ bool no_vec; /* Is vector-send() disallowed? */
+
+ no_vec = false;
#ifdef HTTPS_SUPPORT
- const bool no_vec = (connection->daemon->options & MHD_USE_TLS);
-#else /* ! HTTPS_SUPPORT */
- const bool no_vec = false;
-#endif /* ! HTTPS_SUPPORT */
+ no_vec = no_vec || (connection->daemon->options & MHD_USE_TLS);
+#endif /* HTTPS_SUPPORT */
+#if ! defined(MHD_WINSOCK_SOCKETS) && \
+ (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL)) && \
+ defined(HAVE_SEND_SIGPIPE_SUPPRESS)
+ no_vec = no_vec || (! connection->daemon->sigpipe_blocked &&
+ ! connection->sk_spipe_suppress);
+#endif /* !MHD_WINSOCK_SOCKETS && (!HAVE_SENDMSG || ! MSG_NOSIGNAL)
+ && !HAVE_SEND_SIGPIPE_SUPPRESS */
#endif /* _MHD_USE_SEND_VEC */
mhd_assert ( (NULL != body) || (0 == body_size) );
diff --git a/src/microhttpd/mhd_sockets.h b/src/microhttpd/mhd_sockets.h
@@ -924,9 +924,20 @@ static const int _MHD_socket_int_one = 1;
#define MHD_socket_nosignal_(sock) \
(! setsockopt ((sock),SOL_SOCKET,SO_NOSIGPIPE,&_MHD_socket_int_one, \
sizeof(_MHD_socket_int_one)))
-#elif defined(MHD_POSIX_SOCKETS) && defined(SOCK_NOSIGPIPE) && \
- defined(SOCK_CLOEXEC)
-#endif
+#endif /* SOL_SOCKET && SO_NOSIGPIPE */
+
+
+#if defined(MHD_WINSOCK_SOCKETS) || defined(MHD_socket_nosignal_) || \
+ defined(MSG_NOSIGNAL)
+/**
+ * Indicate that SIGPIPE can be suppressed for normal send() by flags
+ * or socket options.
+ * If this macro is undefined, MHD cannot suppress SIGPIPE for normal
+ * processing so sendfile() or writev() calls is not avoided.
+ */
+#define HAVE_SEND_SIGPIPE_SUPPRESS 1
+#endif /* MHD_WINSOCK_SOCKETS || MHD_socket_nosignal_ || MSG_NOSIGNAL */
+
/**
* Create a listen socket, with noninheritable flag if possible.
@@ -937,4 +948,5 @@ static const int _MHD_socket_int_one = 1;
MHD_socket
MHD_socket_create_listen_ (int pf);
+
#endif /* ! MHD_SOCKETS_H */