blob: 9247fbabaa039961d2a7812ca83c79115c1fc169 [file] [log] [blame]
/*
This file is part of libmicrohttpd
Copyright (C) 2007-2014 Daniel Pittman and Christian Grothoff
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file microhttpd/daemon.c
* @brief A minimal-HTTP server library
* @author Daniel Pittman
* @author Christian Grothoff
*/
#if defined(_WIN32) && !defined(__CYGWIN__)
/* override small default value */
#define FD_SETSIZE 1024
#define MHD_DEFAULT_FD_SETSIZE 64
#else
#define MHD_DEFAULT_FD_SETSIZE FD_SETSIZE
#endif
#include "platform.h"
#include "internal.h"
#include "response.h"
#include "connection.h"
#include "memorypool.h"
#include <limits.h>
#include "autoinit_funcs.h"
#if HAVE_SEARCH_H
#include <search.h>
#else
#include "tsearch.h"
#endif
#if HTTPS_SUPPORT
#include "connection_https.h"
#include <openssl/ssl.h>
#endif
#if defined(HAVE_POLL_H) && defined(HAVE_POLL)
#include <poll.h>
#endif
#ifdef LINUX
#include <sys/sendfile.h>
#endif
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif /* !WIN32_LEAN_AND_MEAN */
#include <windows.h>
#include <process.h>
#endif
#ifndef HAVE_ACCEPT4
#define HAVE_ACCEPT4 0
#endif
/**
* Default connection limit.
*/
#ifndef WINDOWS
#define MHD_MAX_CONNECTIONS_DEFAULT FD_SETSIZE - 4
#else
#define MHD_MAX_CONNECTIONS_DEFAULT FD_SETSIZE
#endif
/**
* Default memory allowed per connection.
*/
#define MHD_POOL_SIZE_DEFAULT (32 * 1024)
#ifdef TCP_FASTOPEN
/**
* Default TCP fastopen queue size.
*/
#define MHD_TCP_FASTOPEN_QUEUE_SIZE_DEFAULT 10
#endif
/**
* Print extra messages with reasons for closing
* sockets? (only adds non-error messages).
*/
#define DEBUG_CLOSE MHD_NO
/**
* Print extra messages when establishing
* connections? (only adds non-error messages).
*/
#define DEBUG_CONNECT MHD_NO
#ifndef LINUX
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
#endif
#ifndef SOCK_CLOEXEC
#define SOCK_CLOEXEC 0
#endif
#ifndef EPOLL_CLOEXEC
#define EPOLL_CLOEXEC 0
#endif
/**
* Default implementation of the panic function,
* prints an error message and aborts.
*
* @param cls unused
* @param file name of the file with the problem
* @param line line number with the problem
* @param reason error message with details
*/
static void
mhd_panic_std (void *cls,
const char *file,
unsigned int line,
const char *reason)
{
#if HAVE_MESSAGES
fprintf (stderr, "Fatal error in GNU libmicrohttpd %s:%u: %s\n",
file, line, reason);
#endif
abort ();
}
/**
* Handler for fatal errors.
*/
MHD_PanicCallback mhd_panic;
/**
* Closure argument for "mhd_panic".
*/
void *mhd_panic_cls;
#ifdef _WIN32
/**
* Track initialization of winsock
*/
static int mhd_winsock_inited_ = 0;
#endif
/**
* Trace up to and return master daemon. If the supplied daemon
* is a master, then return the daemon itself.
*
* @param daemon handle to a daemon
* @return master daemon handle
*/
static struct MHD_Daemon*
MHD_get_master (struct MHD_Daemon *daemon)
{
while (NULL != daemon->master)
daemon = daemon->master;
return daemon;
}
/**
* Maintain connection count for single address.
*/
struct MHD_IPCount
{
/**
* Address family. AF_INET or AF_INET6 for now.
*/
int family;
/**
* Actual address.
*/
union
{
/**
* IPv4 address.
*/
struct in_addr ipv4;
#if HAVE_INET6
/**
* IPv6 address.
*/
struct in6_addr ipv6;
#endif
} addr;
/**
* Counter.
*/
unsigned int count;
};
/**
* Lock shared structure for IP connection counts and connection DLLs.
*
* @param daemon handle to daemon where lock is
*/
static void
MHD_ip_count_lock (struct MHD_Daemon *daemon)
{
if (MHD_YES != MHD_mutex_lock_(&daemon->per_ip_connection_mutex))
{
MHD_PANIC ("Failed to acquire IP connection limit mutex\n");
}
}
/**
* Unlock shared structure for IP connection counts and connection DLLs.
*
* @param daemon handle to daemon where lock is
*/
static void
MHD_ip_count_unlock (struct MHD_Daemon *daemon)
{
if (MHD_YES != MHD_mutex_unlock_(&daemon->per_ip_connection_mutex))
{
MHD_PANIC ("Failed to release IP connection limit mutex\n");
}
}
/**
* Tree comparison function for IP addresses (supplied to tsearch() family).
* We compare everything in the struct up through the beginning of the
* 'count' field.
*
* @param a1 first address to compare
* @param a2 second address to compare
* @return -1, 0 or 1 depending on result of compare
*/
static int
MHD_ip_addr_compare (const void *a1, const void *a2)
{
return memcmp (a1, a2, offsetof (struct MHD_IPCount, count));
}
/**
* Parse address and initialize 'key' using the address.
*
* @param addr address to parse
* @param addrlen number of bytes in addr
* @param key where to store the parsed address
* @return #MHD_YES on success and #MHD_NO otherwise (e.g., invalid address type)
*/
static int
MHD_ip_addr_to_key (const struct sockaddr *addr,
socklen_t addrlen,
struct MHD_IPCount *key)
{
memset(key, 0, sizeof(*key));
/* IPv4 addresses */
if (sizeof (struct sockaddr_in) == addrlen)
{
const struct sockaddr_in *addr4 = (const struct sockaddr_in*) addr;
key->family = AF_INET;
memcpy (&key->addr.ipv4, &addr4->sin_addr, sizeof(addr4->sin_addr));
return MHD_YES;
}
#if HAVE_INET6
/* IPv6 addresses */
if (sizeof (struct sockaddr_in6) == addrlen)
{
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*) addr;
key->family = AF_INET6;
memcpy (&key->addr.ipv6, &addr6->sin6_addr, sizeof(addr6->sin6_addr));
return MHD_YES;
}
#endif
/* Some other address */
return MHD_NO;
}
/**
* Check if IP address is over its limit.
*
* @param daemon handle to daemon where connection counts are tracked
* @param addr address to add (or increment counter)
* @param addrlen number of bytes in addr
* @return Return #MHD_YES if IP below limit, #MHD_NO if IP has surpassed limit.
* Also returns #MHD_NO if fails to allocate memory.
*/
static int
MHD_ip_limit_add (struct MHD_Daemon *daemon,
const struct sockaddr *addr,
socklen_t addrlen)
{
struct MHD_IPCount *key;
void **nodep;
void *node;
int result;
daemon = MHD_get_master (daemon);
/* Ignore if no connection limit assigned */
if (0 == daemon->per_ip_connection_limit)
return MHD_YES;
if (NULL == (key = malloc (sizeof(*key))))
return MHD_NO;
/* Initialize key */
if (MHD_NO == MHD_ip_addr_to_key (addr, addrlen, key))
{
/* Allow unhandled address types through */
free (key);
return MHD_YES;
}
MHD_ip_count_lock (daemon);
/* Search for the IP address */
if (NULL == (nodep = tsearch (key,
&daemon->per_ip_connection_count,
&MHD_ip_addr_compare)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to add IP connection count node\n");
#endif
MHD_ip_count_unlock (daemon);
free (key);
return MHD_NO;
}
node = *nodep;
/* If we got an existing node back, free the one we created */
if (node != key)
free(key);
key = (struct MHD_IPCount *) node;
/* Test if there is room for another connection; if so,
* increment count */
result = (key->count < daemon->per_ip_connection_limit);
if (MHD_YES == result)
++key->count;
MHD_ip_count_unlock (daemon);
return result;
}
/**
* Decrement connection count for IP address, removing from table
* count reaches 0.
*
* @param daemon handle to daemon where connection counts are tracked
* @param addr address to remove (or decrement counter)
* @param addrlen number of bytes in @a addr
*/
static void
MHD_ip_limit_del (struct MHD_Daemon *daemon,
const struct sockaddr *addr,
socklen_t addrlen)
{
struct MHD_IPCount search_key;
struct MHD_IPCount *found_key;
void **nodep;
daemon = MHD_get_master (daemon);
/* Ignore if no connection limit assigned */
if (0 == daemon->per_ip_connection_limit)
return;
/* Initialize search key */
if (MHD_NO == MHD_ip_addr_to_key (addr, addrlen, &search_key))
return;
MHD_ip_count_lock (daemon);
/* Search for the IP address */
if (NULL == (nodep = tfind (&search_key,
&daemon->per_ip_connection_count,
&MHD_ip_addr_compare)))
{
/* Something's wrong if we couldn't find an IP address
* that was previously added */
MHD_PANIC ("Failed to find previously-added IP address\n");
}
found_key = (struct MHD_IPCount *) *nodep;
/* Validate existing count for IP address */
if (0 == found_key->count)
{
MHD_PANIC ("Previously-added IP address had 0 count\n");
}
/* Remove the node entirely if count reduces to 0 */
if (0 == --found_key->count)
{
tdelete (found_key,
&daemon->per_ip_connection_count,
&MHD_ip_addr_compare);
free (found_key);
}
MHD_ip_count_unlock (daemon);
}
#if HTTPS_SUPPORT
static ssize_t
recv_param_adapter (struct MHD_Connection *connection,
void *other,
size_t i);
static ssize_t
send_param_adapter (struct MHD_Connection *connection,
const void *other,
size_t i);
// Internal functions for implementing OpenSSL BIO.
static int
MHD_bio_write (BIO* bio, const char* buf, int size)
{
struct MHD_Connection* connection = (struct MHD_Connection*)bio->ptr;
BIO_clear_retry_flags (bio);
ssize_t written = send_param_adapter (connection, buf, size);
if (written < size)
{
BIO_set_retry_write (bio);
}
return written;
}
static int
MHD_bio_read (BIO* bio, char* buf, int size)
{
struct MHD_Connection* connection = (struct MHD_Connection*)bio->ptr;
BIO_clear_retry_flags (bio);
ssize_t read = recv_param_adapter (connection, buf, size);
if (read < size)
{
BIO_set_retry_read (bio);
}
return read;
}
static long
MHD_bio_ctrl (BIO* bio, int cmd, long num, void* ptr)
{
if (cmd == BIO_CTRL_FLUSH)
{
return 1;
}
return 0;
}
static int
MHD_bio_new (BIO* bio)
{
bio->shutdown = 0;
bio->init = 0;
bio->num = -1; // not used.
return 1;
}
static int
MHD_bio_free (BIO* bio)
{
if (!bio)
return 0;
if (bio->init)
{
bio->ptr = NULL;
bio->init = 0;
}
return 1;
}
// Describes a BIO built on [send|recv]_param_adapter().
BIO_METHOD MHD_bio_method =
{
BIO_TYPE_SOURCE_SINK,
"mhd", // name
MHD_bio_write, // write function
MHD_bio_read, // read function
NULL, // puts function, not implemented
NULL, // gets function, not implemented
MHD_bio_ctrl, // control function
MHD_bio_new, // creation
MHD_bio_free, // free
NULL, // callback function, not used
};
/**
* Callback for receiving data from the socket.
*
* @param connection the MHD_Connection structure
* @param other where to write received data to
* @param i maximum size of other (in bytes)
* @return number of bytes actually received
*/
static ssize_t
recv_tls_adapter (struct MHD_Connection *connection, void *other, size_t i)
{
int res;
if (MHD_YES == connection->tls_read_ready)
{
connection->daemon->num_tls_read_ready--;
connection->tls_read_ready = MHD_NO;
}
res = SSL_read (connection->tls_session, other, i);
if ( res < 0 && SSL_want_read (connection->tls_session) )
{
MHD_set_socket_errno_ (EINTR);
#if EPOLL_SUPPORT
connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY;
#endif
return -1;
}
if (res < 0)
{
/* Likely 'GNUTLS_E_INVALID_SESSION' (client communication
disrupted); set errno to something caller will interpret
correctly as a hard error */
MHD_set_socket_errno_ (ECONNRESET);
return res;
}
if (res == i)
{
connection->tls_read_ready = MHD_YES;
connection->daemon->num_tls_read_ready++;
}
return res;
}
/**
* Callback for writing data to the socket.
*
* @param connection the MHD connection structure
* @param other data to write
* @param i number of bytes to write
* @return actual number of bytes written
*/
static ssize_t
send_tls_adapter (struct MHD_Connection *connection,
const void *other, size_t i)
{
int res;
res = SSL_write (connection->tls_session, other, i);
if ( res < 0 && SSL_want_write (connection->tls_session) )
{
MHD_set_socket_errno_ (EINTR);
#if EPOLL_SUPPORT
connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
#endif
return -1;
}
if (res < 0)
{
/* some other GNUTLS error, should set 'errno'; as we do not
really understand the error (not listed in GnuTLS
documentation explicitly), we set 'errno' to something that
will cause the connection to fail. */
MHD_set_socket_errno_ (ECONNRESET);
return -1;
}
return res;
}
/**
* Initialize security aspects of the HTTPS daemon
*
* @param daemon handle to daemon to initialize
* @return 0 on success
*/
static int
MHD_TLS_init (struct MHD_Daemon *daemon)
{
int ret;
daemon->tls_context = SSL_CTX_new (TLSv1_2_server_method());
if (NULL == daemon->tls_context)
return -1;
if (NULL != daemon->https_mem_trust)
{
ret = 0;
BIO* mem_bio = BIO_new_mem_buf ((void*)daemon->https_mem_trust, -1);
X509* x509 = PEM_read_bio_X509 (mem_bio, NULL, NULL, NULL);
BIO_free(mem_bio);
if (x509 != NULL)
{
ret = SSL_CTX_add_client_CA (daemon->tls_context, x509);
}
if (ret == 0)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Bad trust certificate format\n");
#endif
return -1;
}
}
if (NULL != daemon->https_mem_dhparams)
{
ret = 0;
BIO* mem_bio = BIO_new_mem_buf ((void*)daemon->https_mem_dhparams, -1);
DH* dh = PEM_read_bio_DHparams (mem_bio, NULL, NULL, NULL);
BIO_free (mem_bio);
if (dh != NULL)
{
ret = SSL_CTX_set_tmp_dh (daemon->tls_context, dh);
}
if (ret == 0)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Bad DH parameters format\n");
#endif
return -1;
}
}
/* certificate & key loaded from memory */
if ( (NULL != daemon->https_mem_cert) &&
(NULL != daemon->https_mem_key) )
{
ret = 0;
BIO* mem_bio = BIO_new_mem_buf ((void*)daemon->https_mem_key, -1);
EVP_PKEY* key = PEM_read_bio_PrivateKey (mem_bio, NULL, NULL,
(void*)daemon->https_key_password);
BIO_free (mem_bio);
if (key != NULL)
{
ret = SSL_CTX_use_PrivateKey (daemon->tls_context, key);
}
if (ret == 0)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Bad private key format\n");
#endif
return -1;
}
ret = 0;
mem_bio = BIO_new_mem_buf ((void*)daemon->https_mem_cert, -1);
X509* x509 = PEM_read_bio_X509 (mem_bio, NULL, NULL, NULL);
BIO_free (mem_bio);
if (x509 != NULL)
{
ret = SSL_CTX_use_certificate (daemon->tls_context, x509);
}
if (ret == 0)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Bad certificate format\n");
#endif
return -1;
}
if (1 != SSL_CTX_check_private_key (daemon->tls_context))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Invalid key / certificate combination\n");
#endif
return -1;
}
}
else
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"You need to specify a certificate and key location\n");
#endif
return -1;
}
if (NULL != daemon->https_mem_cipher)
{
ret = SSL_CTX_set_cipher_list (daemon->tls_context,
daemon->https_mem_cipher);
if (ret == 0)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Bad cipher string format\n");
#endif
return -1;
}
}
else
{
ret = SSL_CTX_set_cipher_list (daemon->tls_context,
"HIGH!SHA1!DH@STRENGTH");
if (ret == 0)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to setup default cipher string\n");
#endif
return -1;
}
}
return 0;
}
static void
MHD_TLS_deinit (struct MHD_Daemon *daemon)
{
SSL_CTX_free (daemon->tls_context);
}
#endif
/**
* Add @a fd to the @a set. If @a fd is
* greater than @a max_fd, set @a max_fd to @a fd.
*
* @param fd file descriptor to add to the @a set
* @param set set to modify
* @param max_fd maximum value to potentially update
* @param fd_setsize value of FD_SETSIZE
* @return #MHD_YES on success, #MHD_NO otherwise
*/
static int
add_to_fd_set (MHD_socket fd,
fd_set *set,
MHD_socket *max_fd,
unsigned int fd_setsize)
{
if (NULL == set)
return MHD_NO;
#ifdef MHD_WINSOCK_SOCKETS
if (set->fd_count >= fd_setsize)
{
if (FD_ISSET(fd, set))
return MHD_YES;
else
return MHD_NO;
}
#else // ! MHD_WINSOCK_SOCKETS
if (fd >= fd_setsize)
return MHD_NO;
#endif // ! MHD_WINSOCK_SOCKETS
FD_SET (fd, set);
if ( (NULL != max_fd) && (MHD_INVALID_SOCKET != fd) &&
((fd > *max_fd) || (MHD_INVALID_SOCKET == *max_fd)) )
*max_fd = fd;
return MHD_YES;
}
#undef MHD_get_fdset
/**
* Obtain the `select()` sets for this daemon.
* Daemon's FDs will be added to fd_sets. To get only
* daemon FDs in fd_sets, call FD_ZERO for each fd_set
* before calling this function. FD_SETSIZE is assumed
* to be platform's default.
*
* @param daemon daemon to get sets from
* @param read_fd_set read set
* @param write_fd_set write set
* @param except_fd_set except set
* @param max_fd increased to largest FD added (if larger
* than existing value); can be NULL
* @return #MHD_YES on success, #MHD_NO if this
* daemon was not started with the right
* options for this call or any FD didn't
* fit fd_set.
* @ingroup event
*/
int
MHD_get_fdset (struct MHD_Daemon *daemon,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *except_fd_set,
MHD_socket *max_fd)
{
return MHD_get_fdset2(daemon, read_fd_set,
write_fd_set, except_fd_set,
max_fd, MHD_DEFAULT_FD_SETSIZE);
}
/**
* Obtain the `select()` sets for this daemon.
* Daemon's FDs will be added to fd_sets. To get only
* daemon FDs in fd_sets, call FD_ZERO for each fd_set
* before calling this function. Passing custom FD_SETSIZE
* as @a fd_setsize allow usage of larger/smaller than
* platform's default fd_sets.
*
* @param daemon daemon to get sets from
* @param read_fd_set read set
* @param write_fd_set write set
* @param except_fd_set except set
* @param max_fd increased to largest FD added (if larger
* than existing value); can be NULL
* @param fd_setsize value of FD_SETSIZE
* @return #MHD_YES on success, #MHD_NO if this
* daemon was not started with the right
* options for this call or any FD didn't
* fit fd_set.
* @ingroup event
*/
int
MHD_get_fdset2 (struct MHD_Daemon *daemon,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *except_fd_set,
MHD_socket *max_fd,
unsigned int fd_setsize)
{
struct MHD_Connection *pos;
if ( (NULL == daemon)
|| (NULL == read_fd_set)
|| (NULL == write_fd_set)
|| (MHD_YES == daemon->shutdown)
|| (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
|| (0 != (daemon->options & MHD_USE_POLL)))
return MHD_NO;
#if EPOLL_SUPPORT
if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
{
/* we're in epoll mode, use the epoll FD as a stand-in for
the entire event set */
return add_to_fd_set (daemon->epoll_fd, read_fd_set, max_fd, fd_setsize);
}
#endif
if (MHD_INVALID_SOCKET != daemon->socket_fd &&
MHD_YES != add_to_fd_set (daemon->socket_fd, read_fd_set, max_fd, fd_setsize))
return MHD_NO;
for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
{
switch (pos->event_loop_info)
{
case MHD_EVENT_LOOP_INFO_READ:
if (MHD_YES != add_to_fd_set (pos->socket_fd, read_fd_set, max_fd, fd_setsize))
return MHD_NO;
break;
case MHD_EVENT_LOOP_INFO_WRITE:
if (MHD_YES != add_to_fd_set (pos->socket_fd, write_fd_set, max_fd, fd_setsize))
return MHD_NO;
if (pos->read_buffer_size > pos->read_buffer_offset &&
MHD_YES != add_to_fd_set (pos->socket_fd, read_fd_set, max_fd, fd_setsize))
return MHD_NO;
break;
case MHD_EVENT_LOOP_INFO_BLOCK:
if (pos->read_buffer_size > pos->read_buffer_offset &&
MHD_YES != add_to_fd_set (pos->socket_fd, read_fd_set, max_fd, fd_setsize))
return MHD_NO;
break;
case MHD_EVENT_LOOP_INFO_CLEANUP:
/* this should never happen */
break;
}
}
#if DEBUG_CONNECT
#if HAVE_MESSAGES
if (NULL != max_fd)
MHD_DLOG (daemon,
"Maximum socket in select set: %d\n",
*max_fd);
#endif
#endif
return MHD_YES;
}
/**
* Main function of the thread that handles an individual
* connection when #MHD_USE_THREAD_PER_CONNECTION is set.
*
* @param data the `struct MHD_Connection` this thread will handle
* @return always 0
*/
static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
MHD_handle_connection (void *data)
{
struct MHD_Connection *con = data;
int num_ready;
fd_set rs;
fd_set ws;
MHD_socket max;
struct timeval tv;
struct timeval *tvp;
unsigned int timeout;
time_t now;
#if WINDOWS
MHD_pipe spipe = con->daemon->wpipe[0];
char tmp;
#ifdef HAVE_POLL
int extra_slot;
#endif /* HAVE_POLL */
#define EXTRA_SLOTS 1
#else /* !WINDOWS */
#define EXTRA_SLOTS 0
#endif /* !WINDOWS */
#ifdef HAVE_POLL
struct pollfd p[1 + EXTRA_SLOTS];
#endif
timeout = con->daemon->connection_timeout;
while ( (MHD_YES != con->daemon->shutdown) &&
(MHD_CONNECTION_CLOSED != con->state) )
{
tvp = NULL;
if (timeout > 0)
{
now = MHD_monotonic_time();
if (now - con->last_activity > timeout)
tv.tv_sec = 0;
else
tv.tv_sec = timeout - (now - con->last_activity);
tv.tv_usec = 0;
tvp = &tv;
}
#if HTTPS_SUPPORT
if (MHD_YES == con->tls_read_ready)
{
/* do not block (more data may be inside of TLS buffers waiting for us) */
tv.tv_sec = 0;
tv.tv_usec = 0;
tvp = &tv;
}
#endif
if (0 == (con->daemon->options & MHD_USE_POLL))
{
/* use select */
int err_state = 0;
FD_ZERO (&rs);
FD_ZERO (&ws);
max = 0;
switch (con->event_loop_info)
{
case MHD_EVENT_LOOP_INFO_READ:
if (MHD_YES !=
add_to_fd_set (con->socket_fd, &rs, &max, FD_SETSIZE))
err_state = 1;
break;
case MHD_EVENT_LOOP_INFO_WRITE:
if (MHD_YES !=
add_to_fd_set (con->socket_fd, &ws, &max, FD_SETSIZE))
err_state = 1;
if ( (con->read_buffer_size > con->read_buffer_offset) &&
(MHD_YES !=
add_to_fd_set (con->socket_fd, &rs, &max, FD_SETSIZE)) )
err_state = 1;
break;
case MHD_EVENT_LOOP_INFO_BLOCK:
if ( (con->read_buffer_size > con->read_buffer_offset) &&
(MHD_YES !=
add_to_fd_set (con->socket_fd, &rs, &max, FD_SETSIZE)) )
err_state = 1;
tv.tv_sec = 0;
tv.tv_usec = 0;
tvp = &tv;
break;
case MHD_EVENT_LOOP_INFO_CLEANUP:
/* how did we get here!? */
goto exit;
}
#if WINDOWS
if (MHD_INVALID_PIPE_ != spipe)
{
if (MHD_YES !=
add_to_fd_set (spipe, &rs, &max, FD_SETSIZE))
err_state = 1;
}
#endif
if (0 != err_state)
{
#if HAVE_MESSAGES
MHD_DLOG (con->daemon,
"Can't add FD to fd_set\n");
#endif
goto exit;
}
num_ready = MHD_SYS_select_ (max + 1, &rs, &ws, NULL, tvp);
if (num_ready < 0)
{
if (EINTR == MHD_socket_errno_)
continue;
#if HAVE_MESSAGES
MHD_DLOG (con->daemon,
"Error during select (%d): `%s'\n",
MHD_socket_errno_,
MHD_socket_last_strerr_ ());
#endif
break;
}
#if WINDOWS
/* drain signaling pipe */
if ( (MHD_INVALID_PIPE_ != spipe) &&
(FD_ISSET (spipe, &rs)) )
(void) MHD_pipe_read_ (spipe, &tmp, sizeof (tmp));
#endif
/* call appropriate connection handler if necessary */
if ( (FD_ISSET (con->socket_fd, &rs))
#if HTTPS_SUPPORT
|| (MHD_YES == con->tls_read_ready)
#endif
)
con->read_handler (con);
if (FD_ISSET (con->socket_fd, &ws))
con->write_handler (con);
if (MHD_NO == con->idle_handler (con))
goto exit;
}
#ifdef HAVE_POLL
else
{
/* use poll */
memset (&p, 0, sizeof (p));
p[0].fd = con->socket_fd;
switch (con->event_loop_info)
{
case MHD_EVENT_LOOP_INFO_READ:
p[0].events |= POLLIN;
break;
case MHD_EVENT_LOOP_INFO_WRITE:
p[0].events |= POLLOUT;
if (con->read_buffer_size > con->read_buffer_offset)
p[0].events |= POLLIN;
break;
case MHD_EVENT_LOOP_INFO_BLOCK:
if (con->read_buffer_size > con->read_buffer_offset)
p[0].events |= POLLIN;
tv.tv_sec = 0;
tv.tv_usec = 0;
tvp = &tv;
break;
case MHD_EVENT_LOOP_INFO_CLEANUP:
/* how did we get here!? */
goto exit;
}
#if WINDOWS
extra_slot = 0;
if (MHD_INVALID_PIPE_ != spipe)
{
p[1].events |= POLLIN;
p[1].fd = spipe;
p[1].revents = 0;
extra_slot = 1;
}
#endif
if (MHD_sys_poll_ (p,
#if WINDOWS
1 + extra_slot,
#else
1,
#endif
(NULL == tvp) ? -1 : tv.tv_sec * 1000) < 0)
{
if (EINTR == MHD_socket_errno_)
continue;
#if HAVE_MESSAGES
MHD_DLOG (con->daemon,
"Error during poll: `%s'\n",
MHD_socket_last_strerr_ ());
#endif
break;
}
#if WINDOWS
/* drain signaling pipe */
if ( (MHD_INVALID_PIPE_ != spipe) &&
(0 != (p[1].revents & (POLLERR | POLLHUP))) )
(void) MHD_pipe_read_ (spipe, &tmp, sizeof (tmp));
#endif
if ( (0 != (p[0].revents & POLLIN))
#if HTTPS_SUPPORT
|| (MHD_YES == con->tls_read_ready)
#endif
)
con->read_handler (con);
if (0 != (p[0].revents & POLLOUT))
con->write_handler (con);
if (0 != (p[0].revents & (POLLERR | POLLHUP)))
MHD_connection_close (con, MHD_REQUEST_TERMINATED_WITH_ERROR);
if (MHD_NO == con->idle_handler (con))
goto exit;
}
#endif
}
if (MHD_CONNECTION_IN_CLEANUP != con->state)
{
#if DEBUG_CLOSE
#if HAVE_MESSAGES
MHD_DLOG (con->daemon,
"Processing thread terminating, closing connection\n");
#endif
#endif
if (MHD_CONNECTION_CLOSED != con->state)
MHD_connection_close (con,
MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
con->idle_handler (con);
}
exit:
if (NULL != con->response)
{
MHD_destroy_response (con->response);
con->response = NULL;
}
if (NULL != con->daemon->notify_connection)
con->daemon->notify_connection (con->daemon->notify_connection_cls,
con,
&con->socket_context,
MHD_CONNECTION_NOTIFY_CLOSED);
return (MHD_THRD_RTRN_TYPE_)0;
}
/**
* Callback for receiving data from the socket.
*
* @param connection the MHD connection structure
* @param other where to write received data to
* @param i maximum size of other (in bytes)
* @return number of bytes actually received
*/
static ssize_t
recv_param_adapter (struct MHD_Connection *connection,
void *other,
size_t i)
{
ssize_t ret;
if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
(MHD_CONNECTION_CLOSED == connection->state) )
{
MHD_set_socket_errno_ (ENOTCONN);
return -1;
}
ret = recv (connection->socket_fd, other, i, MSG_NOSIGNAL);
#if EPOLL_SUPPORT
if (ret < (ssize_t) i)
{
/* partial read --- no longer read-ready */
connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY;
}
#endif
return ret;
}
/**
* Callback for writing data to the socket.
*
* @param connection the MHD connection structure
* @param other data to write
* @param i number of bytes to write
* @return actual number of bytes written
*/
static ssize_t
send_param_adapter (struct MHD_Connection *connection,
const void *other,
size_t i)
{
ssize_t ret;
#if LINUX
MHD_socket fd;
off_t offset;
off_t left;
#endif
if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
(MHD_CONNECTION_CLOSED == connection->state) )
{
MHD_set_socket_errno_ (ENOTCONN);
return -1;
}
if (0 != (connection->daemon->options & MHD_USE_SSL))
return send (connection->socket_fd, other, i, MSG_NOSIGNAL);
#if LINUX
if ( (connection->write_buffer_append_offset ==
connection->write_buffer_send_offset) &&
(NULL != connection->response) &&
(MHD_INVALID_SOCKET != (fd = connection->response->fd)) )
{
/* can use sendfile */
offset = (off_t) connection->response_write_position + connection->response->fd_off;
left = connection->response->total_size - connection->response_write_position;
if (left > SSIZE_MAX)
left = SSIZE_MAX; /* cap at return value limit */
if (-1 != (ret = sendfile (connection->socket_fd,
fd,
&offset,
(size_t) left)))
{
#if EPOLL_SUPPORT
if (ret < left)
{
/* partial write --- no longer write-ready */
connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
}
#endif
return ret;
}
const int err = MHD_socket_errno_;
if ( (EINTR == err) || (EAGAIN == err) || (EWOULDBLOCK == err) )
return 0;
if ( (EINVAL == err) || (EBADF == err) )
return -1;
/* None of the 'usual' sendfile errors occurred, so we should try
to fall back to 'SEND'; see also this thread for info on
odd libc/Linux behavior with sendfile:
http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */
}
#endif
ret = send (connection->socket_fd, other, i, MSG_NOSIGNAL);
#if EPOLL_SUPPORT
if (ret < (ssize_t) i)
{
/* partial write --- no longer write-ready */
connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
}
#endif
/* Handle broken kernel / libc, returning -1 but not setting errno;
kill connection as that should be safe; reported on mailinglist here:
http://lists.gnu.org/archive/html/libmicrohttpd/2014-10/msg00023.html */
if ( (-1 == ret) && (0 == errno) )
errno = ECONNRESET;
return ret;
}
/**
* Signature of main function for a thread.
*
* @param cls closure argument for the function
* @return termination code from the thread
*/
typedef MHD_THRD_RTRN_TYPE_ (MHD_THRD_CALL_SPEC_ *ThreadStartRoutine)(void *cls);
/**
* Create a thread and set the attributes according to our options.
*
* @param thread handle to initialize
* @param daemon daemon with options
* @param start_routine main function of thread
* @param arg argument for start_routine
* @return 0 on success
*/
static int
create_thread (MHD_thread_handle_ *thread,
const struct MHD_Daemon *daemon,
ThreadStartRoutine start_routine,
void *arg)
{
#if defined(MHD_USE_POSIX_THREADS)
pthread_attr_t attr;
pthread_attr_t *pattr;
int ret;
if (0 != daemon->thread_stack_size)
{
if (0 != (ret = pthread_attr_init (&attr)))
goto ERR;
if (0 != (ret = pthread_attr_setstacksize (&attr, daemon->thread_stack_size)))
{
pthread_attr_destroy (&attr);
goto ERR;
}
pattr = &attr;
}
else
{
pattr = NULL;
}
ret = pthread_create (thread, pattr,
start_routine, arg);
#ifdef HAVE_PTHREAD_SETNAME_NP
(void) pthread_setname_np (*thread, "libmicrohttpd");
#endif /* HAVE_PTHREAD_SETNAME_NP */
if (0 != daemon->thread_stack_size)
pthread_attr_destroy (&attr);
return ret;
ERR:
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to set thread stack size\n");
#endif
errno = EINVAL;
return ret;
#elif defined(MHD_USE_W32_THREADS)
unsigned threadID;
*thread = (HANDLE)_beginthreadex(NULL, (unsigned)daemon->thread_stack_size, start_routine,
arg, 0, &threadID);
if (NULL == (*thread))
return errno;
W32_SetThreadName(threadID, "libmicrohttpd");
return 0;
#endif
}
/**
* Add another client connection to the set of connections
* managed by MHD. This API is usually not needed (since
* MHD will accept inbound connections on the server socket).
* Use this API in special cases, for example if your HTTP
* server is behind NAT and needs to connect out to the
* HTTP client.
*
* The given client socket will be managed (and closed!) by MHD after
* this call and must no longer be used directly by the application
* afterwards.
*
* Per-IP connection limits are ignored when using this API.
*
* @param daemon daemon that manages the connection
* @param client_socket socket to manage (MHD will expect
* to receive an HTTP request from this socket next).
* @param addr IP address of the client
* @param addrlen number of bytes in @a addr
* @param external_add perform additional operations needed due
* to the application calling us directly
* @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
* set to indicate further details about the error.
*/
static int
internal_add_connection (struct MHD_Daemon *daemon,
MHD_socket client_socket,
const struct sockaddr *addr,
socklen_t addrlen,
int external_add)
{
struct MHD_Connection *connection;
int res_thread_create;
unsigned int i;
int eno;
struct MHD_Daemon *worker;
#if OSX
static int on = 1;
#endif
if (NULL != daemon->worker_pool)
{
/* have a pool, try to find a pool with capacity; we use the
socket as the initial offset into the pool for load
balancing */
for (i=0;i<daemon->worker_pool_size;i++)
{
worker = &daemon->worker_pool[(i + client_socket) % daemon->worker_pool_size];
if (worker->connections < worker->connection_limit)
return internal_add_connection (worker,
client_socket,
addr, addrlen,
external_add);
}
/* all pools are at their connection limit, must refuse */
if (0 != MHD_socket_close_ (client_socket))
MHD_PANIC ("close failed\n");
#if ENFILE
errno = ENFILE;
#endif
return MHD_NO;
}
#ifndef WINDOWS
if ( (client_socket >= FD_SETSIZE) &&
(0 == (daemon->options & (MHD_USE_POLL | MHD_USE_EPOLL_LINUX_ONLY))) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Socket descriptor larger than FD_SETSIZE: %d > %d\n",
client_socket,
FD_SETSIZE);
#endif
if (0 != MHD_socket_close_ (client_socket))
MHD_PANIC ("close failed\n");
#if EINVAL
errno = EINVAL;
#endif
return MHD_NO;
}
#endif
#if HAVE_MESSAGES
#if DEBUG_CONNECT
MHD_DLOG (daemon,
"Accepted connection on socket %d\n",
client_socket);
#endif
#endif
if ( (daemon->connections == daemon->connection_limit) ||
(MHD_NO == MHD_ip_limit_add (daemon, addr, addrlen)) )
{
/* above connection limit - reject */
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Server reached connection limit (closing inbound connection)\n");
#endif
if (0 != MHD_socket_close_ (client_socket))
MHD_PANIC ("close failed\n");
#if ENFILE
errno = ENFILE;
#endif
return MHD_NO;
}
/* apply connection acceptance policy if present */
if ( (NULL != daemon->apc) &&
(MHD_NO == daemon->apc (daemon->apc_cls,
addr, addrlen)) )
{
#if DEBUG_CLOSE
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Connection rejected, closing connection\n");
#endif
#endif
if (0 != MHD_socket_close_ (client_socket))
MHD_PANIC ("close failed\n");
MHD_ip_limit_del (daemon, addr, addrlen);
#if EACCESS
errno = EACCESS;
#endif
return MHD_NO;
}
#if OSX
#ifdef SOL_SOCKET
#ifdef SO_NOSIGPIPE
setsockopt (client_socket,
SOL_SOCKET, SO_NOSIGPIPE,
&on, sizeof (on));
#endif
#endif
#endif
if (NULL == (connection = malloc (sizeof (struct MHD_Connection))))
{
eno = errno;
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Error allocating memory: %s\n",
MHD_strerror_ (errno));
#endif
if (0 != MHD_socket_close_ (client_socket))
MHD_PANIC ("close failed\n");
MHD_ip_limit_del (daemon, addr, addrlen);
errno = eno;
return MHD_NO;
}
memset (connection,
0,
sizeof (struct MHD_Connection));
connection->pool = MHD_pool_create (daemon->pool_size);
if (NULL == connection->pool)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Error allocating memory: %s\n",
MHD_strerror_ (errno));
#endif
if (0 != MHD_socket_close_ (client_socket))
MHD_PANIC ("close failed\n");
MHD_ip_limit_del (daemon, addr, addrlen);
free (connection);
#if ENOMEM
errno = ENOMEM;
#endif
return MHD_NO;
}
connection->connection_timeout = daemon->connection_timeout;
if (NULL == (connection->addr = malloc (addrlen)))
{
eno = errno;
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Error allocating memory: %s\n",
MHD_strerror_ (errno));
#endif
if (0 != MHD_socket_close_ (client_socket))
MHD_PANIC ("close failed\n");
MHD_ip_limit_del (daemon, addr, addrlen);
MHD_pool_destroy (connection->pool);
free (connection);
errno = eno;
return MHD_NO;
}
memcpy (connection->addr, addr, addrlen);
connection->addr_len = addrlen;
connection->socket_fd = client_socket;
connection->daemon = daemon;
connection->last_activity = MHD_monotonic_time();
/* set default connection handlers */
MHD_set_http_callbacks_ (connection);
connection->recv_cls = &recv_param_adapter;
connection->send_cls = &send_param_adapter;
if (0 == (connection->daemon->options & MHD_USE_EPOLL_TURBO))
{
/* non-blocking sockets are required on most systems and for GNUtls;
however, they somehow cause serious problems on CYGWIN (#1824);
in turbo mode, we assume that non-blocking was already set
by 'accept4' or whoever calls 'MHD_add_connection' */
#ifdef CYGWIN
if (0 != (daemon->options & MHD_USE_SSL))
#endif
{
/* make socket non-blocking */
#if !defined(WINDOWS) || defined(CYGWIN)
int flags = fcntl (connection->socket_fd, F_GETFL);
if ( (-1 == flags) ||
(0 != fcntl (connection->socket_fd, F_SETFL, flags | O_NONBLOCK)) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to make socket non-blocking: %s\n",
MHD_socket_last_strerr_ ());
#endif
}
#else
unsigned long flags = 1;
if (0 != ioctlsocket (connection->socket_fd, FIONBIO, &flags))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to make socket non-blocking: %s\n",
MHD_socket_last_strerr_ ());
#endif
}
#endif
}
}
#if HTTPS_SUPPORT
if (0 != (daemon->options & MHD_USE_SSL))
{
connection->recv_cls = &recv_tls_adapter;
connection->send_cls = &send_tls_adapter;
connection->state = MHD_TLS_CONNECTION_INIT;
MHD_set_https_callbacks (connection);
connection->tls_session = SSL_new (daemon->tls_context);
BIO* bio = BIO_new (&MHD_bio_method);
if (bio)
{
bio->ptr = connection;
bio->init = 1;
}
SSL_set_bio (connection->tls_session, bio, bio);
SSL_set_app_data (connection->tls_session, connection);
}
#endif
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to acquire cleanup mutex\n");
XDLL_insert (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
connection);
DLL_insert (daemon->connections_head,
daemon->connections_tail,
connection);
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to release cleanup mutex\n");
if (NULL != daemon->notify_connection)
daemon->notify_connection (daemon->notify_connection_cls,
connection,
&connection->socket_context,
MHD_CONNECTION_NOTIFY_STARTED);
/* attempt to create handler thread */
if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
{
res_thread_create = create_thread (&connection->pid,
daemon,
&MHD_handle_connection,
connection);
if (0 != res_thread_create)
{
eno = errno;
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to create a thread: %s\n",
MHD_strerror_ (res_thread_create));
#endif
goto cleanup;
}
}
else
if ( (MHD_YES == external_add) &&
(MHD_INVALID_PIPE_ != daemon->wpipe[1]) &&
(1 != MHD_pipe_write_ (daemon->wpipe[1], "n", 1)) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"failed to signal new connection via pipe");
#endif
}
#if EPOLL_SUPPORT
if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
{
if (0 == (daemon->options & MHD_USE_EPOLL_TURBO))
{
struct epoll_event event;
event.events = EPOLLIN | EPOLLOUT | EPOLLET;
event.data.ptr = connection;
if (0 != epoll_ctl (daemon->epoll_fd,
EPOLL_CTL_ADD,
client_socket,
&event))
{
eno = errno;
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Call to epoll_ctl failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
goto cleanup;
}
connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
}
else
{
connection->epoll_state |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY
| MHD_EPOLL_STATE_IN_EREADY_EDLL;
EDLL_insert (daemon->eready_head,
daemon->eready_tail,
connection);
}
}
#endif
daemon->connections++;
return MHD_YES;
cleanup:
if (NULL != daemon->notify_connection)
daemon->notify_connection (daemon->notify_connection_cls,
connection,
&connection->socket_context,
MHD_CONNECTION_NOTIFY_CLOSED);
if (0 != MHD_socket_close_ (client_socket))
MHD_PANIC ("close failed\n");
MHD_ip_limit_del (daemon, addr, addrlen);
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to acquire cleanup mutex\n");
DLL_remove (daemon->connections_head,
daemon->connections_tail,
connection);
XDLL_remove (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
connection);
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to release cleanup mutex\n");
MHD_pool_destroy (connection->pool);
free (connection->addr);
free (connection);
#if EINVAL
errno = eno;
#endif
return MHD_NO;
}
/**
* Suspend handling of network data for a given connection. This can
* be used to dequeue a connection from MHD's event loop (external
* select, internal select or thread pool; not applicable to
* thread-per-connection!) for a while.
*
* If you use this API in conjunction with a internal select or a
* thread pool, you must set the option #MHD_USE_PIPE_FOR_SHUTDOWN to
* ensure that a resumed connection is immediately processed by MHD.
*
* Suspended connections continue to count against the total number of
* connections allowed (per daemon, as well as per IP, if such limits
* are set). Suspended connections will NOT time out; timeouts will
* restart when the connection handling is resumed. While a
* connection is suspended, MHD will not detect disconnects by the
* client.
*
* The only safe time to suspend a connection is from the
* #MHD_AccessHandlerCallback.
*
* Finally, it is an API violation to call #MHD_stop_daemon while
* having suspended connections (this will at least create memory and
* socket leaks or lead to undefined behavior). You must explicitly
* resume all connections before stopping the daemon.
*
* @param connection the connection to suspend
*/
void
MHD_suspend_connection (struct MHD_Connection *connection)
{
struct MHD_Daemon *daemon;
daemon = connection->daemon;
if (MHD_USE_SUSPEND_RESUME != (daemon->options & MHD_USE_SUSPEND_RESUME))
MHD_PANIC ("Cannot suspend connections without enabling MHD_USE_SUSPEND_RESUME!\n");
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to acquire cleanup mutex\n");
DLL_remove (daemon->connections_head,
daemon->connections_tail,
connection);
DLL_insert (daemon->suspended_connections_head,
daemon->suspended_connections_tail,
connection);
if (connection->connection_timeout == daemon->connection_timeout)
XDLL_remove (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
connection);
else
XDLL_remove (daemon->manual_timeout_head,
daemon->manual_timeout_tail,
connection);
#if EPOLL_SUPPORT
if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
{
if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
{
EDLL_remove (daemon->eready_head,
daemon->eready_tail,
connection);
connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
}
if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET))
{
if (0 != epoll_ctl (daemon->epoll_fd,
EPOLL_CTL_DEL,
connection->socket_fd,
NULL))
MHD_PANIC ("Failed to remove FD from epoll set\n");
connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET;
}
connection->epoll_state |= MHD_EPOLL_STATE_SUSPENDED;
}
#endif
connection->suspended = MHD_YES;
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to release cleanup mutex\n");
}
/**
* Resume handling of network data for suspended connection. It is
* safe to resume a suspended connection at any time. Calling this function
* on a connection that was not previously suspended will result
* in undefined behavior.
*
* @param connection the connection to resume
*/
void
MHD_resume_connection (struct MHD_Connection *connection)
{
struct MHD_Daemon *daemon;
daemon = connection->daemon;
if (MHD_USE_SUSPEND_RESUME != (daemon->options & MHD_USE_SUSPEND_RESUME))
MHD_PANIC ("Cannot resume connections without enabling MHD_USE_SUSPEND_RESUME!\n");
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to acquire cleanup mutex\n");
connection->resuming = MHD_YES;
daemon->resuming = MHD_YES;
if ( (MHD_INVALID_PIPE_ != daemon->wpipe[1]) &&
(1 != MHD_pipe_write_ (daemon->wpipe[1], "r", 1)) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"failed to signal resume via pipe");
#endif
}
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to release cleanup mutex\n");
}
/**
* Run through the suspended connections and move any that are no
* longer suspended back to the active state.
*
* @param daemon daemon context
* @return #MHD_YES if a connection was actually resumed
*/
static int
resume_suspended_connections (struct MHD_Daemon *daemon)
{
struct MHD_Connection *pos;
struct MHD_Connection *next = NULL;
int ret;
ret = MHD_NO;
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to acquire cleanup mutex\n");
if (MHD_YES == daemon->resuming)
next = daemon->suspended_connections_head;
while (NULL != (pos = next))
{
next = pos->next;
if (MHD_NO == pos->resuming)
continue;
ret = MHD_YES;
DLL_remove (daemon->suspended_connections_head,
daemon->suspended_connections_tail,
pos);
DLL_insert (daemon->connections_head,
daemon->connections_tail,
pos);
if (pos->connection_timeout == daemon->connection_timeout)
XDLL_insert (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
pos);
else
XDLL_insert (daemon->manual_timeout_head,
daemon->manual_timeout_tail,
pos);
#if EPOLL_SUPPORT
if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
{
if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
MHD_PANIC ("Resumed connection was already in EREADY set\n");
/* we always mark resumed connections as ready, as we
might have missed the edge poll event during suspension */
EDLL_insert (daemon->eready_head,
daemon->eready_tail,
pos);
pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
pos->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED;
}
#endif
pos->suspended = MHD_NO;
pos->resuming = MHD_NO;
}
daemon->resuming = MHD_NO;
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to release cleanup mutex\n");
return ret;
}
/**
* Change socket options to be non-blocking, non-inheritable.
*
* @param daemon daemon context
* @param sock socket to manipulate
*/
static void
make_nonblocking_noninheritable (struct MHD_Daemon *daemon,
MHD_socket sock)
{
#ifdef WINDOWS
DWORD dwFlags;
unsigned long flags = 1;
if (0 != ioctlsocket (sock, FIONBIO, &flags))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to make socket non-blocking: %s\n",
MHD_socket_last_strerr_ ());
#endif
}
if (!GetHandleInformation ((HANDLE) sock, &dwFlags) ||
((dwFlags != (dwFlags & ~HANDLE_FLAG_INHERIT)) &&
!SetHandleInformation ((HANDLE) sock, HANDLE_FLAG_INHERIT, 0)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to make socket non-inheritable: %u\n",
(unsigned int) GetLastError ());
#endif
}
#else
int flags;
int nonblock;
nonblock = O_NONBLOCK;
#ifdef CYGWIN
if (0 == (daemon->options & MHD_USE_SSL))
nonblock = 0;
#endif
flags = fcntl (sock, F_GETFD);
if ( ( (-1 == flags) ||
( (flags != (flags | FD_CLOEXEC)) &&
(0 != fcntl (sock, F_SETFD, flags | nonblock | FD_CLOEXEC)) ) ) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to make socket non-inheritable: %s\n",
MHD_socket_last_strerr_ ());
#endif
}
#endif
}
/**
* Add another client connection to the set of connections managed by
* MHD. This API is usually not needed (since MHD will accept inbound
* connections on the server socket). Use this API in special cases,
* for example if your HTTP server is behind NAT and needs to connect
* out to the HTTP client, or if you are building a proxy.
*
* If you use this API in conjunction with a internal select or a
* thread pool, you must set the option
* #MHD_USE_PIPE_FOR_SHUTDOWN to ensure that the freshly added
* connection is immediately processed by MHD.
*
* The given client socket will be managed (and closed!) by MHD after
* this call and must no longer be used directly by the application
* afterwards.
*
* Per-IP connection limits are ignored when using this API.
*
* @param daemon daemon that manages the connection
* @param client_socket socket to manage (MHD will expect
* to receive an HTTP request from this socket next).
* @param addr IP address of the client
* @param addrlen number of bytes in @a addr
* @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
* set to indicate further details about the error.
* @ingroup specialized
*/
int
MHD_add_connection (struct MHD_Daemon *daemon,
MHD_socket client_socket,
const struct sockaddr *addr,
socklen_t addrlen)
{
make_nonblocking_noninheritable (daemon,
client_socket);
return internal_add_connection (daemon,
client_socket,
addr, addrlen,
MHD_YES);
}
/**
* Accept an incoming connection and create the MHD_Connection object for
* it. This function also enforces policy by way of checking with the
* accept policy callback.
*
* @param daemon handle with the listen socket
* @return #MHD_YES on success (connections denied by policy or due
* to 'out of memory' and similar errors) are still considered
* successful as far as #MHD_accept_connection() is concerned);
* a return code of #MHD_NO only refers to the actual
* accept() system call.
*/
static int
MHD_accept_connection (struct MHD_Daemon *daemon)
{
#if HAVE_INET6
struct sockaddr_in6 addrstorage;
#else
struct sockaddr_in addrstorage;
#endif
struct sockaddr *addr = (struct sockaddr *) &addrstorage;
socklen_t addrlen;
MHD_socket s;
MHD_socket fd;
int nonblock;
addrlen = sizeof (addrstorage);
memset (addr, 0, sizeof (addrstorage));
if (MHD_INVALID_SOCKET == (fd = daemon->socket_fd))
return MHD_NO;
#ifdef HAVE_SOCK_NONBLOCK
nonblock = SOCK_NONBLOCK;
#else
nonblock = 0;
#endif
#ifdef CYGWIN
if (0 == (daemon->options & MHD_USE_SSL))
nonblock = 0;
#endif
#if HAVE_ACCEPT4
s = accept4 (fd, addr, &addrlen, SOCK_CLOEXEC | nonblock);
#else
s = accept (fd, addr, &addrlen);
#endif
if ((MHD_INVALID_SOCKET == s) || (addrlen <= 0))
{
#if HAVE_MESSAGES
const int err = MHD_socket_errno_;
/* This could be a common occurance with multiple worker threads */
if ( (EINVAL == err) &&
(MHD_INVALID_SOCKET == daemon->socket_fd) )
return MHD_NO; /* can happen during shutdown */
if ((EAGAIN != err) && (EWOULDBLOCK != err))
MHD_DLOG (daemon,
"Error accepting connection: %s\n",
MHD_socket_last_strerr_ ());
#endif
if (MHD_INVALID_SOCKET != s)
{
if (0 != MHD_socket_close_ (s))
MHD_PANIC ("close failed\n");
/* just in case */
}
return MHD_NO;
}
#if !defined(HAVE_ACCEPT4) || HAVE_ACCEPT4+0 == 0 || !defined(HAVE_SOCK_NONBLOCK) || SOCK_CLOEXEC+0 == 0
make_nonblocking_noninheritable (daemon, s);
#endif
#if HAVE_MESSAGES
#if DEBUG_CONNECT
MHD_DLOG (daemon,
"Accepted connection on socket %d\n",
s);
#endif
#endif
(void) internal_add_connection (daemon, s,
addr, addrlen,
MHD_NO);
return MHD_YES;
}
/**
* Free resources associated with all closed connections.
* (destroy responses, free buffers, etc.). All closed
* connections are kept in the "cleanup" doubly-linked list.
*
* @param daemon daemon to clean up
*/
static void
MHD_cleanup_connections (struct MHD_Daemon *daemon)
{
struct MHD_Connection *pos;
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to acquire cleanup mutex\n");
while (NULL != (pos = daemon->cleanup_head))
{
DLL_remove (daemon->cleanup_head,
daemon->cleanup_tail,
pos);
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_NO == pos->thread_joined) )
{
if (0 != MHD_join_thread_ (pos->pid))
{
MHD_PANIC ("Failed to join a thread\n");
}
}
MHD_pool_destroy (pos->pool);
#if HTTPS_SUPPORT
if (NULL != pos->tls_session)
SSL_free (pos->tls_session);
#endif
if (NULL != daemon->notify_connection)
daemon->notify_connection (daemon->notify_connection_cls,
pos,
&pos->socket_context,
MHD_CONNECTION_NOTIFY_CLOSED);
MHD_ip_limit_del (daemon, pos->addr, pos->addr_len);
#if EPOLL_SUPPORT
if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
{
EDLL_remove (daemon->eready_head,
daemon->eready_tail,
pos);
pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
}
if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
(MHD_INVALID_SOCKET != daemon->epoll_fd) &&
(0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) )
{
/* epoll documentation suggests that closing a FD
automatically removes it from the epoll set; however,
this is not true as if we fail to do manually remove it,
we are still seeing an event for this fd in epoll,
causing grief (use-after-free...) --- at least on my
system. */
if (0 != epoll_ctl (daemon->epoll_fd,
EPOLL_CTL_DEL,
pos->socket_fd,
NULL))
MHD_PANIC ("Failed to remove FD from epoll set\n");
pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET;
}
#endif
if (NULL != pos->response)
{
MHD_destroy_response (pos->response);
pos->response = NULL;
}
if (MHD_INVALID_SOCKET != pos->socket_fd)
{
#ifdef WINDOWS
shutdown (pos->socket_fd, SHUT_WR);
#endif
if (0 != MHD_socket_close_ (pos->socket_fd))
MHD_PANIC ("close failed\n");
}
if (NULL != pos->addr)
free (pos->addr);
free (pos);
daemon->connections--;
}
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to release cleanup mutex\n");
}
/**
* Obtain timeout value for `select()` for this daemon (only needed if
* connection timeout is used). The returned value is how long
* `select()` or `poll()` should at most block, not the timeout value set
* for connections. This function MUST NOT be called if MHD is
* running with #MHD_USE_THREAD_PER_CONNECTION.
*
* @param daemon daemon to query for timeout
* @param timeout set to the timeout (in milliseconds)
* @return #MHD_YES on success, #MHD_NO if timeouts are
* not used (or no connections exist that would
* necessiate the use of a timeout right now).
* @ingroup event
*/
int
MHD_get_timeout (struct MHD_Daemon *daemon,
MHD_UNSIGNED_LONG_LONG *timeout)
{
time_t earliest_deadline;
time_t now;
struct MHD_Connection *pos;
int have_timeout;
if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Illegal call to MHD_get_timeout\n");
#endif
return MHD_NO;
}
#if HTTPS_SUPPORT
if (0 != daemon->num_tls_read_ready)
{
/* if there is any TLS connection with data ready for
reading, we must not block in the event loop */
*timeout = 0;
return MHD_YES;
}
#endif
have_timeout = MHD_NO;
earliest_deadline = 0; /* avoid compiler warnings */
for (pos = daemon->manual_timeout_head; NULL != pos; pos = pos->nextX)
{
if (0 != pos->connection_timeout)
{
if ( (! have_timeout) ||
(earliest_deadline > pos->last_activity + pos->connection_timeout) )
earliest_deadline = pos->last_activity + pos->connection_timeout;
#if HTTPS_SUPPORT
if ( (0 != (daemon->options & MHD_USE_SSL)) &&
(0 != SSL_pending (pos->tls_session)) )
earliest_deadline = 0;
#endif
have_timeout = MHD_YES;
}
}
/* normal timeouts are sorted, so we only need to look at the 'head' */
pos = daemon->normal_timeout_head;
if ( (NULL != pos) &&
(0 != pos->connection_timeout) )
{
if ( (! have_timeout) ||
(earliest_deadline > pos->last_activity + pos->connection_timeout) )
earliest_deadline = pos->last_activity + pos->connection_timeout;
#if HTTPS_SUPPORT
if ( (0 != (daemon->options & MHD_USE_SSL)) &&
(0 != SSL_pending (pos->tls_session)) )
earliest_deadline = 0;
#endif
have_timeout = MHD_YES;
}
if (MHD_NO == have_timeout)
return MHD_NO;
now = MHD_monotonic_time();
if (earliest_deadline < now)
*timeout = 0;
else
*timeout = 1000 * (1 + earliest_deadline - now);
return MHD_YES;
}
/**
* Run webserver operations. This method should be called by clients
* in combination with #MHD_get_fdset if the client-controlled select
* method is used.
*
* You can use this function instead of #MHD_run if you called
* `select()` on the result from #MHD_get_fdset. File descriptors in
* the sets that are not controlled by MHD will be ignored. Calling
* this function instead of #MHD_run is more efficient as MHD will
* not have to call `select()` again to determine which operations are
* ready.
*
* @param daemon daemon to run select loop for
* @param read_fd_set read set
* @param write_fd_set write set
* @param except_fd_set except set (not used, can be NULL)
* @return #MHD_NO on serious errors, #MHD_YES on success
* @ingroup event
*/
int
MHD_run_from_select (struct MHD_Daemon *daemon,
const fd_set *read_fd_set,
const fd_set *write_fd_set,
const fd_set *except_fd_set)
{
MHD_socket ds;
char tmp;
struct MHD_Connection *pos;
struct MHD_Connection *next;
#if EPOLL_SUPPORT
if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
{
/* we're in epoll mode, the epoll FD stands for
the entire event set! */
if (daemon->epoll_fd >= FD_SETSIZE)
return MHD_NO; /* poll fd too big, fail hard */
if (FD_ISSET (daemon->epoll_fd, read_fd_set))
return MHD_run (daemon);
return MHD_YES;
}
#endif
/* select connection thread handling type */
if ( (MHD_INVALID_SOCKET != (ds = daemon->socket_fd)) &&
(FD_ISSET (ds, (fd_set*)read_fd_set)) )
(void) MHD_accept_connection (daemon);
/* drain signaling pipe to avoid spinning select */
if ( (MHD_INVALID_PIPE_ != daemon->wpipe[0]) &&
(FD_ISSET (daemon->wpipe[0], (fd_set*)read_fd_set)) )
(void) MHD_pipe_read_ (daemon->wpipe[0], &tmp, sizeof (tmp));
if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
{
/* do not have a thread per connection, process all connections now */
next = daemon->connections_head;
while (NULL != (pos = next))
{
next = pos->next;
ds = pos->socket_fd;
if (MHD_INVALID_SOCKET == ds)
continue;
switch (pos->event_loop_info)
{
case MHD_EVENT_LOOP_INFO_READ:
if ( (FD_ISSET (ds, (fd_set*)read_fd_set))
#if HTTPS_SUPPORT
|| (MHD_YES == pos->tls_read_ready)
#endif
)
pos->read_handler (pos);
break;
case MHD_EVENT_LOOP_INFO_WRITE:
if ( (FD_ISSET (ds, (fd_set*)read_fd_set)) &&
(pos->read_buffer_size > pos->read_buffer_offset) )
pos->read_handler (pos);
if (FD_ISSET (ds, (fd_set*)write_fd_set))
pos->write_handler (pos);
break;
case MHD_EVENT_LOOP_INFO_BLOCK:
if ( (FD_ISSET (ds, (fd_set*)read_fd_set)) &&
(pos->read_buffer_size > pos->read_buffer_offset) )
pos->read_handler (pos);
break;
case MHD_EVENT_LOOP_INFO_CLEANUP:
/* should never happen */
break;
}
pos->idle_handler (pos);
}
}
MHD_cleanup_connections (daemon);
return MHD_YES;
}
/**
* Main internal select() call. Will compute select sets, call select()
* and then #MHD_run_from_select with the result.
*
* @param daemon daemon to run select() loop for
* @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
* @return #MHD_NO on serious errors, #MHD_YES on success
*/
static int
MHD_select (struct MHD_Daemon *daemon,
int may_block)
{
int num_ready;
fd_set rs;
fd_set ws;
fd_set es;
MHD_socket max;
struct timeval timeout;
struct timeval *tv;
MHD_UNSIGNED_LONG_LONG ltimeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if (MHD_YES == daemon->shutdown)
return MHD_NO;
FD_ZERO (&rs);
FD_ZERO (&ws);
FD_ZERO (&es);
max = MHD_INVALID_SOCKET;
if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
{
if ( (MHD_USE_SUSPEND_RESUME == (daemon->options & MHD_USE_SUSPEND_RESUME)) &&
(MHD_YES == resume_suspended_connections (daemon)) )
may_block = MHD_NO;
/* single-threaded, go over everything */
if (MHD_NO == MHD_get_fdset2 (daemon, &rs, &ws, &es, &max, FD_SETSIZE))
return MHD_NO;
/* If we're at the connection limit, no need to
accept new connections; however, make sure
we do not miss the shutdown, so only do this
optimization if we have a shutdown signaling
pipe. */
if ( (MHD_INVALID_SOCKET != daemon->socket_fd) &&
(daemon->connections == daemon->connection_limit) &&
(0 != (daemon->options & MHD_USE_PIPE_FOR_SHUTDOWN)) )
FD_CLR (daemon->socket_fd, &rs);
}
else
{
/* accept only, have one thread per connection */
if ( (MHD_INVALID_SOCKET != daemon->socket_fd) &&
(MHD_YES != add_to_fd_set (daemon->socket_fd,
&rs,
&max,
FD_SETSIZE)) )
return MHD_NO;
}
if ( (MHD_INVALID_PIPE_ != daemon->wpipe[0]) &&
(MHD_YES != add_to_fd_set (daemon->wpipe[0],
&rs,
&max,
FD_SETSIZE)) )
return MHD_NO;
tv = NULL;
if (MHD_NO == may_block)
{
timeout.tv_usec = 0;
timeout.tv_sec = 0;
tv = &timeout;
}
else if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES == MHD_get_timeout (daemon, &ltimeout)) )
{
/* ltimeout is in ms */
timeout.tv_usec = (ltimeout % 1000) * 1000;
timeout.tv_sec = ltimeout / 1000;
tv = &timeout;
}
if (MHD_INVALID_SOCKET == max)
return MHD_YES;
num_ready = MHD_SYS_select_ (max + 1, &rs, &ws, &es, tv);
if (MHD_YES == daemon->shutdown)
return MHD_NO;
if (num_ready < 0)
{
if (EINTR == MHD_socket_errno_)
return MHD_YES;
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"select failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
return MHD_NO;
}
return MHD_run_from_select (daemon, &rs, &ws, &es);
}
#ifdef HAVE_POLL
/**
* Process all of our connections and possibly the server
* socket using poll().
*
* @param daemon daemon to run poll loop for
* @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
* @return #MHD_NO on serious errors, #MHD_YES on success
*/
static int
MHD_poll_all (struct MHD_Daemon *daemon,
int may_block)
{
unsigned int num_connections;
struct MHD_Connection *pos;
struct MHD_Connection *next;
if ( (MHD_USE_SUSPEND_RESUME == (daemon->options & MHD_USE_SUSPEND_RESUME)) &&
(MHD_YES == resume_suspended_connections (daemon)) )
may_block = MHD_NO;
/* count number of connections and thus determine poll set size */
num_connections = 0;
for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
num_connections++;
{
MHD_UNSIGNED_LONG_LONG ltimeout;
unsigned int i;
int timeout;
unsigned int poll_server;
int poll_listen;
int poll_pipe;
char tmp;
struct pollfd *p;
p = malloc(sizeof (struct pollfd) * (2 + num_connections));
if (NULL == p)
{
#if HAVE_MESSAGES
MHD_DLOG(daemon,
"Error allocating memory: %s\n",
MHD_strerror_(errno));
#endif
return MHD_NO;
}
memset (p, 0, sizeof (struct pollfd) * (2 + num_connections));
poll_server = 0;
poll_listen = -1;
if ( (MHD_INVALID_SOCKET != daemon->socket_fd) &&
(daemon->connections < daemon->connection_limit) )
{
/* only listen if we are not at the connection limit */
p[poll_server].fd = daemon->socket_fd;
p[poll_server].events = POLLIN;
p[poll_server].revents = 0;
poll_listen = (int) poll_server;
poll_server++;
}
poll_pipe = -1;
if (MHD_INVALID_PIPE_ != daemon->wpipe[0])
{
p[poll_server].fd = daemon->wpipe[0];
p[poll_server].events = POLLIN;
p[poll_server].revents = 0;
poll_pipe = (int) poll_server;
poll_server++;
}
if (may_block == MHD_NO)
timeout = 0;
else if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
(MHD_YES != MHD_get_timeout (daemon, &ltimeout)) )
timeout = -1;
else
timeout = (ltimeout > INT_MAX) ? INT_MAX : (int) ltimeout;
i = 0;
for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
{
p[poll_server+i].fd = pos->socket_fd;
switch (pos->event_loop_info)
{
case MHD_EVENT_LOOP_INFO_READ:
p[poll_server+i].events |= POLLIN;
break;
case MHD_EVENT_LOOP_INFO_WRITE:
p[poll_server+i].events |= POLLOUT;
if (pos->read_buffer_size > pos->read_buffer_offset)
p[poll_server+i].events |= POLLIN;
break;
case MHD_EVENT_LOOP_INFO_BLOCK:
if (pos->read_buffer_size > pos->read_buffer_offset)
p[poll_server+i].events |= POLLIN;
break;
case MHD_EVENT_LOOP_INFO_CLEANUP:
timeout = 0; /* clean up "pos" immediately */
break;
}
i++;
}
if (0 == poll_server + num_connections)
{
free(p);
return MHD_YES;
}
if (MHD_sys_poll_(p, poll_server + num_connections, timeout) < 0)
{
if (EINTR == MHD_socket_errno_)
{
free(p);
return MHD_YES;
}
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"poll failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
free(p);
return MHD_NO;
}
/* handle shutdown */
if (MHD_YES == daemon->shutdown)
{
free(p);
return MHD_NO;
}
i = 0;
next = daemon->connections_head;
while (NULL != (pos = next))
{
next = pos->next;
switch (pos->event_loop_info)
{
case MHD_EVENT_LOOP_INFO_READ:
/* first, sanity checks */
if (i >= num_connections)
break; /* connection list changed somehow, retry later ... */
if (p[poll_server+i].fd != pos->socket_fd)
break; /* fd mismatch, something else happened, retry later ... */
/* normal handling */
if (0 != (p[poll_server+i].revents & POLLIN))
pos->read_handler (pos);
pos->idle_handler (pos);
i++;
break;
case MHD_EVENT_LOOP_INFO_WRITE:
/* first, sanity checks */
if (i >= num_connections)
break; /* connection list changed somehow, retry later ... */
if (p[poll_server+i].fd != pos->socket_fd)
break; /* fd mismatch, something else happened, retry later ... */
/* normal handling */
if (0 != (p[poll_server+i].revents & POLLIN))
pos->read_handler (pos);
if (0 != (p[poll_server+i].revents & POLLOUT))
pos->write_handler (pos);
pos->idle_handler (pos);
i++;
break;
case MHD_EVENT_LOOP_INFO_BLOCK:
if (0 != (p[poll_server+i].revents & POLLIN))
pos->read_handler (pos);
pos->idle_handler (pos);
break;
case MHD_EVENT_LOOP_INFO_CLEANUP:
pos->idle_handler (pos);
break;
}
}
/* handle 'listen' FD */
if ( (-1 != poll_listen) &&
(0 != (p[poll_listen].revents & POLLIN)) )
(void) MHD_accept_connection (daemon);
/* handle pipe FD */
if ( (-1 != poll_pipe) &&
(0 != (p[poll_pipe].revents & POLLIN)) )
(void) MHD_pipe_read_ (daemon->wpipe[0], &tmp, sizeof (tmp));
free(p);
}
return MHD_YES;
}
/**
* Process only the listen socket using poll().
*
* @param daemon daemon to run poll loop for
* @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
* @return #MHD_NO on serious errors, #MHD_YES on success
*/
static int
MHD_poll_listen_socket (struct MHD_Daemon *daemon,
int may_block)
{
struct pollfd p[2];
int timeout;
unsigned int poll_count;
int poll_listen;
memset (&p, 0, sizeof (p));
poll_count = 0;
poll_listen = -1;
if (MHD_INVALID_SOCKET != daemon->socket_fd)
{
p[poll_count].fd = daemon->socket_fd;
p[poll_count].events = POLLIN;
p[poll_count].revents = 0;
poll_listen = poll_count;
poll_count++;
}
if (MHD_INVALID_PIPE_ != daemon->wpipe[0])
{
p[poll_count].fd = daemon->wpipe[0];
p[poll_count].events = POLLIN;
p[poll_count].revents = 0;
poll_count++;
}
if (MHD_NO == may_block)
timeout = 0;
else
timeout = -1;
if (0 == poll_count)
return MHD_YES;
if (MHD_sys_poll_(p, poll_count, timeout) < 0)
{
if (EINTR == MHD_socket_errno_)
return MHD_YES;
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"poll failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
return MHD_NO;
}
/* handle shutdown */
if (MHD_YES == daemon->shutdown)
return MHD_NO;
if ( (-1 != poll_listen) &&
(0 != (p[poll_listen].revents & POLLIN)) )
(void) MHD_accept_connection (daemon);
return MHD_YES;
}
#endif
/**
* Do poll()-based processing.
*
* @param daemon daemon to run poll()-loop for
* @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
* @return #MHD_NO on serious errors, #MHD_YES on success
*/
static int
MHD_poll (struct MHD_Daemon *daemon,
int may_block)
{
#ifdef HAVE_POLL
if (MHD_YES == daemon->shutdown)
return MHD_NO;
if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
return MHD_poll_all (daemon, may_block);
else
return MHD_poll_listen_socket (daemon, may_block);
#else
return MHD_NO;
#endif
}
#if EPOLL_SUPPORT
/**
* How many events to we process at most per epoll() call? Trade-off
* between required stack-size and number of system calls we have to
* make; 128 should be way enough to avoid more than one system call
* for most scenarios, and still be moderate in stack size
* consumption. Embedded systems might want to choose a smaller value
* --- but why use epoll() on such a system in the first place?
*/
#define MAX_EVENTS 128
/**
* Do epoll()-based processing (this function is allowed to
* block if @a may_block is set to #MHD_YES).
*
* @param daemon daemon to run poll loop for
* @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
* @return #MHD_NO on serious errors, #MHD_YES on success
*/
static int
MHD_epoll (struct MHD_Daemon *daemon,
int may_block)
{
struct MHD_Connection *pos;
struct MHD_Connection *next;
struct epoll_event events[MAX_EVENTS];
struct epoll_event event;
int timeout_ms;
MHD_UNSIGNED_LONG_LONG timeout_ll;
int num_events;
unsigned int i;
unsigned int series_length;
char tmp;
if (-1 == daemon->epoll_fd)
return MHD_NO; /* we're down! */
if (MHD_YES == daemon->shutdown)
return MHD_NO;
if ( (MHD_INVALID_SOCKET != daemon->socket_fd) &&
(daemon->connections < daemon->connection_limit) &&
(MHD_NO == daemon->listen_socket_in_epoll) )
{
event.events = EPOLLIN;
event.data.ptr = daemon;
if (0 != epoll_ctl (daemon->epoll_fd,
EPOLL_CTL_ADD,
daemon->socket_fd,
&event))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Call to epoll_ctl failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
return MHD_NO;
}
daemon->listen_socket_in_epoll = MHD_YES;
}
if ( (MHD_YES == daemon->listen_socket_in_epoll) &&
(daemon->connections == daemon->connection_limit) )
{
/* we're at the connection limit, disable listen socket
for event loop for now */
if (0 != epoll_ctl (daemon->epoll_fd,
EPOLL_CTL_DEL,
daemon->socket_fd,
NULL))
MHD_PANIC ("Failed to remove listen FD from epoll set\n");
daemon->listen_socket_in_epoll = MHD_NO;
}
if (MHD_YES == may_block)
{
if (MHD_YES == MHD_get_timeout (daemon,
&timeout_ll))
{
if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX)
timeout_ms = INT_MAX;
else
timeout_ms = (int) timeout_ll;
}
else
timeout_ms = -1;
}
else
timeout_ms = 0;
/* drain 'epoll' event queue; need to iterate as we get at most
MAX_EVENTS in one system call here; in practice this should
pretty much mean only one round, but better an extra loop here
than unfair behavior... */
num_events = MAX_EVENTS;
while (MAX_EVENTS == num_events)
{
/* update event masks */
num_events = epoll_wait (daemon->epoll_fd,
events, MAX_EVENTS, timeout_ms);
if (-1 == num_events)
{
if (EINTR == MHD_socket_errno_)
return MHD_YES;
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Call to epoll_wait failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
return MHD_NO;
}
for (i=0;i<(unsigned int) num_events;i++)
{
if (NULL == events[i].data.ptr)
continue; /* shutdown signal! */
if ( (MHD_INVALID_PIPE_ != daemon->wpipe[0]) &&
(daemon->wpipe[0] == events[i].data.fd) )
{
(void) MHD_pipe_read_ (daemon->wpipe[0], &tmp, sizeof (tmp));
continue;
}
if (daemon != events[i].data.ptr)
{
/* this is an event relating to a 'normal' connection,
remember the event and if appropriate mark the
connection as 'eready'. */
pos = events[i].data.ptr;
if (0 != (events[i].events & EPOLLIN))
{
pos->epoll_state |= MHD_EPOLL_STATE_READ_READY;
if ( ( (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info) ||
(pos->read_buffer_size > pos->read_buffer_offset) ) &&
(0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
{
EDLL_insert (daemon->eready_head,
daemon->eready_tail,
pos);
pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
}
}
if (0 != (events[i].events & EPOLLOUT))
{
pos->epoll_state |= MHD_EPOLL_STATE_WRITE_READY;
if ( (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info) &&
(0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
{
EDLL_insert (daemon->eready_head,
daemon->eready_tail,
pos);
pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
}
}
}
else /* must be listen socket */
{
/* run 'accept' until it fails or we are not allowed to take
on more connections */
series_length = 0;
while ( (MHD_YES == MHD_accept_connection (daemon)) &&
(daemon->connections < daemon->connection_limit) &&
(series_length < 128) )
series_length++;
}
}
}
/* we handle resumes here because we may have ready connections
that will not be placed into the epoll list immediately. */
if ( (MHD_USE_SUSPEND_RESUME == (daemon->options & MHD_USE_SUSPEND_RESUME)) &&
(MHD_YES == resume_suspended_connections (daemon)) )
may_block = MHD_NO;
/* process events for connections */
while (NULL != (pos = daemon->eready_tail))
{
EDLL_remove (daemon->eready_head,
daemon->eready_tail,
pos);
pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
if (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info)
pos->read_handler (pos);
if (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info)
pos->write_handler (pos);
pos->idle_handler (pos);
}
/* Finally, handle timed-out connections; we need to do this here
as the epoll mechanism won't call the 'idle_handler' on everything,
as the other event loops do. As timeouts do not get an explicit
event, we need to find those connections that might have timed out
here.
Connections with custom timeouts must all be looked at, as we
do not bother to sort that (presumably very short) list. */
next = daemon->manual_timeout_head;
while (NULL != (pos = next))
{
next = pos->nextX;
pos->idle_handler (pos);
}
/* Connections with the default timeout are sorted by prepending
them to the head of the list whenever we touch the connection;
thus it sufficies to iterate from the tail until the first
connection is NOT timed out */
next = daemon->normal_timeout_tail;
while (NULL != (pos = next))
{
next = pos->prevX;
pos->idle_handler (pos);
if (MHD_CONNECTION_CLOSED != pos->state)
break; /* sorted by timeout, no need to visit the rest! */
}
return MHD_YES;
}
#endif
/**
* Run webserver operations (without blocking unless in client
* callbacks). This method should be called by clients in combination
* with #MHD_get_fdset if the client-controlled select method is used.
*
* This function is a convenience method, which is useful if the
* fd_sets from #MHD_get_fdset were not directly passed to `select()`;
* with this function, MHD will internally do the appropriate `select()`
* call itself again. While it is always safe to call #MHD_run (in
* external select mode), you should call #MHD_run_from_select if
* performance is important (as it saves an expensive call to
* `select()`).
*
* @param daemon daemon to run
* @return #MHD_YES on success, #MHD_NO if this
* daemon was not started with the right
* options for this call.
* @ingroup event
*/
int
MHD_run (struct MHD_Daemon *daemon)
{
if ( (MHD_YES == daemon->shutdown) ||
(0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
(0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) )
return MHD_NO;
if (0 != (daemon->options & MHD_USE_POLL))
{
MHD_poll (daemon, MHD_NO);
MHD_cleanup_connections (daemon);
}
#if EPOLL_SUPPORT
else if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
{
MHD_epoll (daemon, MHD_NO);
MHD_cleanup_connections (daemon);
}
#endif
else
{
MHD_select (daemon, MHD_NO);
/* MHD_select does MHD_cleanup_connections already */
}
return MHD_YES;
}
/**
* Thread that runs the select loop until the daemon
* is explicitly shut down.
*
* @param cls 'struct MHD_Deamon' to run select loop in a thread for
* @return always 0 (on shutdown)
*/
static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
MHD_select_thread (void *cls)
{
struct MHD_Daemon *daemon = cls;
while (MHD_YES != daemon->shutdown)
{
if (0 != (daemon->options & MHD_USE_POLL))
MHD_poll (daemon, MHD_YES);
#if EPOLL_SUPPORT
else if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
MHD_epoll (daemon, MHD_YES);
#endif
else
MHD_select (daemon, MHD_YES);
MHD_cleanup_connections (daemon);
}
return (MHD_THRD_RTRN_TYPE_)0;
}
/**
* Process escape sequences ('%HH') Updates val in place; the
* result should be UTF-8 encoded and cannot be larger than the input.
* The result must also still be 0-terminated.
*
* @param cls closure (use NULL)
* @param connection handle to connection, not used
* @param val value to unescape (modified in the process)
* @return length of the resulting val (strlen(val) maybe
* shorter afterwards due to elimination of escape sequences)
*/
static size_t
unescape_wrapper (void *cls,
struct MHD_Connection *connection,
char *val)
{
return MHD_http_unescape (val);
}
/**
* Start a webserver on the given port. Variadic version of
* #MHD_start_daemon_va.
*
* @param flags combination of `enum MHD_FLAG` values
* @param port port to bind to
* @param apc callback to call to check which clients
* will be allowed to connect; you can pass NULL
* in which case connections from any IP will be
* accepted
* @param apc_cls extra argument to @a apc
* @param dh handler called for all requests (repeatedly)
* @param dh_cls extra argument to @a dh
* @return NULL on error, handle to daemon on success
* @ingroup event
*/
struct MHD_Daemon *
MHD_start_daemon (unsigned int flags,
uint16_t port,
MHD_AcceptPolicyCallback apc,
void *apc_cls,
MHD_AccessHandlerCallback dh, void *dh_cls, ...)
{
struct MHD_Daemon *daemon;
va_list ap;
va_start (ap, dh_cls);
daemon = MHD_start_daemon_va (flags, port, apc, apc_cls, dh, dh_cls, ap);
va_end (ap);
return daemon;
}
/**
* Stop accepting connections from the listening socket. Allows
* clients to continue processing, but stops accepting new
* connections. Note that the caller is responsible for closing the
* returned socket; however, if MHD is run using threads (anything but
* external select mode), it must not be closed until AFTER
* #MHD_stop_daemon has been called (as it is theoretically possible
* that an existing thread is still using it).
*
* Note that some thread modes require the caller to have passed
* #MHD_USE_PIPE_FOR_SHUTDOWN when using this API. If this daemon is
* in one of those modes and this option was not given to
* #MHD_start_daemon, this function will return #MHD_INVALID_SOCKET.
*
* @param daemon daemon to stop accepting new connections for
* @return old listen socket on success, #MHD_INVALID_SOCKET if
* the daemon was already not listening anymore
* @ingroup specialized
*/
MHD_socket
MHD_quiesce_daemon (struct MHD_Daemon *daemon)
{
unsigned int i;
MHD_socket ret;
ret = daemon->socket_fd;
if (MHD_INVALID_SOCKET == ret)
return MHD_INVALID_SOCKET;
if ( (MHD_INVALID_PIPE_ == daemon->wpipe[1]) &&
(0 != (daemon->options & (MHD_USE_SELECT_INTERNALLY | MHD_USE_THREAD_PER_CONNECTION))) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Using MHD_quiesce_daemon in this mode requires MHD_USE_PIPE_FOR_SHUTDOWN\n");
#endif
return MHD_INVALID_SOCKET;
}
if (NULL != daemon->worker_pool)
for (i = 0; i < daemon->worker_pool_size; i++)
{
daemon->worker_pool[i].socket_fd = MHD_INVALID_SOCKET;
#if EPOLL_SUPPORT
if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
(-1 != daemon->worker_pool[i].epoll_fd) &&
(MHD_YES == daemon->worker_pool[i].listen_socket_in_epoll) )
{
if (0 != epoll_ctl (daemon->worker_pool[i].epoll_fd,
EPOLL_CTL_DEL,
ret,
NULL))
MHD_PANIC ("Failed to remove listen FD from epoll set\n");
daemon->worker_pool[i].listen_socket_in_epoll = MHD_NO;
}
#endif
}
daemon->socket_fd = MHD_INVALID_SOCKET;
#if EPOLL_SUPPORT
if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
(-1 != daemon->epoll_fd) &&
(MHD_YES == daemon->listen_socket_in_epoll) )
{
if (0 != epoll_ctl (daemon->epoll_fd,
EPOLL_CTL_DEL,
ret,
NULL))
MHD_PANIC ("Failed to remove listen FD from epoll set\n");
daemon->listen_socket_in_epoll = MHD_NO;
}
#endif
return ret;
}
/**
* Signature of the MHD custom logger function.
*
* @param cls closure
* @param format format string
* @param va arguments to the format string (fprintf-style)
*/
typedef void (*VfprintfFunctionPointerType)(void *cls,
const char *format,
va_list va);
/**
* Parse a list of options given as varargs.
*
* @param daemon the daemon to initialize
* @param servaddr where to store the server's listen address
* @param ap the options
* @return #MHD_YES on success, #MHD_NO on error
*/
static int
parse_options_va (struct MHD_Daemon *daemon,
const struct sockaddr **servaddr,
va_list ap);
/**
* Parse a list of options given as varargs.
*
* @param daemon the daemon to initialize
* @param servaddr where to store the server's listen address
* @param ... the options
* @return #MHD_YES on success, #MHD_NO on error
*/
static int
parse_options (struct MHD_Daemon *daemon,
const struct sockaddr **servaddr,
...)
{
va_list ap;
int ret;
va_start (ap, servaddr);
ret = parse_options_va (daemon, servaddr, ap);
va_end (ap);
return ret;
}
/**
* Parse a list of options given as varargs.
*
* @param daemon the daemon to initialize
* @param servaddr where to store the server's listen address
* @param ap the options
* @return #MHD_YES on success, #MHD_NO on error
*/
static int
parse_options_va (struct MHD_Daemon *daemon,
const struct sockaddr **servaddr,
va_list ap)
{
enum MHD_OPTION opt;
struct MHD_OptionItem *oa;
unsigned int i;
#if HTTPS_SUPPORT
int ret;
const char *pstr;
#endif
while (MHD_OPTION_END != (opt = (enum MHD_OPTION) va_arg (ap, int)))
{
switch (opt)
{
case MHD_OPTION_CONNECTION_MEMORY_LIMIT:
daemon->pool_size = va_arg (ap, size_t);
break;
case MHD_OPTION_CONNECTION_MEMORY_INCREMENT:
daemon->pool_increment= va_arg (ap, size_t);
break;
case MHD_OPTION_CONNECTION_LIMIT:
daemon->connection_limit = va_arg (ap, unsigned int);
break;
case MHD_OPTION_CONNECTION_TIMEOUT:
daemon->connection_timeout = va_arg (ap, unsigned int);
break;
case MHD_OPTION_NOTIFY_COMPLETED:
daemon->notify_completed =
va_arg (ap, MHD_RequestCompletedCallback);
daemon->notify_completed_cls = va_arg (ap, void *);
break;
case MHD_OPTION_NOTIFY_CONNECTION:
daemon->notify_connection =
va_arg (ap, MHD_NotifyConnectionCallback);
daemon->notify_connection_cls = va_arg (ap, void *);
break;
case MHD_OPTION_PER_IP_CONNECTION_LIMIT:
daemon->per_ip_connection_limit = va_arg (ap, unsigned int);
break;
case MHD_OPTION_SOCK_ADDR:
*servaddr = va_arg (ap, const struct sockaddr *);
break;
case MHD_OPTION_URI_LOG_CALLBACK:
daemon->uri_log_callback =
va_arg (ap, LogCallback);
daemon->uri_log_callback_cls = va_arg (ap, void *);
break;
case MHD_OPTION_THREAD_POOL_SIZE:
daemon->worker_pool_size = va_arg (ap, unsigned int);
if (daemon->worker_pool_size >= (SIZE_MAX / sizeof (struct MHD_Daemon)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Specified thread pool size (%u) too big\n",
daemon->worker_pool_size);
#endif
return MHD_NO;
}
break;
#if HTTPS_SUPPORT
case MHD_OPTION_HTTPS_MEM_KEY:
if (0 != (daemon->options & MHD_USE_SSL))
{
daemon->https_mem_key = va_arg (ap, const char *);
}
else
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD HTTPS option %d passed to MHD but MHD_USE_SSL not set\n",
opt);
#endif
}
break;
case MHD_OPTION_HTTPS_KEY_PASSWORD:
if (0 != (daemon->options & MHD_USE_SSL))
{
daemon->https_key_password = va_arg (ap, const char *);
}
else
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD HTTPS option %d passed to MHD but MHD_USE_SSL not set\n",
opt);
#endif
}
break;
case MHD_OPTION_HTTPS_MEM_CERT:
if (0 != (daemon->options & MHD_USE_SSL))
{
daemon->https_mem_cert = va_arg (ap, const char *);
}
else
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD HTTPS option %d passed to MHD but MHD_USE_SSL not set\n",
opt);
#endif
}
break;
case MHD_OPTION_HTTPS_MEM_TRUST:
if (0 != (daemon->options & MHD_USE_SSL))
{
daemon->https_mem_trust = va_arg (ap, const char *);
}
else
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD HTTPS option %d passed to MHD but MHD_USE_SSL not set\n",
opt);
#endif
}
break;
case MHD_OPTION_HTTPS_CRED_TYPE:
break;
case MHD_OPTION_HTTPS_MEM_DHPARAMS:
if (0 != (daemon->options & MHD_USE_SSL))
{
daemon->https_mem_dhparams = va_arg (ap, const char *);
}
else
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD HTTPS option %d passed to MHD but MHD_USE_SSL not set\n",
opt);
#endif
}
break;
case MHD_OPTION_HTTPS_PRIORITIES:
if (0 != (daemon->options & MHD_USE_SSL))
{
daemon->https_mem_cipher = va_arg (ap, const char *);
}
else
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD HTTPS option %d passed to MHD but MHD_USE_SSL not set\n",
opt);
#endif
}
break;
case MHD_OPTION_HTTPS_CERT_CALLBACK:
break;
#endif
#ifdef DAUTH_SUPPORT
case MHD_OPTION_DIGEST_AUTH_RANDOM:
daemon->digest_auth_rand_size = va_arg (ap, size_t);
daemon->digest_auth_random = va_arg (ap, const char *);
break;
case MHD_OPTION_NONCE_NC_SIZE:
daemon->nonce_nc_size = va_arg (ap, unsigned int);
break;
#endif
case MHD_OPTION_LISTEN_SOCKET:
daemon->socket_fd = va_arg (ap, MHD_socket);
break;
case MHD_OPTION_EXTERNAL_LOGGER:
#if HAVE_MESSAGES
daemon->custom_error_log =
va_arg (ap, VfprintfFunctionPointerType);
daemon->custom_error_log_cls = va_arg (ap, void *);
#else
va_arg (ap, VfprintfFunctionPointerType);
va_arg (ap, void *);
#endif
break;
case MHD_OPTION_THREAD_STACK_SIZE:
daemon->thread_stack_size = va_arg (ap, size_t);
break;
#ifdef TCP_FASTOPEN
case MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE:
daemon->fastopen_queue_size = va_arg (ap, unsigned int);
break;
#endif
case MHD_OPTION_LISTENING_ADDRESS_REUSE:
daemon->listening_address_reuse = va_arg (ap, unsigned int) ? 1 : -1;
break;
case MHD_OPTION_ARRAY:
oa = va_arg (ap, struct MHD_OptionItem*);
i = 0;
while (MHD_OPTION_END != (opt = oa[i].option))
{
switch (opt)
{
/* all options taking 'size_t' */
case MHD_OPTION_CONNECTION_MEMORY_LIMIT:
case MHD_OPTION_CONNECTION_MEMORY_INCREMENT:
case MHD_OPTION_THREAD_STACK_SIZE:
if (MHD_YES != parse_options (daemon,
servaddr,
opt,
(size_t) oa[i].value,
MHD_OPTION_END))
return MHD_NO;
break;
/* all options taking 'unsigned int' */
case MHD_OPTION_NONCE_NC_SIZE:
case MHD_OPTION_CONNECTION_LIMIT:
case MHD_OPTION_CONNECTION_TIMEOUT:
case MHD_OPTION_PER_IP_CONNECTION_LIMIT:
case MHD_OPTION_THREAD_POOL_SIZE:
case MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE:
case MHD_OPTION_LISTENING_ADDRESS_REUSE:
if (MHD_YES != parse_options (daemon,
servaddr,
opt,
(unsigned int) oa[i].value,
MHD_OPTION_END))
return MHD_NO;
break;
/* all options taking 'enum' */
case MHD_OPTION_HTTPS_CRED_TYPE:
if (MHD_YES != parse_options (daemon,
servaddr,
opt,
(int) oa[i].value,
MHD_OPTION_END))
return MHD_NO;
break;
/* all options taking 'MHD_socket' */
case MHD_OPTION_LISTEN_SOCKET:
if (MHD_YES != parse_options (daemon,
servaddr,
opt,
(MHD_socket) oa[i].value,
MHD_OPTION_END))
return MHD_NO;
break;
/* all options taking one pointer */
case MHD_OPTION_SOCK_ADDR:
case MHD_OPTION_HTTPS_MEM_KEY:
case MHD_OPTION_HTTPS_KEY_PASSWORD:
case MHD_OPTION_HTTPS_MEM_CERT:
case MHD_OPTION_HTTPS_MEM_TRUST:
case MHD_OPTION_HTTPS_MEM_DHPARAMS:
case MHD_OPTION_HTTPS_PRIORITIES:
case MHD_OPTION_ARRAY:
case MHD_OPTION_HTTPS_CERT_CALLBACK:
if (MHD_YES != parse_options (daemon,
servaddr,
opt,
oa[i].ptr_value,
MHD_OPTION_END))
return MHD_NO;
break;
/* all options taking two pointers */
case MHD_OPTION_NOTIFY_COMPLETED:
case MHD_OPTION_NOTIFY_CONNECTION:
case MHD_OPTION_URI_LOG_CALLBACK:
case MHD_OPTION_EXTERNAL_LOGGER:
case MHD_OPTION_UNESCAPE_CALLBACK:
if (MHD_YES != parse_options (daemon,
servaddr,
opt,
(void *) oa[i].value,
oa[i].ptr_value,
MHD_OPTION_END))
return MHD_NO;
break;
/* options taking size_t-number followed by pointer */
case MHD_OPTION_DIGEST_AUTH_RANDOM:
if (MHD_YES != parse_options (daemon,
servaddr,
opt,
(size_t) oa[i].value,
oa[i].ptr_value,
MHD_OPTION_END))
return MHD_NO;
break;
default:
return MHD_NO;
}
i++;
}
break;
case MHD_OPTION_UNESCAPE_CALLBACK:
daemon->unescape_callback =
va_arg (ap, UnescapeCallback);
daemon->unescape_callback_cls = va_arg (ap, void *);
break;
default:
#if HAVE_MESSAGES
if (((opt >= MHD_OPTION_HTTPS_MEM_KEY) &&
(opt <= MHD_OPTION_HTTPS_PRIORITIES)) || (opt == MHD_OPTION_HTTPS_MEM_TRUST))
{
MHD_DLOG (daemon,
"MHD HTTPS option %d passed to MHD compiled without HTTPS support\n",
opt);
}
else
{
MHD_DLOG (daemon,
"Invalid option %d! (Did you terminate the list with MHD_OPTION_END?)\n",
opt);
}
#endif
return MHD_NO;
}
}
return MHD_YES;
}
/**
* Create a listen socket, if possible with SOCK_CLOEXEC flag set.
*
* @param daemon daemon for which we create the socket
* @param domain socket domain (i.e. PF_INET)
* @param type socket type (usually SOCK_STREAM)
* @param protocol desired protocol, 0 for default
*/
static MHD_socket
create_socket (struct MHD_Daemon *daemon,
int domain, int type, int protocol)
{
int ctype = type | SOCK_CLOEXEC;
MHD_socket fd;
/* use SOCK_STREAM rather than ai_socktype: some getaddrinfo
* implementations do not set ai_socktype, e.g. RHL6.2. */
fd = socket (domain, ctype, protocol);
if ( (MHD_INVALID_SOCKET == fd) && (EINVAL == MHD_socket_errno_) && (0 != SOCK_CLOEXEC) )
{
ctype = type;
fd = socket(domain, type, protocol);
}
if (MHD_INVALID_SOCKET == fd)
return MHD_INVALID_SOCKET;
if (type == ctype)
make_nonblocking_noninheritable (daemon, fd);
return fd;
}
#if EPOLL_SUPPORT
/**
* Setup epoll() FD for the daemon and initialize it to listen
* on the listen FD.
*
* @param daemon daemon to initialize for epoll()
* @return #MHD_YES on success, #MHD_NO on failure
*/
static int
setup_epoll_to_listen (struct MHD_Daemon *daemon)
{
struct epoll_event event;
#ifdef HAVE_EPOLL_CREATE1
daemon->epoll_fd = epoll_create1 (EPOLL_CLOEXEC);
#else /* !HAVE_EPOLL_CREATE1 */
daemon->epoll_fd = epoll_create (MAX_EVENTS);
#endif /* !HAVE_EPOLL_CREATE1 */
if (-1 == daemon->epoll_fd)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Call to epoll_create1 failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
return MHD_NO;
}
#ifndef HAVE_EPOLL_CREATE1
else
{
int fdflags = fcntl (daemon->epoll_fd, F_GETFD);
if (0 > fdflags || 0 > fcntl (daemon->epoll_fd, F_SETFD, fdflags | FD_CLOEXEC))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to change flags on epoll fd: %s\n",
MHD_socket_last_strerr_ ());
#endif /* HAVE_MESSAGES */
}
}
#endif /* !HAVE_EPOLL_CREATE1 */
if (0 == EPOLL_CLOEXEC)
make_nonblocking_noninheritable (daemon,
daemon->epoll_fd);
if (MHD_INVALID_SOCKET == daemon->socket_fd)
return MHD_YES; /* non-listening daemon */
event.events = EPOLLIN;
event.data.ptr = daemon;
if (0 != epoll_ctl (daemon->epoll_fd,
EPOLL_CTL_ADD,
daemon->socket_fd,
&event))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Call to epoll_ctl failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
return MHD_NO;
}
if ( (MHD_INVALID_PIPE_ != daemon->wpipe[0]) &&
(MHD_USE_SUSPEND_RESUME == (daemon->options & MHD_USE_SUSPEND_RESUME)) )
{
event.events = EPOLLIN | EPOLLET;
event.data.ptr = NULL;
event.data.fd = daemon->wpipe[0];
if (0 != epoll_ctl (daemon->epoll_fd,
EPOLL_CTL_ADD,
daemon->wpipe[0],
&event))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Call to epoll_ctl failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
return MHD_NO;
}
}
daemon->listen_socket_in_epoll = MHD_YES;
return MHD_YES;
}
#endif
/**
* Start a webserver on the given port.
*
* @param flags combination of `enum MHD_FLAG` values
* @param port port to bind to (in host byte order)
* @param apc callback to call to check which clients
* will be allowed to connect; you can pass NULL
* in which case connections from any IP will be
* accepted
* @param apc_cls extra argument to @a apc
* @param dh handler called for all requests (repeatedly)
* @param dh_cls extra argument to @a dh
* @param ap list of options (type-value pairs,
* terminated with #MHD_OPTION_END).
* @return NULL on error, handle to daemon on success
* @ingroup event
*/
struct MHD_Daemon *
MHD_start_daemon_va (unsigned int flags,
uint16_t port,
MHD_AcceptPolicyCallback apc,
void *apc_cls,
MHD_AccessHandlerCallback dh, void *dh_cls,
va_list ap)
{
const int on = 1;
struct MHD_Daemon *daemon;
MHD_socket socket_fd;
struct sockaddr_in servaddr4;
#if HAVE_INET6
struct sockaddr_in6 servaddr6;
#endif
const struct sockaddr *servaddr = NULL;
socklen_t addrlen;
unsigned int i;
int res_thread_create;
int use_pipe;
#ifndef HAVE_INET6
if (0 != (flags & MHD_USE_IPv6))
return NULL;
#endif
#ifndef HAVE_POLL
if (0 != (flags & MHD_USE_POLL))
return NULL;
#endif
#if ! HTTPS_SUPPORT
if (0 != (flags & MHD_USE_SSL))
return NULL;
#endif
#ifndef TCP_FASTOPEN
if (0 != (flags & MHD_USE_TCP_FASTOPEN))
return NULL;
#endif
if (NULL == dh)
return NULL;
if (NULL == (daemon = malloc (sizeof (struct MHD_Daemon))))
return NULL;
memset (daemon, 0, sizeof (struct MHD_Daemon));
#if EPOLL_SUPPORT
daemon->epoll_fd = -1;
#endif
/* try to open listen socket */
daemon->socket_fd = MHD_INVALID_SOCKET;
daemon->listening_address_reuse = 0;
daemon->options = flags;
#if WINDOWS
/* Winsock is broken with respect to 'shutdown';
this disables us calling 'shutdown' on W32. */
daemon->options |= MHD_USE_EPOLL_TURBO;
#endif
daemon->port = port;
daemon->apc = apc;
daemon->apc_cls = apc_cls;
daemon->default_handler = dh;
daemon->default_handler_cls = dh_cls;
daemon->connections = 0;
daemon->connection_limit = MHD_MAX_CONNECTIONS_DEFAULT;
daemon->pool_size = MHD_POOL_SIZE_DEFAULT;
daemon->pool_increment = MHD_BUF_INC_SIZE;
daemon->unescape_callback = &unescape_wrapper;
daemon->connection_timeout = 0; /* no timeout */
daemon->wpipe[0] = MHD_INVALID_PIPE_;
daemon->wpipe[1] = MHD_INVALID_PIPE_;
#if HAVE_MESSAGES
daemon->custom_error_log = (MHD_LogCallback) &vfprintf;
daemon->custom_error_log_cls = stderr;
#endif
#ifdef HAVE_LISTEN_SHUTDOWN
use_pipe = (0 != (daemon->options & (MHD_USE_NO_LISTEN_SOCKET | MHD_USE_PIPE_FOR_SHUTDOWN)));
#else
use_pipe = 1; /* yes, must use pipe to signal shutdown */
#endif
if (0 == (flags & (MHD_USE_SELECT_INTERNALLY | MHD_USE_THREAD_PER_CONNECTION)))
use_pipe = 0; /* useless if we are using 'external' select */
if ( (use_pipe) && (0 != MHD_pipe_ (daemon->wpipe)) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to create control pipe: %s\n",
MHD_strerror_ (errno));
#endif
free (daemon);
return NULL;
}
#ifndef WINDOWS
if ( (0 == (flags & (MHD_USE_POLL | MHD_USE_EPOLL_LINUX_ONLY))) &&
(1 == use_pipe) &&
(daemon->wpipe[0] >= FD_SETSIZE) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"file descriptor for control pipe exceeds maximum value\n");
#endif
if (0 != MHD_pipe_close_ (daemon->wpipe[0]))
MHD_PANIC ("close failed\n");
if (0 != MHD_pipe_close_ (daemon->wpipe[1]))
MHD_PANIC ("close failed\n");
free (daemon);
return NULL;
}
#endif
#ifdef DAUTH_SUPPORT
daemon->digest_auth_rand_size = 0;
daemon->digest_auth_random = NULL;
daemon->nonce_nc_size = 4; /* tiny */
#endif
if (MHD_YES != parse_options_va (daemon, &servaddr, ap))
{
free (daemon);
return NULL;
}
#ifdef DAUTH_SUPPORT
if (daemon->nonce_nc_size > 0)
{
if ( ( (size_t) (daemon->nonce_nc_size * sizeof (struct MHD_NonceNc))) /
sizeof(struct MHD_NonceNc) != daemon->nonce_nc_size)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Specified value for NC_SIZE too large\n");
#endif
free (daemon);
return NULL;
}
daemon->nnc = malloc (daemon->nonce_nc_size * sizeof (struct MHD_NonceNc));
if (NULL == daemon->nnc)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to allocate memory for nonce-nc map: %s\n",
MHD_strerror_ (errno));
#endif
free (daemon);
return NULL;
}
}
if (MHD_YES != MHD_mutex_create_ (&daemon->nnc_lock))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD failed to initialize nonce-nc mutex\n");
#endif
free (daemon->nnc);
free (daemon);
return NULL;
}
#endif
/* Thread pooling currently works only with internal select thread model */
if ( (0 == (flags & MHD_USE_SELECT_INTERNALLY)) &&
(daemon->worker_pool_size > 0) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD thread pooling only works with MHD_USE_SELECT_INTERNALLY\n");
#endif
goto free_and_fail;
}
if ( (MHD_USE_SUSPEND_RESUME == (flags & MHD_USE_SUSPEND_RESUME)) &&
(0 != (flags & MHD_USE_THREAD_PER_CONNECTION)) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_SUSPEND_RESUME is not supported.\n");
#endif
goto free_and_fail;
}
#ifdef __SYMBIAN32__
if (0 != (flags & (MHD_USE_SELECT_INTERNALLY | MHD_USE_THREAD_PER_CONNECTION)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Threaded operations are not supported on Symbian.\n");
#endif
goto free_and_fail;
}
#endif
if ( (MHD_INVALID_SOCKET == daemon->socket_fd) &&
(0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
{
/* try to open listen socket */
if (0 != (flags & MHD_USE_IPv6))
socket_fd = create_socket (daemon,
PF_INET6, SOCK_STREAM, 0);
else
socket_fd = create_socket (daemon,
PF_INET, SOCK_STREAM, 0);
if (MHD_INVALID_SOCKET == socket_fd)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Call to socket failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
goto free_and_fail;
}
/* Apply the socket options according to listening_address_reuse. */
if (0 == daemon->listening_address_reuse)
{
/* No user requirement, use "traditional" default SO_REUSEADDR,
and do not fail if it doesn't work */
if (0 > setsockopt (socket_fd,
SOL_SOCKET,
SO_REUSEADDR,
(void*)&on, sizeof (on)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"setsockopt failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
}
}
else if (daemon->listening_address_reuse > 0)
{
/* User requested to allow reusing listening address:port.
* Use SO_REUSEADDR on Windows and SO_REUSEPORT on most platforms.
* Fail if SO_REUSEPORT does not exist or setsockopt fails.
*/
#ifdef _WIN32
/* SO_REUSEADDR on W32 has the same semantics
as SO_REUSEPORT on BSD/Linux */
if (0 > setsockopt (socket_fd,
SOL_SOCKET,
SO_REUSEADDR,
(void*)&on, sizeof (on)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"setsockopt failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
goto free_and_fail;
}
#else
#ifndef SO_REUSEPORT
#ifdef LINUX
/* Supported since Linux 3.9, but often not present (or commented out)
in the headers at this time; but 15 is reserved for this and
thus should be safe to use. */
#define SO_REUSEPORT 15
#endif
#endif
#ifdef SO_REUSEPORT
if (0 > setsockopt (socket_fd,
SOL_SOCKET,
SO_REUSEPORT,
(void*)&on, sizeof (on)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"setsockopt failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
goto free_and_fail;
}
#else
/* we're supposed to allow address:port re-use, but
on this platform we cannot; fail hard */
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Cannot allow listening address reuse: SO_REUSEPORT not defined\n");
#endif
goto free_and_fail;
#endif
#endif
}
else /* if (daemon->listening_address_reuse < 0) */
{
/* User requested to disallow reusing listening address:port.
* Do nothing except for Windows where SO_EXCLUSIVEADDRUSE
* is used. Fail if it does not exist or setsockopt fails.
*/
#ifdef _WIN32
#ifdef SO_EXCLUSIVEADDRUSE
if (0 > setsockopt (socket_fd,
SOL_SOCKET,
SO_EXCLUSIVEADDRUSE,
(void*)&on, sizeof (on)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"setsockopt failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
goto free_and_fail;
}
#else /* SO_EXCLUSIVEADDRUSE not defined on W32? */
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Cannot disallow listening address reuse: SO_EXCLUSIVEADDRUSE not defined\n");
#endif
goto free_and_fail;
#endif
#endif /* _WIN32 */
}
/* check for user supplied sockaddr */
#if HAVE_INET6
if (0 != (flags & MHD_USE_IPv6))
addrlen = sizeof (struct sockaddr_in6);
else
#endif
addrlen = sizeof (struct sockaddr_in);
if (NULL == servaddr)
{
#if HAVE_INET6
if (0 != (flags & MHD_USE_IPv6))
{
memset (&servaddr6, 0, sizeof (struct sockaddr_in6));
servaddr6.sin6_family = AF_INET6;
servaddr6.sin6_port = htons (port);
#if HAVE_SOCKADDR_IN_SIN_LEN
servaddr6.sin6_len = sizeof (struct sockaddr_in6);
#endif
servaddr = (struct sockaddr *) &servaddr6;
}
else
#endif
{
memset (&servaddr4, 0, sizeof (struct sockaddr_in));
servaddr4.sin_family = AF_INET;
servaddr4.sin_port = htons (port);
#if HAVE_SOCKADDR_IN_SIN_LEN
servaddr4.sin_len = sizeof (struct sockaddr_in);
#endif
servaddr = (struct sockaddr *) &servaddr4;
}
}
daemon->socket_fd = socket_fd;
if (0 != (flags & MHD_USE_IPv6))
{
#ifdef IPPROTO_IPV6
#ifdef IPV6_V6ONLY
/* Note: "IPV6_V6ONLY" is declared by Windows Vista ff., see "IPPROTO_IPV6 Socket Options"
(http://msdn.microsoft.com/en-us/library/ms738574%28v=VS.85%29.aspx);
and may also be missing on older POSIX systems; good luck if you have any of those,
your IPv6 socket may then also bind against IPv4 anyway... */
#ifndef WINDOWS
const int
#else
const char
#endif
on = (MHD_USE_DUAL_STACK != (flags & MHD_USE_DUAL_STACK));
if (0 > setsockopt (socket_fd,
IPPROTO_IPV6, IPV6_V6ONLY,
&on, sizeof (on)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"setsockopt failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
}
#endif
#endif
}
if (-1 == bind (socket_fd, servaddr, addrlen))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to bind to port %u: %s\n",
(unsigned int) port,
MHD_socket_last_strerr_ ());
#endif
if (0 != MHD_socket_close_ (socket_fd))
MHD_PANIC ("close failed\n");
goto free_and_fail;
}
#ifdef TCP_FASTOPEN
if (0 != (flags & MHD_USE_TCP_FASTOPEN))
{
if (0 == daemon->fastopen_queue_size)
daemon->fastopen_queue_size = MHD_TCP_FASTOPEN_QUEUE_SIZE_DEFAULT;
if (0 != setsockopt (socket_fd,
IPPROTO_TCP, TCP_FASTOPEN,
&daemon->fastopen_queue_size,
sizeof (daemon->fastopen_queue_size)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"setsockopt failed: %s\n",
MHD_socket_last_strerr_ ());
#endif
}
}
#endif
#if EPOLL_SUPPORT
if (0 != (flags & MHD_USE_EPOLL_LINUX_ONLY))
{
int sk_flags = fcntl (socket_fd, F_GETFL);
if (0 != fcntl (socket_fd, F_SETFL, sk_flags | O_NONBLOCK))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to make listen socket non-blocking: %s\n",
MHD_socket_last_strerr_ ());
#endif
if (0 != MHD_socket_close_ (socket_fd))
MHD_PANIC ("close failed\n");
goto free_and_fail;
}
}
#endif
if (listen (socket_fd, 32) < 0)
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to listen for connections: %s\n",
MHD_socket_last_strerr_ ());
#endif
if (0 != MHD_socket_close_ (socket_fd))
MHD_PANIC ("close failed\n");
goto free_and_fail;
}
}
else
{
socket_fd = daemon->socket_fd;
}
#ifndef WINDOWS
if ( (socket_fd >= FD_SETSIZE) &&
(0 == (flags & (MHD_USE_POLL | MHD_USE_EPOLL_LINUX_ONLY)) ) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Socket descriptor larger than FD_SETSIZE: %d > %d\n",
socket_fd,
FD_SETSIZE);
#endif
if (0 != MHD_socket_close_ (socket_fd))
MHD_PANIC ("close failed\n");
goto free_and_fail;
}
#endif
#if EPOLL_SUPPORT
if ( (0 != (flags & MHD_USE_EPOLL_LINUX_ONLY)) &&
(0 == daemon->worker_pool_size) &&
(0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
{
if (0 != (flags & MHD_USE_THREAD_PER_CONNECTION))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_EPOLL_LINUX_ONLY is not supported.\n");
#endif
goto free_and_fail;
}
if (MHD_YES != setup_epoll_to_listen (daemon))
goto free_and_fail;
}
#else
if (0 != (flags & MHD_USE_EPOLL_LINUX_ONLY))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"epoll is not supported on this platform by this build.\n");
#endif
goto free_and_fail;
}
#endif
if (MHD_YES != MHD_mutex_create_ (&daemon->per_ip_connection_mutex))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD failed to initialize IP connection limit mutex\n");
#endif
if ( (MHD_INVALID_SOCKET != socket_fd) &&
(0 != MHD_socket_close_ (socket_fd)) )
MHD_PANIC ("close failed\n");
goto free_and_fail;
}
if (MHD_YES != MHD_mutex_create_ (&daemon->cleanup_connection_mutex))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD failed to initialize IP connection limit mutex\n");
#endif
(void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
if ( (MHD_INVALID_SOCKET != socket_fd) &&
(0 != MHD_socket_close_ (socket_fd)) )
MHD_PANIC ("close failed\n");
goto free_and_fail;
}
#if HTTPS_SUPPORT
/* initialize HTTPS daemon certificate aspects & send / recv functions */
if ((0 != (flags & MHD_USE_SSL)) && (0 != MHD_TLS_init (daemon)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to initialize TLS support\n");
#endif
if ( (MHD_INVALID_SOCKET != socket_fd) &&
(0 != MHD_socket_close_ (socket_fd)) )
MHD_PANIC ("close failed\n");
(void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
(void) MHD_mutex_destroy_ (&daemon->per_ip_connection_mutex);
goto free_and_fail;
}
#endif
if ( ( (0 != (flags & MHD_USE_THREAD_PER_CONNECTION)) ||
( (0 != (flags & MHD_USE_SELECT_INTERNALLY)) &&
(0 == daemon->worker_pool_size)) ) &&
(0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) &&
(0 != (res_thread_create =
create_thread (&daemon->pid, daemon, &MHD_select_thread, daemon))))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to create listen thread: %s\n",
MHD_strerror_ (res_thread_create));
#endif
(void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
(void) MHD_mutex_destroy_ (&daemon->per_ip_connection_mutex);
if ( (MHD_INVALID_SOCKET != socket_fd) &&
(0 != MHD_socket_close_ (socket_fd)) )
MHD_PANIC ("close failed\n");
goto free_and_fail;
}
if ( (daemon->worker_pool_size > 0) &&
(0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
{
#if !defined(WINDOWS) || defined(CYGWIN)
int sk_flags;
#else
unsigned long sk_flags;
#endif
/* Coarse-grained count of connections per thread (note error
* due to integer division). Also keep track of how many
* connections are leftover after an equal split. */
unsigned int conns_per_thread = daemon->connection_limit
/ daemon->worker_pool_size;
unsigned int leftover_conns = daemon->connection_limit
% daemon->worker_pool_size;
i = 0; /* we need this in case fcntl or malloc fails */
/* Accept must be non-blocking. Multiple children may wake up
* to handle a new connection, but only one will win the race.
* The others must immediately return. */
#if !defined(WINDOWS) || defined(CYGWIN)
sk_flags = fcntl (socket_fd, F_GETFL);
if (sk_flags < 0)
goto thread_failed;
if (0 != fcntl (socket_fd, F_SETFL, sk_flags | O_NONBLOCK))
goto thread_failed;
#else
sk_flags = 1;
if (SOCKET_ERROR == ioctlsocket (socket_fd, FIONBIO, &sk_flags))
goto thread_failed;
#endif /* WINDOWS && !CYGWIN */
/* Allocate memory for pooled objects */
daemon->worker_pool = malloc (sizeof (struct MHD_Daemon)
* daemon->worker_pool_size);
if (NULL == daemon->worker_pool)
goto thread_failed;
/* Start the workers in the pool */
for (i = 0; i < daemon->worker_pool_size; ++i)
{
/* Create copy of the Daemon object for each worker */
struct MHD_Daemon *d = &daemon->worker_pool[i];
memcpy (d, daemon, sizeof (struct MHD_Daemon));
/* Adjust pooling params for worker daemons; note that memcpy()
has already copied MHD_USE_SELECT_INTERNALLY thread model into
the worker threads. */
d->master = daemon;
d->worker_pool_size = 0;
d->worker_pool = NULL;
if ( (MHD_USE_SUSPEND_RESUME == (flags & MHD_USE_SUSPEND_RESUME)) &&
(0 != MHD_pipe_ (d->wpipe)) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to create worker control pipe: %s\n",
MHD_pipe_last_strerror_() );
#endif
goto thread_failed;
}
#ifndef WINDOWS
if ( (0 == (flags & (MHD_USE_POLL | MHD_USE_EPOLL_LINUX_ONLY))) &&
(MHD_USE_SUSPEND_RESUME == (flags & MHD_USE_SUSPEND_RESUME)) &&
(d->wpipe[0] >= FD_SETSIZE) )
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"file descriptor for worker control pipe exceeds maximum value\n");
#endif
if (0 != MHD_pipe_close_ (d->wpipe[0]))
MHD_PANIC ("close failed\n");
if (0 != MHD_pipe_close_ (d->wpipe[1]))
MHD_PANIC ("close failed\n");
goto thread_failed;
}
#endif
/* Divide available connections evenly amongst the threads.
* Thread indexes in [0, leftover_conns) each get one of the
* leftover connections. */
d->connection_limit = conns_per_thread;
if (i < leftover_conns)
++d->connection_limit;
#if EPOLL_SUPPORT
if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
(MHD_YES != setup_epoll_to_listen (d)) )
goto thread_failed;
#endif
/* Must init cleanup connection mutex for each worker */
if (MHD_YES != MHD_mutex_create_ (&d->cleanup_connection_mutex))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD failed to initialize cleanup connection mutex for thread worker %d\n", i);
#endif
goto thread_failed;
}
/* Spawn the worker thread */
if (0 != (res_thread_create =
create_thread (&d->pid, daemon, &MHD_select_thread, d)))
{
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to create pool thread: %s\n",
MHD_strerror_ (res_thread_create));
#endif
/* Free memory for this worker; cleanup below handles
* all previously-created workers. */
(void) MHD_mutex_destroy_ (&d->cleanup_connection_mutex);
goto thread_failed;
}
}
}
#if HTTPS_SUPPORT
/* API promises to never use the password after initialization,
so we additionally NULL it here to not deref a dangling pointer. */
daemon->https_key_password = NULL;
#endif /* HTTPS_SUPPORT */
return daemon;
thread_failed:
/* If no worker threads created, then shut down normally. Calling
MHD_stop_daemon (as we do below) doesn't work here since it
assumes a 0-sized thread pool means we had been in the default
MHD_USE_SELECT_INTERNALLY mode. */
if (0 == i)
{
if ( (MHD_INVALID_SOCKET != socket_fd) &&
(0 != MHD_socket_close_ (socket_fd)) )
MHD_PANIC ("close failed\n");
(void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
(void) MHD_mutex_destroy_ (&daemon->per_ip_connection_mutex);
if (NULL != daemon->worker_pool)
free (daemon->worker_pool);
goto free_and_fail;
}
/* Shutdown worker threads we've already created. Pretend
as though we had fully initialized our daemon, but
with a smaller number of threads than had been
requested. */
daemon->worker_pool_size = i;
MHD_stop_daemon (daemon);
return NULL;
free_and_fail:
/* clean up basic memory state in 'daemon' and return NULL to
indicate failure */
#if EPOLL_SUPPORT
if (-1 != daemon->epoll_fd)
close (daemon->epoll_fd);
#endif
#ifdef DAUTH_SUPPORT
free (daemon->nnc);
(void) MHD_mutex_destroy_ (&daemon->nnc_lock);
#endif
free (daemon);
return NULL;
}
/**
* Close the given connection, remove it from all of its
* DLLs and move it into the cleanup queue.
*
* @param pos connection to move to cleanup
*/
static void
close_connection (struct MHD_Connection *pos)
{
struct MHD_Daemon *daemon = pos->daemon;
MHD_connection_close (pos,
MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
return; /* must let thread to the rest */
if (pos->connection_timeout == pos->daemon->connection_timeout)
XDLL_remove (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
pos);
else
XDLL_remove (daemon->manual_timeout_head,
daemon->manual_timeout_tail,
pos);
DLL_remove (daemon->connections_head,
daemon->connections_tail,
pos);
pos->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
DLL_insert (daemon->cleanup_head,
daemon->cleanup_tail,
pos);
}
/**
* Close all connections for the daemon; must only be called after
* all of the threads have been joined and there is no more concurrent
* activity on the connection lists.
*
* @param daemon daemon to close down
*/
static void
close_all_connections (struct MHD_Daemon *daemon)
{
struct MHD_Connection *pos;
/* first, make sure all threads are aware of shutdown; need to
traverse DLLs in peace... */
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to acquire cleanup mutex\n");
for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
{
shutdown (pos->socket_fd,
(pos->read_closed == MHD_YES) ? SHUT_WR : SHUT_RDWR);
#if WINDOWS
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_INVALID_PIPE_ != daemon->wpipe[1]) &&
(1 != MHD_pipe_write_ (daemon->wpipe[1], "e", 1)) )
MHD_PANIC ("failed to signal shutdown via pipe");
#endif
}
if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
MHD_PANIC ("Failed to release cleanup mutex\n");
/* now, collect threads from thread pool */
if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
{
for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
{
if (0 != MHD_join_thread_ (pos->pid))
MHD_PANIC ("Failed to join a thread\n");
pos->thread_joined = MHD_YES;
}
}
/* now that we're alone, move everyone to cleanup */
while (NULL != (pos = daemon->connections_head))
close_connection (pos);
MHD_cleanup_connections (daemon);
}
#if EPOLL_SUPPORT
/**
* Shutdown epoll()-event loop by adding 'wpipe' to its event set.
*
* @param daemon daemon of which the epoll() instance must be signalled
*/
static void
epoll_shutdown (struct MHD_Daemon *daemon)
{
struct epoll_event event;
if (MHD_INVALID_PIPE_ == daemon->wpipe[1])
{
/* wpipe was required in this mode, how could this happen? */
MHD_PANIC ("Internal error\n");
}
event.events = EPOLLOUT;
event.data.ptr = NULL;
if (0 != epoll_ctl (daemon->epoll_fd,
EPOLL_CTL_ADD,
daemon->wpipe[1],
&event))
MHD_PANIC ("Failed to add wpipe to epoll set to signal termination\n");
}
#endif
/**
* Shutdown an HTTP daemon.
*
* @param daemon daemon to stop
* @ingroup event
*/
void
MHD_stop_daemon (struct MHD_Daemon *daemon)
{
MHD_socket fd;
unsigned int i;
if (NULL == daemon)
return;
daemon->shutdown = MHD_YES;
fd = daemon->socket_fd;
daemon->socket_fd = MHD_INVALID_SOCKET;
/* Prepare workers for shutdown */
if (NULL != daemon->worker_pool)
{
/* MHD_USE_NO_LISTEN_SOCKET disables thread pools, hence we need to check */
for (i = 0; i < daemon->worker_pool_size; ++i)
{
daemon->worker_pool[i].shutdown = MHD_YES;
daemon->worker_pool[i].socket_fd = MHD_INVALID_SOCKET;
#if EPOLL_SUPPORT
if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
(-1 != daemon->worker_pool[i].epoll_fd) &&
(MHD_INVALID_SOCKET == fd) )
epoll_shutdown (&daemon->worker_pool[i]);
#endif
}
}
if (MHD_INVALID_PIPE_ != daemon->wpipe[1])
{
if (1 != MHD_pipe_write_ (daemon->wpipe[1], "e", 1))
MHD_PANIC ("failed to signal shutdown via pipe");
}
#ifdef HAVE_LISTEN_SHUTDOWN
else
{
/* fd might be MHD_INVALID_SOCKET here due to 'MHD_quiesce_daemon' */
if (MHD_INVALID_SOCKET != fd)
(void) shutdown (fd, SHUT_RDWR);
}
#endif
#if EPOLL_SUPPORT
if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
(-1 != daemon->epoll_fd) &&
(MHD_INVALID_SOCKET == fd) )
epoll_shutdown (daemon);
#endif
#if DEBUG_CLOSE
#if HAVE_MESSAGES
MHD_DLOG (daemon,
"MHD listen socket shutdown\n");
#endif
#endif
/* Signal workers to stop and clean them up */
if (NULL != daemon->worker_pool)
{
/* MHD_USE_NO_LISTEN_SOCKET disables thread pools, hence we need to check */
for (i = 0; i < daemon->worker_pool_size; ++i)
{
if (MHD_INVALID_PIPE_ != daemon->worker_pool[i].wpipe[1])
{
if (1 != MHD_pipe_write_ (daemon->worker_pool[i].wpipe[1], "e", 1))
MHD_PANIC ("failed to signal shutdown via pipe");
}
if (0 != MHD_join_thread_ (daemon->worker_pool[i].pid))
MHD_PANIC ("Failed to join a thread\n");
close_all_connections (&daemon->worker_pool[i]);
(void) MHD_mutex_destroy_ (&daemon->worker_pool[i].cleanup_connection_mutex);
#if EPOLL_SUPPORT
if ( (-1 != daemon->worker_pool[i].epoll_fd) &&
(0 != MHD_socket_close_ (daemon->worker_pool[i].epoll_fd)) )
MHD_PANIC ("close failed\n");
#endif
if ( (MHD_USE_SUSPEND_RESUME == (daemon->options & MHD_USE_SUSPEND_RESUME)) )
{
if (MHD_INVALID_PIPE_ != daemon->worker_pool[i].wpipe[1])
{
if (0 != MHD_pipe_close_ (daemon->worker_pool[i].wpipe[0]))
MHD_PANIC ("close failed\n");
if (0 != MHD_pipe_close_ (daemon->worker_pool[i].wpipe[1]))
MHD_PANIC ("close failed\n");
}
}
}
free (daemon->worker_pool);
}
else
{
/* clean up master threads */
if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
((0 != (daemon->options & MHD_USE_SELECT_INTERNALLY))
&& (0 == daemon->worker_pool_size)))
{
if (0 != MHD_join_thread_ (daemon->pid))
{
MHD_PANIC ("Failed to join a thread\n");
}
}
}
close_all_connections (daemon);
if ( (MHD_INVALID_SOCKET != fd) &&
(0 != MHD_socket_close_ (fd)) )
MHD_PANIC ("close failed\n");
/* TLS clean up */
#if HTTPS_SUPPORT
if (0 != (daemon->options & MHD_USE_SSL))
{
MHD_TLS_deinit (daemon);
}
#endif
#if EPOLL_SUPPORT
if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
(-1 != daemon->epoll_fd) &&
(0 != MHD_socket_close_ (daemon->epoll_fd)) )
MHD_PANIC ("close failed\n");
#endif
#ifdef DAUTH_SUPPORT
free (daemon->nnc);
(void) MHD_mutex_destroy_ (&daemon->nnc_lock);
#endif
(void) MHD_mutex_destroy_ (&daemon->per_ip_connection_mutex);
(void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
if (MHD_INVALID_PIPE_ != daemon->wpipe[1])
{
if (0 != MHD_pipe_close_ (daemon->wpipe[0]))
MHD_PANIC ("close failed\n");
if (0 != MHD_pipe_close_ (daemon->wpipe[1]))
MHD_PANIC ("close failed\n");
}
free (daemon);
}
/**
* Obtain information about the given daemon
* (not fully implemented!).
*
* @param daemon what daemon to get information about
* @param info_type what information is desired?
* @param ... depends on @a info_type
* @return NULL if this information is not available
* (or if the @a info_type is unknown)
* @ingroup specialized
*/
const union MHD_DaemonInfo *
MHD_get_daemon_info (struct MHD_Daemon *daemon,
enum MHD_DaemonInfoType info_type,
...)
{
switch (info_type)
{
case MHD_DAEMON_INFO_KEY_SIZE:
return NULL; /* no longer supported */
case MHD_DAEMON_INFO_MAC_KEY_SIZE:
return NULL; /* no longer supported */
case MHD_DAEMON_INFO_LISTEN_FD:
return (const union MHD_DaemonInfo *) &daemon->socket_fd;
#if EPOLL_SUPPORT
case MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY:
return (const union MHD_DaemonInfo *) &daemon->epoll_fd;
#endif
case MHD_DAEMON_INFO_CURRENT_CONNECTIONS:
MHD_cleanup_connections (daemon);
if (daemon->worker_pool)
{
/* Collect the connection information stored in the workers. */
unsigned int i;
daemon->connections = 0;
for (i=0;i<daemon->worker_pool_size;i++)
{
MHD_cleanup_connections (&daemon->worker_pool[i]);
daemon->connections += daemon->worker_pool[i].connections;
}
}
return (const union MHD_DaemonInfo *) &daemon->connections;
default:
return NULL;
};
}
/**
* Sets the global error handler to a different implementation. @a cb
* will only be called in the case of typically fatal, serious
* internal consistency issues. These issues should only arise in the
* case of serious memory corruption or similar problems with the
* architecture. While @a cb is allowed to return and MHD will then
* try to continue, this is never safe.
*
* The default implementation that is used if no panic function is set
* simply prints an error message and calls `abort()`. Alternative
* implementations might call `exit()` or other similar functions.
*
* @param cb new error handler
* @param cls passed to @a cb
* @ingroup logging
*/
void
MHD_set_panic_func (MHD_PanicCallback cb, void *cls)
{
mhd_panic = cb;
mhd_panic_cls = cls;
}
/**
* Obtain the version of this library
*
* @return static version string, e.g. "0.9.9"
* @ingroup specialized
*/
const char *
MHD_get_version (void)
{
#ifdef PACKAGE_VERSION
return PACKAGE_VERSION;
#else /* !PACKAGE_VERSION */
static char ver[12] = "\0\0\0\0\0\0\0\0\0\0\0";
if (0 == ver[0])
{
int res = MHD_snprintf_(ver, sizeof(ver), "%x.%x.%x",
(((int)MHD_VERSION >> 24) & 0xFF),
(((int)MHD_VERSION >> 16) & 0xFF),
(((int)MHD_VERSION >> 8) & 0xFF));
if (0 >= res || sizeof(ver) <= res)
return "0.0.0"; /* Can't return real version*/
}
return ver;
#endif /* !PACKAGE_VERSION */
}
/**
* Get information about supported MHD features.
* Indicate that MHD was compiled with or without support for
* particular feature. Some features require additional support
* by kernel. Kernel support is not checked by this function.
*
* @param feature type of requested information
* @return #MHD_YES if feature is supported by MHD, #MHD_NO if
* feature is not supported or feature is unknown.
* @ingroup specialized
*/
_MHD_EXTERN int
MHD_is_feature_supported(enum MHD_FEATURE feature)
{
switch(feature)
{
case MHD_FEATURE_MESSGES:
#if HAVE_MESSAGES
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_SSL:
#if HTTPS_SUPPORT
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_HTTPS_CERT_CALLBACK:
return MHD_NO;
case MHD_FEATURE_IPv6:
#ifdef HAVE_INET6
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_IPv6_ONLY:
#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_POLL:
#ifdef HAVE_POLL
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_EPOLL:
#if EPOLL_SUPPORT
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_SHUTDOWN_LISTEN_SOCKET:
#ifdef HAVE_LISTEN_SHUTDOWN
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_SOCKETPAIR:
#ifdef MHD_DONT_USE_PIPES
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_TCP_FASTOPEN:
#ifdef TCP_FASTOPEN
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_BASIC_AUTH:
#if BAUTH_SUPPORT
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_DIGEST_AUTH:
#if DAUTH_SUPPORT
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_POSTPROCESSOR:
#if HAVE_POSTPROCESSOR
return MHD_YES;
#else
return MHD_NO;
#endif
case MHD_FEATURE_HTTPS_KEY_PASSWORD:
#if HTTPS_SUPPORT
return MHD_YES;
#else
return MHD_NO;
#endif
}
return MHD_NO;
}
/**
* Initialize do setup work.
*/
void MHD_init(void)
{
mhd_panic = &mhd_panic_std;
mhd_panic_cls = NULL;
#ifdef _WIN32
WSADATA wsd;
if (0 != WSAStartup(MAKEWORD(2, 2), &wsd))
MHD_PANIC ("Failed to initialize winsock\n");
mhd_winsock_inited_ = 1;
if (2 != LOBYTE(wsd.wVersion) && 2 != HIBYTE(wsd.wVersion))
MHD_PANIC ("Winsock version 2.2 is not available\n");
#endif
#if HTTPS_SUPPORT
SSL_library_init();
#endif
}
void MHD_fini(void)
{
#ifdef _WIN32
if (mhd_winsock_inited_)
WSACleanup();
#endif
}
_SET_INIT_AND_DEINIT_FUNCS(MHD_init, MHD_fini);
/* end of daemon.c */