libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 7512349b3bb7ec147195cf25864f637e1a99c569
parent e77fce2749a06b448e496140e8ad7e51891972de
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri,  9 Feb 2018 06:09:02 +0100

allow passing pf instead of just v6 flag to listen socket creation

Diffstat:
Am4/ac_define_dir.m4 | 35+++++++++++++++++++++++++++++++++++
Asrc/lib/Makefile.am | 167+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/base64.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/base64.h | 17+++++++++++++++++
Asrc/lib/internal.c | 281+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/md5.c | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/md5.h | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/memorypool.c | 340+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/memorypool.h | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_assert.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_byteorder.h | 160+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_compat.c | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_compat.h | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_itc.c | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_itc.h | 361+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_itc_types.h | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_limits.h | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_locks.h | 183+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_mono_clock.c | 377+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_mono_clock.h | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Csrc/microhttpd/mhd_sockets.c -> src/lib/mhd_sockets.c | 0
Asrc/lib/mhd_sockets.h | 760+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_str.c | 758+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_str.h | 266+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_threads.c | 363+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/mhd_threads.h | 229+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/sysfdsetsize.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/sysfdsetsize.h | 36++++++++++++++++++++++++++++++++++++
Asrc/lib/tsearch.c | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/tsearch.h | 38++++++++++++++++++++++++++++++++++++++
Msrc/microhttpd/daemon.c | 12+++++++++++-
Msrc/microhttpd/mhd_sockets.c | 19+++++--------------
32 files changed, 5730 insertions(+), 15 deletions(-)

diff --git a/m4/ac_define_dir.m4 b/m4/ac_define_dir.m4 @@ -0,0 +1,35 @@ +dnl @synopsis AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION]) +dnl +dnl This macro _AC_DEFINEs VARNAME to the expansion of the DIR +dnl variable, taking care of fixing up ${prefix} and such. +dnl +dnl VARNAME is offered as both a C preprocessor symbol, and an output +dnl variable. +dnl +dnl Note that the 3 argument form is only supported with autoconf 2.13 +dnl and later (i.e. only where _AC_DEFINE supports 3 arguments). +dnl +dnl Examples: +dnl +dnl AC_DEFINE_DIR(DATADIR, datadir) +dnl AC_DEFINE_DIR(PROG_PATH, bindir, [Location of installed binaries]) +dnl +dnl @category Misc +dnl @author Stepan Kasal <kasal@ucw.cz> +dnl @author Andreas Schwab <schwab@suse.de> +dnl @author Guido Draheim <guidod@gmx.de> +dnl @author Alexandre Oliva +dnl @version 2005-01-17 +dnl @license AllPermissive + +AC_DEFUN([AC_DEFINE_DIR], [ + prefix_NONE= + exec_prefix_NONE= + test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix + test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix + eval ac_define_dir="\"[$]$2\"" + AC_SUBST($1, "$ac_define_dir") + AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3]) + test "$prefix_NONE" && prefix=NONE + test "$exec_prefix_NONE" && exec_prefix=NONE +]) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -0,0 +1,167 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/include \ + -I$(top_srcdir)/src/lib + +AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS) + +lib_LTLIBRARIES = \ + libmicrohttpd.la + +noinst_DATA = +MOSTLYCLEANFILES = + +if W32_SHARED_LIB_EXP +W32_MHD_LIB_LDFLAGS = -Wl,--output-def,$(lt_cv_objdir)/libmicrohttpd.def -XCClinker -static-libgcc +noinst_DATA += $(lt_cv_objdir)/libmicrohttpd.lib $(lt_cv_objdir)/libmicrohttpd.def $(lt_cv_objdir)/libmicrohttpd.exp +MOSTLYCLEANFILES += $(lt_cv_objdir)/libmicrohttpd.lib $(lt_cv_objdir)/libmicrohttpd.def $(lt_cv_objdir)/libmicrohttpd.exp + +$(lt_cv_objdir)/libmicrohttpd.def: libmicrohttpd.la + +$(lt_cv_objdir)/libmicrohttpd.exp: $(lt_cv_objdir)/libmicrohttpd.lib + +$(lt_cv_objdir)/libmicrohttpd.lib: $(lt_cv_objdir)/libmicrohttpd.def libmicrohttpd.la $(libmicrohttpd_la_OBJECTS) +if USE_MS_LIB_TOOL + @echo Creating $@ and libmicrohttpd.exp by $(MS_LIB_TOOL)... && \ + dll_name=`$(EGREP) -o dlname=\'.+\' libmicrohttpd.la` && \ + dll_name=$${dll_name#*\'} && dll_name=$${dll_name%\'} && test -n "$$dll_name" && \ + echo Creating $$dll_name by $(MS_LIB_TOOL).. && cd "$(lt_cv_objdir)" && \ + $(MS_LIB_TOOL) -def:libmicrohttpd.def -name:$$dll_name -out:libmicrohttpd.lib $(libmicrohttpd_la_OBJECTS:.lo=.o) && cd .. +else + @echo Creating $@ and libmicrohttpd.exp by $(DLLTOOL)... && \ + dll_name=`$(EGREP) -o dlname=\'.+\' libmicrohttpd.la` && \ + dll_name=$${dll_name#*\'} && dll_name=$${dll_name%\'} && test -n "$$dll_name" && \ + echo Creating $$dll_name by $(DLLTOOL).. && cd "$(lt_cv_objdir)" && \ + $(DLLTOOL) -d ./libmicrohttpd.def -D $$dll_name -l libmicrohttpd.lib $(libmicrohttpd_la_OBJECTS:.lo=.o) -e ./libmicrohttpd.exp && cd .. &&\ + echo Created libmicrohttpd.exp and libmicrohttpd.lib. +endif +else + W32_MHD_LIB_LDFLAGS = +endif + +if W32_STATIC_LIB +noinst_DATA += $(lt_cv_objdir)/libmicrohttpd-static.lib +MOSTLYCLEANFILES += $(lt_cv_objdir)/libmicrohttpd-static.lib + +$(lt_cv_objdir)/libmicrohttpd-static.lib: libmicrohttpd.la $(libmicrohttpd_la_OBJECTS) +if USE_MS_LIB_TOOL + $(MS_LIB_TOOL) -out:$@ $(libmicrohttpd_la_OBJECTS:.lo=.o) +else + cp $(lt_cv_objdir)/libmicrohttpd.a $@ +endif +endif + + +libmicrohttpd_la_SOURCES = \ + action_continue.c \ + action_from_response.c \ + action_parse_post.c \ + action_process_upload.c \ + action_suspend.c \ + connection_info.c \ + connection_options.c \ + daemon_create.c \ + daemon_destroy.c \ + daemon_info.c \ + daemon_options.c \ + daemon_start.c \ + daemon_quiesce.c \ + init.c init.h \ + internal.c internal.h \ + memorypool.c memorypool.h \ + mhd_assert.h \ + mhd_byteorder.h \ + mhd_compat.c mhd_compat.h \ + mhd_itc.c mhd_itc.h mhd_itc_types.h \ + mhd_limits.h \ + mhd_locks.h \ + mhd_mono_clock.c mhd_mono_clock.h \ + mhd_str.c mhd_str.h \ + mhd_sockets.c mhd_sockets.h \ + mhd_threads.c mhd_threads.h \ + response.c \ + response_for_upgrade.c \ + response_from_buffer.c \ + response_from_callback.c \ + response_from_fd.c \ + response_options.c \ + reason_phrase.c \ + request.c \ + request_info.c \ + request_resume.c \ + sysfdsetsize.c sysfdsetsize.h \ + panic.c \ + version.c + + + +libmicrohttpd_la_CPPFLAGS = \ + $(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) \ + -DBUILDING_MHD_LIB=1 +libmicrohttpd_la_CFLAGS = \ + $(AM_CFLAGS) $(MHD_LIB_CFLAGS) +libmicrohttpd_la_LDFLAGS = \ + $(MHD_LIB_LDFLAGS) \ + $(W32_MHD_LIB_LDFLAGS) \ + -version-info @LIB_VERSION_CURRENT@:@LIB_VERSION_REVISION@:@LIB_VERSION_AGE@ +libmicrohttpd_la_LIBADD = \ + $(MHD_LIBDEPS) + +if HAVE_W32 +MHD_DLL_RES_SRC = microhttpd_dll_res.rc +MHD_DLL_RES_LO = libmicrohttpd_la-$(MHD_DLL_RES_SRC:.rc=.lo) + +EXTRA_libmicrohttpd_la_DEPENDENCIES = $(MHD_DLL_RES_LO) +libmicrohttpd_la_LIBADD += $(MHD_DLL_RES_LO) + +# General rule is not required, but keep it just in case +.rc.lo: + $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $< -o $@ + +# To add dll resource only to .dll file and exclude it form static +# lib, a little trick was used. Allow libtool to create file.lo, +# file.o and .libs/file.lo, .libs/file.o files, then overwrite file.o +# by empty object generated from empty c-file. Later libtool will +# use .libs/file.o for shared lib and empty file.o for static lib. +# This implementation is based on trick found in liblzma. +# Note: windres does not understand '-isystem' flag, so all +# possible '-isystem' flags are replaced by simple '-I' flags. +$(MHD_DLL_RES_LO): $(MHD_DLL_RES_SRC) + RC_CPP_FLAGS=" $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) " && \ + $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $${RC_CPP_FLAGS// -isystem / -I } $< -o $@ && \ + echo > $@-empty.c && $(CC) $(AM_CFLAGS) $(CFLAGS) -c $@-empty.c -o $(@:.lo=.o) && rm -f $@-empty.c +endif + +if USE_COVERAGE + AM_CFLAGS += --coverage +endif + +if !MHD_HAVE_TSEARCH +libmicrohttpd_la_SOURCES += \ + tsearch.c tsearch.h +endif + +# TBD! +if HAVE_POSTPROCESSOR +libmicrohttpd_la_SOURCES += \ + postprocessor.c +endif + +# TBD! +if ENABLE_DAUTH +libmicrohttpd_la_SOURCES += \ + digestauth.c \ + md5.c md5.h +endif + +# TBD! +if ENABLE_BAUTH +libmicrohttpd_la_SOURCES += \ + basicauth.c \ + base64.c base64.h +endif + + + + + diff --git a/src/lib/base64.c b/src/lib/base64.c @@ -0,0 +1,59 @@ +/* + * This code implements the BASE64 algorithm. + * This code is in the public domain; do with it what you wish. + * + * @file base64.c + * @brief This code implements the BASE64 algorithm + * @author Matthieu Speder + */ +#include "base64.h" + +static const char base64_digits[] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 0, 0, 0, -1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + +char * +BASE64Decode(const char* src) +{ + size_t in_len = strlen (src); + char* dest; + char* result; + + if (in_len % 4) + { + /* Wrong base64 string length */ + return NULL; + } + result = dest = malloc(in_len / 4 * 3 + 1); + if (NULL == result) + return NULL; /* out of memory */ + while (*src) { + char a = base64_digits[(unsigned char)*(src++)]; + char b = base64_digits[(unsigned char)*(src++)]; + char c = base64_digits[(unsigned char)*(src++)]; + char d = base64_digits[(unsigned char)*(src++)]; + *(dest++) = (a << 2) | ((b & 0x30) >> 4); + if (c == (char)-1) + break; + *(dest++) = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == (char)-1) + break; + *(dest++) = ((c & 0x03) << 6) | d; + } + *dest = 0; + return result; +} + + +/* end of base64.c */ diff --git a/src/lib/base64.h b/src/lib/base64.h @@ -0,0 +1,17 @@ +/* + * This code implements the BASE64 algorithm. + * This code is in the public domain; do with it what you wish. + * + * @file base64.c + * @brief This code implements the BASE64 algorithm + * @author Matthieu Speder + */ +#ifndef BASE64_H +#define BASE64_H + +#include "platform.h" + +char * +BASE64Decode(const char* src); + +#endif /* !BASE64_H */ diff --git a/src/lib/internal.c b/src/lib/internal.c @@ -0,0 +1,281 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2007 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/internal.c + * @brief internal shared structures + * @author Daniel Pittman + * @author Christian Grothoff + */ + +#include "internal.h" +#include "mhd_str.h" + +#ifdef HAVE_MESSAGES +#if DEBUG_STATES +/** + * State to string dictionary. + */ +const char * +MHD_state_to_string (enum MHD_CONNECTION_STATE state) +{ + switch (state) + { + case MHD_CONNECTION_INIT: + return "connection init"; + case MHD_CONNECTION_URL_RECEIVED: + return "connection url received"; + case MHD_CONNECTION_HEADER_PART_RECEIVED: + return "header partially received"; + case MHD_CONNECTION_HEADERS_RECEIVED: + return "headers received"; + case MHD_CONNECTION_HEADERS_PROCESSED: + return "headers processed"; + case MHD_CONNECTION_CONTINUE_SENDING: + return "continue sending"; + case MHD_CONNECTION_CONTINUE_SENT: + return "continue sent"; + case MHD_CONNECTION_BODY_RECEIVED: + return "body received"; + case MHD_CONNECTION_FOOTER_PART_RECEIVED: + return "footer partially received"; + case MHD_CONNECTION_FOOTERS_RECEIVED: + return "footers received"; + case MHD_CONNECTION_HEADERS_SENDING: + return "headers sending"; + case MHD_CONNECTION_HEADERS_SENT: + return "headers sent"; + case MHD_CONNECTION_NORMAL_BODY_READY: + return "normal body ready"; + case MHD_CONNECTION_NORMAL_BODY_UNREADY: + return "normal body unready"; + case MHD_CONNECTION_CHUNKED_BODY_READY: + return "chunked body ready"; + case MHD_CONNECTION_CHUNKED_BODY_UNREADY: + return "chunked body unready"; + case MHD_CONNECTION_BODY_SENT: + return "body sent"; + case MHD_CONNECTION_FOOTERS_SENDING: + return "footers sending"; + case MHD_CONNECTION_FOOTERS_SENT: + return "footers sent"; + case MHD_CONNECTION_CLOSED: + return "closed"; + default: + return "unrecognized connection state"; + } +} +#endif +#endif + + +#ifdef HAVE_MESSAGES +/** + * fprintf-like helper function for logging debug + * messages. + */ +void +MHD_DLOG (const struct MHD_Daemon *daemon, + const char *format, + ...) +{ + va_list va; + + if (0 == (daemon->options & MHD_USE_ERROR_LOG)) + return; + va_start (va, format); + daemon->custom_error_log (daemon->custom_error_log_cls, + format, + va); + va_end (va); +} +#endif + + +/** + * Convert all occurrences of '+' to ' '. + * + * @param arg string that is modified (in place), must be 0-terminated + */ +void +MHD_unescape_plus (char *arg) +{ + char *p; + + for (p=strchr (arg, '+'); NULL != p; p = strchr (p + 1, '+')) + *p = ' '; +} + + +/** + * 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 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) + */ +size_t +MHD_http_unescape (char *val) +{ + char *rpos = val; + char *wpos = val; + + while ('\0' != *rpos) + { + uint32_t num; + switch (*rpos) + { + case '%': + if (2 == MHD_strx_to_uint32_n_ (rpos + 1, + 2, + &num)) + { + *wpos = (char)((unsigned char) num); + wpos++; + rpos += 3; + break; + } + /* TODO: add bad sequence handling */ + /* intentional fall through! */ + default: + *wpos = *rpos; + wpos++; + rpos++; + } + } + *wpos = '\0'; /* add 0-terminator */ + return wpos - val; /* = strlen(val) */ +} + + +/** + * Parse and unescape the arguments given by the client + * as part of the HTTP request URI. + * + * @param kind header kind to pass to @a cb + * @param connection connection to add headers to + * @param[in,out] args argument URI string (after "?" in URI), + * clobbered in the process! + * @param cb function to call on each key-value pair found + * @param[out] num_headers set to the number of headers found + * @return #MHD_NO on failure (@a cb returned #MHD_NO), + * #MHD_YES for success (parsing succeeded, @a cb always + * returned #MHD_YES) + */ +int +MHD_parse_arguments_ (struct MHD_Connection *connection, + enum MHD_ValueKind kind, + char *args, + MHD_ArgumentIterator_ cb, + unsigned int *num_headers) +{ + struct MHD_Daemon *daemon = connection->daemon; + char *equals; + char *amper; + + *num_headers = 0; + while ( (NULL != args) && + ('\0' != args[0]) ) + { + equals = strchr (args, '='); + amper = strchr (args, '&'); + if (NULL == amper) + { + /* last argument */ + if (NULL == equals) + { + /* last argument, without '=' */ + MHD_unescape_plus (args); + daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + args); + if (MHD_YES != cb (connection, + args, + NULL, + kind)) + return MHD_NO; + (*num_headers)++; + break; + } + /* got 'foo=bar' */ + equals[0] = '\0'; + equals++; + MHD_unescape_plus (args); + daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + args); + MHD_unescape_plus (equals); + daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + equals); + if (MHD_YES != cb (connection, + args, + equals, + kind)) + return MHD_NO; + (*num_headers)++; + break; + } + /* amper is non-NULL here */ + amper[0] = '\0'; + amper++; + if ( (NULL == equals) || + (equals >= amper) ) + { + /* got 'foo&bar' or 'foo&bar=val', add key 'foo' with NULL for value */ + MHD_unescape_plus (args); + daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + args); + if (MHD_YES != cb (connection, + args, + NULL, + kind)) + return MHD_NO; + /* continue with 'bar' */ + (*num_headers)++; + args = amper; + continue; + } + /* equals and amper are non-NULL here, and equals < amper, + so we got regular 'foo=value&bar...'-kind of argument */ + equals[0] = '\0'; + equals++; + MHD_unescape_plus (args); + daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + args); + MHD_unescape_plus (equals); + daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + equals); + if (MHD_YES != cb (connection, + args, + equals, + kind)) + return MHD_NO; + (*num_headers)++; + args = amper; + } + return MHD_YES; +} + +/* end of internal.c */ diff --git a/src/lib/md5.c b/src/lib/md5.c @@ -0,0 +1,264 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* Based on OpenBSD modifications */ + +#include "md5.h" +#include "mhd_byteorder.h" + +#define PUT_64BIT_LE(cp, value) do { \ + (cp)[7] = (uint8_t)((value) >> 56); \ + (cp)[6] = (uint8_t)((value) >> 48); \ + (cp)[5] = (uint8_t)((value) >> 40); \ + (cp)[4] = (uint8_t)((value) >> 32); \ + (cp)[3] = (uint8_t)((value) >> 24); \ + (cp)[2] = (uint8_t)((value) >> 16); \ + (cp)[1] = (uint8_t)((value) >> 8); \ + (cp)[0] = (uint8_t)((value)); } while (0) + +#define PUT_32BIT_LE(cp, value) do { \ + (cp)[3] = (uint8_t)((value) >> 24); \ + (cp)[2] = (uint8_t)((value) >> 16); \ + (cp)[1] = (uint8_t)((value) >> 8); \ + (cp)[0] = (uint8_t)((value)); } while (0) + +static uint8_t PADDING[MD5_BLOCK_SIZE] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + if (!ctx) + return; + + ctx->count = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, const unsigned char *input, size_t len) +{ + size_t have, need; + + if (!ctx || !input) + return; + + /* Check how many bytes we already have and how many more we need. */ + have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_SIZE - 1)); + need = MD5_BLOCK_SIZE - have; + + /* Update bitcount */ + ctx->count += (uint64_t)len << 3; + + if (len >= need) + { + if (have != 0) + { + memcpy(ctx->buffer + have, input, need); + MD5Transform(ctx->state, ctx->buffer); + input += need; + len -= need; + have = 0; + } + + /* Process data in MD5_BLOCK_SIZE-byte chunks. */ + while (len >= MD5_BLOCK_SIZE) + { + MD5Transform(ctx->state, input); + input += MD5_BLOCK_SIZE; + len -= MD5_BLOCK_SIZE; + } + } + + /* Handle any remaining bytes of data. */ + if (len != 0) + memcpy(ctx->buffer + have, input, len); +} + +/* + * Pad pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Pad(struct MD5Context *ctx) +{ + uint8_t count[8]; + size_t padlen; + + if (!ctx) + return; + + /* Convert count to 8 bytes in little endian order. */ + PUT_64BIT_LE(count, ctx->count); + + /* Pad out to 56 mod 64. */ + padlen = MD5_BLOCK_SIZE - + ((ctx->count >> 3) & (MD5_BLOCK_SIZE - 1)); + if (padlen < 1 + 8) + padlen += MD5_BLOCK_SIZE; + MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ + MD5Update(ctx, count, 8); +} + +/* + * Final wrapup--call MD5Pad, fill in digest and zero out ctx. + */ +void +MD5Final(unsigned char digest[MD5_DIGEST_SIZE], struct MD5Context *ctx) +{ + int i; + + if (!ctx || !digest) + return; + + MD5Pad(ctx); + for (i = 0; i < 4; i++) + PUT_32BIT_LE(digest + i * 4, ctx->state[i]); + + memset(ctx, 0, sizeof(*ctx)); +} + + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(uint32_t state[4], const uint8_t block[MD5_BLOCK_SIZE]) +{ + uint32_t a, b, c, d, in[MD5_BLOCK_SIZE / 4]; + +#if _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN + memcpy(in, block, sizeof(in)); +#else + for (a = 0; a < MD5_BLOCK_SIZE / 4; a++) + { + in[a] = (uint32_t)( + (uint32_t)(block[a * 4 + 0]) | + (uint32_t)(block[a * 4 + 1]) << 8 | + (uint32_t)(block[a * 4 + 2]) << 16 | + (uint32_t)(block[a * 4 + 3]) << 24); + } +#endif + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +/* end of md5.c */ diff --git a/src/lib/md5.h b/src/lib/md5.h @@ -0,0 +1,64 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef MHD_MD5_H +#define MHD_MD5_H + +#include "platform.h" + +#define MD5_BLOCK_SIZE 64 +#define MD5_DIGEST_SIZE 16 +#define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_SIZE * 2 + 1) + +struct MD5Context +{ + uint32_t state[4]; /* state */ + uint64_t count; /* number of bits, mod 2^64 */ + uint8_t buffer[MD5_BLOCK_SIZE]; /* input buffer */ +}; + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx); + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, const unsigned char *input, size_t len); + +/* + * Pad pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Pad(struct MD5Context *ctx); + +/* + * Final wrapup--call MD5Pad, fill in digest and zero out ctx. + */ +void MD5Final(unsigned char digest[MD5_DIGEST_SIZE], struct MD5Context *ctx); + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(uint32_t state[4], const uint8_t block[MD5_BLOCK_SIZE]); + +#endif /* !MHD_MD5_H */ diff --git a/src/lib/memorypool.c b/src/lib/memorypool.c @@ -0,0 +1,340 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2007, 2009, 2010 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 memorypool.c + * @brief memory pool + * @author Christian Grothoff + */ +#include "memorypool.h" + +/* define MAP_ANONYMOUS for Mac OS X */ +#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) +#define MAP_ANONYMOUS MAP_ANON +#endif +#ifndef MAP_FAILED +#define MAP_FAILED ((void*)-1) +#endif + +/** + * Align to 2x word size (as GNU libc does). + */ +#define ALIGN_SIZE (2 * sizeof(void*)) + +/** + * Round up 'n' to a multiple of ALIGN_SIZE. + */ +#define ROUND_TO_ALIGN(n) ((n+(ALIGN_SIZE-1)) & (~(ALIGN_SIZE-1))) + + +/** + * Handle for a memory pool. Pools are not reentrant and must not be + * used by multiple threads. + */ +struct MemoryPool +{ + + /** + * Pointer to the pool's memory + */ + char *memory; + + /** + * Size of the pool. + */ + size_t size; + + /** + * Offset of the first unallocated byte. + */ + size_t pos; + + /** + * Offset of the last unallocated byte. + */ + size_t end; + + /** + * #MHD_NO if pool was malloc'ed, #MHD_YES if mmapped (VirtualAlloc'ed for W32). + */ + int is_mmap; +}; + + +/** + * Free the memory given by @a ptr. Calls "free(ptr)". This function + * should be used to free the username returned by + * #MHD_digest_auth_get_username(). + * @note Since v0.9.56 + * + * @param ptr pointer to free. + */ +_MHD_EXTERN void +MHD_free (void *ptr) +{ + free (ptr); +} + + +/** + * Create a memory pool. + * + * @param max maximum size of the pool + * @return NULL on error + */ +struct MemoryPool * +MHD_pool_create (size_t max) +{ + struct MemoryPool *pool; + + pool = malloc (sizeof (struct MemoryPool)); + if (NULL == pool) + return NULL; +#if defined(MAP_ANONYMOUS) || defined(_WIN32) + if (max <= 32 * 1024) + pool->memory = MAP_FAILED; + else +#if defined(MAP_ANONYMOUS) && !defined(_WIN32) + pool->memory = mmap (NULL, + max, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0); +#elif defined(_WIN32) + pool->memory = VirtualAlloc (NULL, + max, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); +#endif +#else + pool->memory = MAP_FAILED; +#endif + if ( (MAP_FAILED == pool->memory) || + (NULL == pool->memory)) + { + pool->memory = malloc (max); + if (NULL == pool->memory) + { + free (pool); + return NULL; + } + pool->is_mmap = MHD_NO; + } + else + { + pool->is_mmap = MHD_YES; + } + pool->pos = 0; + pool->end = max; + pool->size = max; + return pool; +} + + +/** + * Destroy a memory pool. + * + * @param pool memory pool to destroy + */ +void +MHD_pool_destroy (struct MemoryPool *pool) +{ + if (NULL == pool) + return; + if (MHD_NO == pool->is_mmap) + free (pool->memory); + else +#if defined(MAP_ANONYMOUS) && !defined(_WIN32) + munmap (pool->memory, + pool->size); +#elif defined(_WIN32) + VirtualFree (pool->memory, + 0, + MEM_RELEASE); +#else + abort (); +#endif + free (pool); +} + + +/** + * Check how much memory is left in the @a pool + * + * @param pool pool to check + * @return number of bytes still available in @a pool + */ +size_t +MHD_pool_get_free (struct MemoryPool *pool) +{ + return (pool->end - pool->pos); +} + + +/** + * Allocate size bytes from the pool. + * + * @param pool memory pool to use for the operation + * @param size number of bytes to allocate + * @param from_end allocate from end of pool (set to #MHD_YES); + * use this for small, persistent allocations that + * will never be reallocated + * @return NULL if the pool cannot support size more + * bytes + */ +void * +MHD_pool_allocate (struct MemoryPool *pool, + size_t size, + int from_end) +{ + void *ret; + size_t asize; + + asize = ROUND_TO_ALIGN (size); + if ( (0 == asize) && (0 != size) ) + return NULL; /* size too close to SIZE_MAX */ + if ( (pool->pos + asize > pool->end) || + (pool->pos + asize < pool->pos)) + return NULL; + if (from_end == MHD_YES) + { + ret = &pool->memory[pool->end - asize]; + pool->end -= asize; + } + else + { + ret = &pool->memory[pool->pos]; + pool->pos += asize; + } + return ret; +} + + +/** + * Reallocate a block of memory obtained from the pool. + * This is particularly efficient when growing or + * shrinking the block that was last (re)allocated. + * If the given block is not the most recently + * (re)allocated block, the memory of the previous + * allocation may be leaked until the pool is + * destroyed (and copying the data maybe required). + * + * @param pool memory pool to use for the operation + * @param old the existing block + * @param old_size the size of the existing block + * @param new_size the new size of the block + * @return new address of the block, or + * NULL if the pool cannot support @a new_size + * bytes (old continues to be valid for @a old_size) + */ +void * +MHD_pool_reallocate (struct MemoryPool *pool, + void *old, + size_t old_size, + size_t new_size) +{ + void *ret; + size_t asize; + + asize = ROUND_TO_ALIGN (new_size); + if ( (0 == asize) && + (0 != new_size) ) + return NULL; /* new_size too close to SIZE_MAX */ + if ( (pool->end < old_size) || + (pool->end < asize) ) + return NULL; /* unsatisfiable or bogus request */ + + if ( (pool->pos >= old_size) && + (&pool->memory[pool->pos - old_size] == old) ) + { + /* was the previous allocation - optimize! */ + if (pool->pos + asize - old_size <= pool->end) + { + /* fits */ + pool->pos += asize - old_size; + if (asize < old_size) /* shrinking - zero again! */ + memset (&pool->memory[pool->pos], + 0, + old_size - asize); + return old; + } + /* does not fit */ + return NULL; + } + if (asize <= old_size) + return old; /* cannot shrink, no need to move */ + if ((pool->pos + asize >= pool->pos) && + (pool->pos + asize <= pool->end)) + { + /* fits */ + ret = &pool->memory[pool->pos]; + if (0 != old_size) + memmove (ret, + old, + old_size); + pool->pos += asize; + return ret; + } + /* does not fit */ + return NULL; +} + + +/** + * Clear all entries from the memory pool except + * for @a keep of the given @a size. The pointer + * returned should be a buffer of @a new_size where + * the first @a copy_bytes are from @a keep. + * + * @param pool memory pool to use for the operation + * @param keep pointer to the entry to keep (maybe NULL) + * @param copy_bytes how many bytes need to be kept at this address + * @param new_size how many bytes should the allocation we return have? + * (should be larger or equal to @a copy_bytes) + * @return addr new address of @a keep (if it had to change) + */ +void * +MHD_pool_reset (struct MemoryPool *pool, + void *keep, + size_t copy_bytes, + size_t new_size) +{ + if ( (NULL != keep) && + (keep != pool->memory) ) + { + if (0 != copy_bytes) + memmove (pool->memory, + keep, + copy_bytes); + keep = pool->memory; + } + pool->end = pool->size; + /* technically not needed, but safer to zero out */ + if (pool->size > copy_bytes) + memset (&pool->memory[copy_bytes], + 0, + pool->size - copy_bytes); + if (NULL != keep) + pool->pos = ROUND_TO_ALIGN (new_size); + return keep; +} + + +/* end of memorypool.c */ diff --git a/src/lib/memorypool.h b/src/lib/memorypool.h @@ -0,0 +1,130 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2007, 2009 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 memorypool.h + * @brief memory pool; mostly used for efficient (de)allocation + * for each connection and bounding memory use for each + * request + * @author Christian Grothoff + */ + +#ifndef MEMORYPOOL_H +#define MEMORYPOOL_H + +#include "internal.h" + +/** + * Opaque handle for a memory pool. + * Pools are not reentrant and must not be used + * by multiple threads. + */ +struct MemoryPool; + + +/** + * Create a memory pool. + * + * @param max maximum size of the pool + * @return NULL on error + */ +struct MemoryPool * +MHD_pool_create (size_t max); + + +/** + * Destroy a memory pool. + * + * @param pool memory pool to destroy + */ +void +MHD_pool_destroy (struct MemoryPool *pool); + + +/** + * Allocate size bytes from the pool. + * + * @param pool memory pool to use for the operation + * @param size number of bytes to allocate + * @param from_end allocate from end of pool (set to #MHD_YES); + * use this for small, persistent allocations that + * will never be reallocated + * @return NULL if the pool cannot support size more + * bytes + */ +void * +MHD_pool_allocate (struct MemoryPool *pool, + size_t size, + int from_end); + + +/** + * Reallocate a block of memory obtained from the pool. + * This is particularly efficient when growing or + * shrinking the block that was last (re)allocated. + * If the given block is not the most recently + * (re)allocated block, the memory of the previous + * allocation may be leaked until the pool is + * destroyed (and copying the data maybe required). + * + * @param pool memory pool to use for the operation + * @param old the existing block + * @param old_size the size of the existing block + * @param new_size the new size of the block + * @return new address of the block, or + * NULL if the pool cannot support new_size + * bytes (old continues to be valid for old_size) + */ +void * +MHD_pool_reallocate (struct MemoryPool *pool, + void *old, + size_t old_size, + size_t new_size); + + +/** + * Check how much memory is left in the @a pool + * + * @param pool pool to check + * @return number of bytes still available in @a pool + */ +size_t +MHD_pool_get_free (struct MemoryPool *pool); + + +/** + * Clear all entries from the memory pool except + * for @a keep of the given @a copy_bytes. The pointer + * returned should be a buffer of @a new_size where + * the first @a copy_bytes are from @a keep. + * + * @param pool memory pool to use for the operation + * @param keep pointer to the entry to keep (maybe NULL) + * @param copy_bytes how many bytes need to be kept at this address + * @param new_size how many bytes should the allocation we return have? + * (should be larger or equal to @a copy_bytes) + * @return addr new address of @a keep (if it had to change) + */ +void * +MHD_pool_reset (struct MemoryPool *pool, + void *keep, + size_t copy_bytes, + size_t new_size); + +#endif diff --git a/src/lib/mhd_assert.h b/src/lib/mhd_assert.h @@ -0,0 +1,49 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2017 Karlson2k (Evgeny Grin) + + 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, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file microhttpd/mhd_assert.h + * @brief macros for mhd_assert() + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_ASSERT_H +#define MHD_ASSERT_H 1 + +#include "mhd_options.h" +#ifdef NDEBUG +# define mhd_assert(ignore) ((void)0) +#else /* _DEBUG */ +# ifdef HAVE_ASSERT +# include <assert.h> +# define mhd_assert(CHK) assert(CHK) +# else /* ! HAVE_ASSERT */ +# include <stdio.h> +# include <stdlib.h> +# define mhd_assert(CHK) \ + do { \ + if (!(CHK)) { \ + fprintf(stderr, "%s:%u Assertion failed: %s\nProgram aborted.\n", \ + __FILE__, (unsigned)__LINE__, #CHK); \ + fflush(stderr); abort(); } \ + } while(0) +# endif /* ! HAVE_ASSERT */ +#endif /* _DEBUG */ + +#endif /* ! MHD_ASSERT_H */ diff --git a/src/lib/mhd_byteorder.h b/src/lib/mhd_byteorder.h @@ -0,0 +1,160 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2015 Karlson2k (Evgeny Grin) + + 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, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file microhttpd/mhd_byteorder.h + * @brief macro definitions for host byte order + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_BYTEORDER_H +#define MHD_BYTEORDER_H + +#include "platform.h" + +#if HAVE_ENDIAN_H +#include <endian.h> +#endif + +#if HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#if HAVE_MACHINE_ENDIAN_H +#include <machine/endian.h> +#endif + +#if HAVE_SYS_ENDIAN_H +#include <sys/endian.h> +#endif + +#if HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#if HAVE_SYS_BYTEORDER_H +#include <sys/byteorder.h> +#endif + +#if HAVE_SYS_MACHINE_H +#include <sys/machine.h> +#endif + +#if HAVE_MACHINE_PARAM_H +#include <machine/param.h> +#endif + +#if HAVE_SYS_ISA_DEFS_H +#include <sys/isa_defs.h> +#endif + +#define _MHD_BIG_ENDIAN 1234 +#define _MHD_LITTLE_ENDIAN 4321 +#define _MHD_PDP_ENDIAN 2143 + +#if defined(__BYTE_ORDER__) +#if defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN +#elif defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN +#elif defined(__ORDER_PDP_ENDIAN__) && __BYTE_ORDER__ == __ORDER_PDP_ENDIAN__ +#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN +#endif /* __BYTE_ORDER__ == __ORDER_PDP_ENDIAN__ */ +#elif defined(__BYTE_ORDER) +#if defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN +#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN +#elif defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN +#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN +#elif defined(__PDP_ENDIAN) && __BYTE_ORDER == __PDP_ENDIAN +#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN +#endif /* __BYTE_ORDER == __PDP_ENDIAN */ +#elif defined (BYTE_ORDER) +#if defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN +#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN +#elif defined(LITTLE_ENDIAN) && BYTE_ORDER == LITTLE_ENDIAN +#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN +#elif defined(PDP_ENDIAN) && BYTE_ORDER == PDP_ENDIAN +#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN +#endif /* __BYTE_ORDER == _PDP_ENDIAN */ +#elif defined (_BYTE_ORDER) +#if defined(_BIG_ENDIAN) && _BYTE_ORDER == _BIG_ENDIAN +#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN +#elif defined(_LITTLE_ENDIAN) && _BYTE_ORDER == _LITTLE_ENDIAN +#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN +#elif defined(_PDP_ENDIAN) && _BYTE_ORDER == _PDP_ENDIAN +#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN +#endif /* _BYTE_ORDER == _PDP_ENDIAN */ +#endif /* _BYTE_ORDER */ + +#ifndef _MHD_BYTE_ORDER +/* Byte order specification didn't detected in system headers */ +/* Try some guessing */ + +#if (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) || \ + (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) +/* Seems that we are on big endian platform */ +#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN +#elif (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) || \ + (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) +/* Seems that we are on little endian platform */ +#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN +#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \ + defined(_M_X64) || defined(_M_AMD64) || defined(i386) || defined(__i386) || \ + defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || \ + defined(_M_IX86) || defined(_X86_) || defined (__THW_INTEL__) +/* x86 family is little endian */ +#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN +#elif defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ + defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__) +/* Looks like we are on ARM/MIPS in big endian mode */ +#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN +#elif defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \ + defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) +/* Looks like we are on ARM/MIPS in little endian mode */ +#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN +#elif defined(__m68k__) || defined(M68000) || defined(__hppa__) || defined(__hppa) || \ + defined(__HPPA__) || defined(__370__) || defined(__THW_370__) || \ + defined(__s390__) || defined(__s390x__) || defined(__SYSC_ZARCH__) +/* Looks like we are on big endian platform */ +#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN +#elif defined(__ia64__) || defined(_IA64) || defined(__IA64__) || defined(__ia64) || \ + defined(_M_IA64) || defined(__itanium__) || defined(__bfin__) || \ + defined(__BFIN__) || defined(bfin) || defined(BFIN) +/* Looks like we are on little endian platform */ +#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN +#elif defined(_WIN32) +/* W32 is always little endian on all platforms */ +#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN +#elif defined(WORDS_BIGENDIAN) +/* Use byte order detected by configure */ +#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN +#endif /* _WIN32 */ + +#endif /* !_MHD_BYTE_ORDER */ + +#ifdef _MHD_BYTE_ORDER +/* Some safety checks */ +#if defined(WORDS_BIGENDIAN) && _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN +#error Configure detected big endian byte order but headers specify different byte order +#elif !defined(WORDS_BIGENDIAN) && _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN +#error Configure did not detect big endian byte order but headers specify big endian byte order +#endif /* !WORDS_BIGENDIAN && _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN */ +#endif /* _MHD_BYTE_ORDER */ + +#endif /* !MHD_BYTEORDER_H */ diff --git a/src/lib/mhd_compat.c b/src/lib/mhd_compat.c @@ -0,0 +1,114 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2014-2016 Karlson2k (Evgeny Grin) + + 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/mhd_compat.c + * @brief Implementation of platform missing functions. + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_compat.h" +#if defined(_WIN32) && !defined(__CYGWIN__) +#include <stdint.h> +#include <time.h> +#ifndef HAVE_SNPRINTF +#include <stdio.h> +#include <stdarg.h> +#endif /* HAVE_SNPRINTF */ +#endif /* _WIN32 && !__CYGWIN__ */ + +#ifndef HAVE_CALLOC +#include <string.h> /* for memset() */ +#endif /* ! HAVE_CALLOC */ + +#if defined(_WIN32) && !defined(__CYGWIN__) + +#ifndef HAVE_SNPRINTF +/* Emulate snprintf function on W32 */ +int +W32_snprintf (char *__restrict s, + size_t n, + const char *__restrict format, + ...) +{ + int ret; + va_list args; + + if ( (0 != n) && + (NULL != s) ) + { + va_start (args, + format); + ret = _vsnprintf (s, + n, + format, + args); + va_end (args); + if ((int)n == ret) + s[n - 1] = 0; + if (ret >= 0) + return ret; + } + va_start(args, + format); + ret = _vscprintf (format, + args); + va_end(args); + if ( (0 <= ret) && + (0 != n) && + (NULL == s) ) + return -1; + + return ret; +} + +#endif /* HAVE_SNPRINTF */ +#endif /* _WIN32 && !__CYGWIN__ */ + +#ifndef HAVE_CALLOC + +#ifdef __has_builtin +# if __has_builtin(__builtin_mul_overflow) +# define MHD_HAVE_NUL_OVERFLOW 1 +# endif +#elif __GNUC__+0 >= 5 +# define MHD_HAVE_NUL_OVERFLOW 1 +#endif /* __GNUC__ >= 5 */ + + +void *MHD_calloc_(size_t nelem, size_t elsize) +{ + size_t alloc_size; + void *ptr; +#ifdef MHD_HAVE_NUL_OVERFLOW + if (__builtin_mul_overflow(nelem, elsize, &alloc_size) || 0 == alloc_size) + return NULL; +#else /* ! MHD_HAVE_NUL_OVERFLOW */ + alloc_size = nelem * elsize; + if (0 == alloc_size || elsize != alloc_size / nelem) + return NULL; +#endif /* ! MHD_HAVE_NUL_OVERFLOW */ + ptr = malloc (alloc_size); + if (NULL == ptr) + return NULL; + memset(ptr, 0, alloc_size); + return ptr; +} +#endif /* ! HAVE_CALLOC */ diff --git a/src/lib/mhd_compat.h b/src/lib/mhd_compat.h @@ -0,0 +1,87 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2014-2016 Karlson2k (Evgeny Grin) + + 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/mhd_compat.h + * @brief Header for platform missing functions. + * @author Karlson2k (Evgeny Grin) + * + * Provides compatibility for platforms with some missing + * functionality. + * Any functions can be implemented as macro on some platforms + * unless explicitly marked otherwise. + * Any function argument can be skipped in macro, so avoid + * variable modification in function parameters. + */ + +#ifndef MHD_COMPAT_H +#define MHD_COMPAT_H 1 + +#include "mhd_options.h" +#include <stdlib.h> +#ifdef HAVE_STRING_H /* for strerror() */ +#include <string.h> +#endif /* HAVE_STRING_H */ + + /* MHD_strerror_ is strerror */ +#define MHD_strerror_(errnum) strerror((errnum)) + +/* Platform-independent snprintf name */ +#if defined(HAVE_SNPRINTF) +#define MHD_snprintf_ snprintf +#else /* ! HAVE_SNPRINTF */ +#if defined(_WIN32) && ! defined(__CYGWIN__) +/* Emulate snprintf function on W32 */ +int W32_snprintf(char *__restrict s, size_t n, const char *__restrict format, ...); +#define MHD_snprintf_ W32_snprintf +#else /* ! _WIN32 || __CYGWIN__ */ +#error Your platform does not support snprintf() and MHD does not know how to emulate it on your platform. +#endif /* ! _WIN32 || __CYGWIN__ */ +#endif /* ! HAVE_SNPRINTF */ + +#ifdef HAVE_RANDOM +/** + * Generate pseudo random number at least 30-bit wide. + * @return pseudo random number at least 30-bit wide. + */ +#define MHD_random_() random() +#else /* HAVE_RANDOM */ +#ifdef HAVE_RAND +/** + * Generate pseudo random number at least 30-bit wide. + * @return pseudo random number at least 30-bit wide. + */ +#define MHD_random_() ( (((long)rand()) << 15) + (long)rand() ) +#endif /* HAVE_RAND */ +#endif /* HAVE_RANDOM */ + +#ifdef HAVE_CALLOC +/** + * MHD_calloc_ is platform-independent calloc() + */ +#define MHD_calloc_(n,s) calloc((n),(s)) +#else /* ! HAVE_CALLOC */ +/** + * MHD_calloc_ is platform-independent calloc() + */ +void *MHD_calloc_(size_t nelem, size_t elsize); +#endif /* ! HAVE_CALLOC */ + +#endif /* MHD_COMPAT_H */ diff --git a/src/lib/mhd_itc.c b/src/lib/mhd_itc.c @@ -0,0 +1,70 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2016 Karlson2k (Evgeny Grin) + + 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/mhd_itc.c + * @brief Implementation of inter-thread communication functions + * @author Karlson2k (Evgeny Grin) + * @author Christian Grothoff + */ + +#include "mhd_itc.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#include <fcntl.h> +#include "internal.h" + + +#if defined(_MHD_ITC_PIPE) +#if !defined(_WIN32) || defined(__CYGWIN__) + +#ifndef HAVE_PIPE2_FUNC +/** + * Change itc FD options to be non-blocking. + * + * @param itc the inter-thread communication primitive to manipulate + * @return non-zero if succeeded, zero otherwise + */ +int +MHD_itc_nonblocking_ (struct MHD_itc_ itc) +{ + unsigned int i; + + for (i=0;i<2;i++) + { + int flags; + + flags = fcntl (itc.fd[i], + F_GETFL); + if (-1 == flags) + return 0; + + if ( ((flags | O_NONBLOCK) != flags) && + (0 != fcntl (itc.fd[i], + F_SETFL, + flags | O_NONBLOCK)) ) + return 0; + } + return !0; +} +#endif /* ! HAVE_PIPE2_FUNC */ +#endif /* !_WIN32 || __CYGWIN__ */ +#endif /* _MHD_ITC_EVENTFD || _MHD_ITC_PIPE */ diff --git a/src/lib/mhd_itc.h b/src/lib/mhd_itc.h @@ -0,0 +1,361 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2016 Karlson2k (Evgeny Grin) + + 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/mhd_itc.h + * @brief Header for platform-independent inter-thread communication + * @author Karlson2k (Evgeny Grin) + * @author Christian Grothoff + * + * Provides basic abstraction for inter-thread communication. + * Any functions can be implemented as macro on some platforms + * unless explicitly marked otherwise. + * Any function argument can be skipped in macro, so avoid + * variable modification in function parameters. + */ +#ifndef MHD_ITC_H +#define MHD_ITC_H 1 +#include "mhd_itc_types.h" + +#include <fcntl.h> + +#ifndef MHD_PANIC +# include <stdio.h> +# include <stdlib.h> +/* Simple implementation of MHD_PANIC, to be used outside lib */ +# define MHD_PANIC(msg) do { fprintf (stderr, \ + "Abnormal termination at %d line in file %s: %s\n", \ + (int)__LINE__, __FILE__, msg); abort();} while(0) +#endif /* ! MHD_PANIC */ + +#if defined(_MHD_ITC_EVENTFD) + +/* **************** Optimized GNU/Linux ITC implementation by eventfd ********** */ +#include <sys/eventfd.h> +#include <stdint.h> /* for uint64_t */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for read(), write(), errno */ +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> /* for strerror() */ +#endif + + +/** + * Initialise ITC by generating eventFD + * @param itc the itc to initialise + * @return non-zero if succeeded, zero otherwise + */ +#define MHD_itc_init_(itc) (-1 != ((itc).fd = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK))) + +/** + * Get description string of last errno for itc operations. + */ +#define MHD_itc_last_strerror_() strerror(errno) + +/** + * Internal static const helper for MHD_itc_activate_() + */ +static const uint64_t _MHD_itc_wr_data = 1; + +/** + * Activate signal on @a itc + * @param itc the itc to use + * @param str ignored + * @return non-zero if succeeded, zero otherwise + */ +#define MHD_itc_activate_(itc, str) \ + ((write((itc).fd, (const void*)&_MHD_itc_wr_data, 8) > 0) || (EAGAIN == errno)) + +/** + * Return read FD of @a itc which can be used for poll(), select() etc. + * @param itc the itc to get FD + * @return FD of read side + */ +#define MHD_itc_r_fd_(itc) ((itc).fd) + +/** + * Return write FD of @a itc + * @param itc the itc to get FD + * @return FD of write side + */ +#define MHD_itc_w_fd_(itc) ((itc).fd) + +/** + * Clear signaled state on @a itc + * @param itc the itc to clear + */ +#define MHD_itc_clear_(itc) \ + do { uint64_t __b; int __r; \ + __r = read((itc).fd, &__b, sizeof(__b)); \ + (void)__r; } while(0) + +/** + * Destroy previously initialised ITC + * @param itc the itc to destroy + * @return non-zero if succeeded, zero otherwise + */ +#define MHD_itc_destroy_(itc) ((0 != close ((itc).fd)) || (EBADF != errno)) + +/** + * Check whether ITC has valid value. + * + * Macro check whether @a itc value is valid (allowed), + * macro does not check whether @a itc was really initialised. + * @param itc the itc to check + * @return boolean true if @a itc has valid value, + * boolean false otherwise. + */ +#define MHD_ITC_IS_VALID_(itc) (-1 != ((itc).fd)) + +/** + * Set @a itc to invalid value. + * @param itc the itc to set + */ +#define MHD_itc_set_invalid_(itc) ((itc).fd = -1) + + +#elif defined(_MHD_ITC_PIPE) + +/* **************** Standard UNIX ITC implementation by pipe ********** */ + +#if defined(HAVE_PIPE2_FUNC) && defined(HAVE_FCNTL_H) +# include <fcntl.h> /* for O_CLOEXEC, O_NONBLOCK */ +#endif /* HAVE_PIPE2_FUNC && HAVE_FCNTL_H */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for read(), write(), errno */ +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> /* for strerror() */ +#endif + + +/** + * Initialise ITC by generating pipe + * @param itc the itc to initialise + * @return non-zero if succeeded, zero otherwise + */ +#ifdef HAVE_PIPE2_FUNC +# define MHD_itc_init_(itc) (!pipe2((itc).fd, O_CLOEXEC | O_NONBLOCK)) +#else /* ! HAVE_PIPE2_FUNC */ +# define MHD_itc_init_(itc) \ + ( (!pipe((itc).fd)) ? \ + (MHD_itc_nonblocking_((itc)) ? \ + (!0) : \ + (MHD_itc_destroy_((itc)), 0) ) \ + : (0) ) +#endif /* ! HAVE_PIPE2_FUNC */ + +/** + * Get description string of last errno for itc operations. + */ +#define MHD_itc_last_strerror_() strerror(errno) + +/** + * Activate signal on @a itc + * @param itc the itc to use + * @param str one-symbol string, useful only for strace debug + * @return non-zero if succeeded, zero otherwise + */ +#define MHD_itc_activate_(itc, str) \ + ((write((itc).fd[1], (const void*)(str), 1) > 0) || (EAGAIN == errno)) + + +/** + * Return read FD of @a itc which can be used for poll(), select() etc. + * @param itc the itc to get FD + * @return FD of read side + */ +#define MHD_itc_r_fd_(itc) ((itc).fd[0]) + +/** + * Return write FD of @a itc + * @param itc the itc to get FD + * @return FD of write side + */ +#define MHD_itc_w_fd_(itc) ((itc).fd[1]) + +/** + * Clear signaled state on @a itc + * @param itc the itc to clear + */ +#define MHD_itc_clear_(itc) do \ + { long __b; \ + while(0 < read((itc).fd[0], &__b, sizeof(__b))) \ + {} } while(0) + +/** + * Destroy previously initialised ITC + * @param itc the itc to destroy + * @return non-zero if succeeded, zero otherwise + */ +#define MHD_itc_destroy_(itc) \ + ( (0 == close ((itc).fd[0])) ? \ + (0 == close ((itc).fd[1])) : \ + ((close ((itc).fd[1])), 0) ) + +/** + * Check whether ITC has valid value. + * + * Macro check whether @a itc value is valid (allowed), + * macro does not check whether @a itc was really initialised. + * @param itc the itc to check + * @return boolean true if @a itc has valid value, + * boolean false otherwise. + */ +#define MHD_ITC_IS_VALID_(itc) (-1 != (itc).fd[0]) + +/** + * Set @a itc to invalid value. + * @param itc the itc to set + */ +#define MHD_itc_set_invalid_(itc) ((itc).fd[0] = (itc).fd[1] = -1) + +#ifndef HAVE_PIPE2_FUNC + /** + * Change itc FD options to be non-blocking. + * + * @param fd the FD to manipulate + * @return non-zero if succeeded, zero otherwise + */ + int + MHD_itc_nonblocking_ (struct MHD_itc_ itc); +#endif /* ! HAVE_PIPE2_FUNC */ + + +#elif defined(_MHD_ITC_SOCKETPAIR) + +/* **************** ITC implementation by socket pair ********** */ + +#include "mhd_sockets.h" + + +/** + * Initialise ITC by generating socketpair + * @param itc the itc to initialise + * @return non-zero if succeeded, zero otherwise + */ +#ifdef MHD_socket_pair_nblk_ +# define MHD_itc_init_(itc) MHD_socket_pair_nblk_((itc).sk) +#else /* ! MHD_socket_pair_nblk_ */ +# define MHD_itc_init_(itc) \ + (MHD_socket_pair_((itc).sk) ? \ + (MHD_itc_nonblocking_((itc)) ? \ + (!0) : \ + (MHD_itc_destroy_((itc)), 0) ) \ + : (0)) +#endif /* ! MHD_socket_pair_nblk_ */ + +/** + * Get description string of last error for itc operations. + */ +#define MHD_itc_last_strerror_() MHD_socket_last_strerr_() + +/** + * Activate signal on @a itc + * @param itc the itc to use + * @param str one-symbol string, useful only for strace debug + * @return non-zero if succeeded, zero otherwise + */ +#define MHD_itc_activate_(itc, str) \ + ((MHD_send_((itc).sk[1], (str), 1) > 0) || \ + (MHD_SCKT_ERR_IS_EAGAIN_(MHD_socket_get_error_()))) + +/** + * Return read FD of @a itc which can be used for poll(), select() etc. + * @param itc the itc to get FD + * @return FD of read side + */ +#define MHD_itc_r_fd_(itc) ((itc).sk[0]) + +/** + * Return write FD of @a itc + * @param itc the itc to get FD + * @return FD of write side + */ +#define MHD_itc_w_fd_(itc) ((itc).sk[1]) + +/** + * Clear signaled state on @a itc + * @param itc the itc to clear + */ +#define MHD_itc_clear_(itc) do \ + { long __b; \ + while(0 < recv((itc).sk[0], \ + (char*)&__b, \ + sizeof(__b), 0)) \ + {} } while(0) + +/** + * Destroy previously initialised ITC + * @param itc the itc to destroy + * @return non-zero if succeeded, zero otherwise + */ +#define MHD_itc_destroy_(itc) \ + ( MHD_socket_close_((itc).sk[0]) ? \ + MHD_socket_close_((itc).sk[1]) : \ + ((void)MHD_socket_close_((itc).sk[1]), 0) ) + + +/** + * Check whether ITC has valid value. + * + * Macro check whether @a itc value is valid (allowed), + * macro does not check whether @a itc was really initialised. + * @param itc the itc to check + * @return boolean true if @a itc has valid value, + * boolean false otherwise. + */ +#define MHD_ITC_IS_VALID_(itc) (MHD_INVALID_SOCKET != (itc).sk[0]) + +/** + * Set @a itc to invalid value. + * @param itc the itc to set + */ +#define MHD_itc_set_invalid_(itc) ((itc).sk[0] = (itc).sk[1] = MHD_INVALID_SOCKET) + +#ifndef MHD_socket_pair_nblk_ +# define MHD_itc_nonblocking_(pip) (MHD_socket_nonblocking_((pip).sk[0]) && MHD_socket_nonblocking_((pip).sk[1])) +#endif /* ! MHD_socket_pair_nblk_ */ + +#endif /* _MHD_ITC_SOCKETPAIR */ + +/** + * Destroy previously initialised ITC and abort execution + * if error is detected. + * @param itc the itc to destroy + */ +#define MHD_itc_destroy_chk_(itc) do { \ + if (!MHD_itc_destroy_(itc)) \ + MHD_PANIC(_("Failed to destroy ITC.\n")); \ + } while(0) + +/** + * Check whether ITC has invalid value. + * + * Macro check whether @a itc value is invalid, + * macro does not check whether @a itc was destroyed. + * @param itc the itc to check + * @return boolean true if @a itc has invalid value, + * boolean false otherwise. + */ +#define MHD_ITC_IS_INVALID_(itc) (! MHD_ITC_IS_VALID_(itc)) + +#endif /* MHD_ITC_H */ diff --git a/src/lib/mhd_itc_types.h b/src/lib/mhd_itc_types.h @@ -0,0 +1,77 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2016 Karlson2k (Evgeny Grin), 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/mhd_itc_types.h + * @brief Types for platform-independent inter-thread communication + * @author Karlson2k (Evgeny Grin) + * @author Christian Grothoff + * + * Provides basic types for inter-thread communication. + * Designed to be included by other headers. + */ +#ifndef MHD_ITC_TYPES_H +#define MHD_ITC_TYPES_H 1 +#include "mhd_options.h" + +/* Force socketpair on native W32 */ +#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(_MHD_ITC_SOCKETPAIR) +#error _MHD_ITC_SOCKETPAIR is not defined on naitive W32 platform +#endif /* _WIN32 && !__CYGWIN__ && !_MHD_ITC_SOCKETPAIR */ + +#if defined(_MHD_ITC_EVENTFD) +/* **************** Optimized GNU/Linux ITC implementation by eventfd ********** */ + +/** + * Data type for a MHD ITC. + */ +struct MHD_itc_ +{ + int fd; +}; + +#elif defined(_MHD_ITC_PIPE) +/* **************** Standard UNIX ITC implementation by pipe ********** */ + +/** + * Data type for a MHD ITC. + */ +struct MHD_itc_ +{ + int fd[2]; +}; + + +#elif defined(_MHD_ITC_SOCKETPAIR) +/* **************** ITC implementation by socket pair ********** */ + +#include "mhd_sockets.h" + +/** + * Data type for a MHD ITC. + */ +struct MHD_itc_ +{ + MHD_socket sk[2]; +}; + +#endif /* _MHD_ITC_SOCKETPAIR */ + +#endif /* ! MHD_ITC_TYPES_H */ diff --git a/src/lib/mhd_limits.h b/src/lib/mhd_limits.h @@ -0,0 +1,146 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2015 Karlson2k (Evgeny Grin) + + 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/mhd_limits.h + * @brief limits values definitions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_LIMITS_H +#define MHD_LIMITS_H + +#include "platform.h" + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif /* HAVE_LIMITS_H */ + +#define MHD_UNSIGNED_TYPE_MAX_(type) ((type)-1) +/* Assume 8 bits per byte, no padding bits. */ +#define MHD_SIGNED_TYPE_MAX_(type) \ + ( (type)((( ((type)1) << (sizeof(type)*8 - 2)) - 1)*2 + 1) ) +#define MHD_TYPE_IS_SIGNED_(type) (((type)0)>((type)-1)) + +#ifndef UINT_MAX +#ifdef __UINT_MAX__ +#define UINT_MAX __UINT_MAX__ +#else /* ! __UINT_MAX__ */ +#define UINT_MAX MHD_UNSIGNED_TYPE_MAX_(unsigned int) +#endif /* ! __UINT_MAX__ */ +#endif /* !UINT_MAX */ + +#ifndef LONG_MAX +#ifdef __LONG_MAX__ +#define LONG_MAX __LONG_MAX__ +#else /* ! __LONG_MAX__ */ +#define LONG_MAX MHD_SIGNED_TYPE_MAX(long) +#endif /* ! __LONG_MAX__ */ +#endif /* !OFF_T_MAX */ + +#ifndef ULLONG_MAX +#define ULLONG_MAX MHD_UNSIGNED_TYPE_MAX_(MHD_UNSIGNED_LONG_LONG) +#endif /* !ULLONG_MAX */ + +#ifndef INT32_MAX +#ifdef __INT32_MAX__ +#define INT32_MAX __INT32_MAX__ +#else /* ! __INT32_MAX__ */ +#define INT32_MAX ((int32_t)0x7FFFFFFF) +#endif /* ! __INT32_MAX__ */ +#endif /* !INT32_MAX */ + +#ifndef UINT32_MAX +#ifdef __UINT32_MAX__ +#define UINT32_MAX __UINT32_MAX__ +#else /* ! __UINT32_MAX__ */ +#define UINT32_MAX ((int32_t)0xFFFFFFFF) +#endif /* ! __UINT32_MAX__ */ +#endif /* !UINT32_MAX */ + +#ifndef UINT64_MAX +#ifdef __UINT64_MAX__ +#define UINT64_MAX __UINT64_MAX__ +#else /* ! __UINT64_MAX__ */ +#define UINT64_MAX ((uint64_t)0xFFFFFFFFFFFFFFFF) +#endif /* ! __UINT64_MAX__ */ +#endif /* !UINT64_MAX */ + +#ifndef INT64_MAX +#ifdef __INT64_MAX__ +#define INT64_MAX __INT64_MAX__ +#else /* ! __INT64_MAX__ */ +#define INT64_MAX ((int64_t)0x7FFFFFFFFFFFFFFF) +#endif /* ! __UINT64_MAX__ */ +#endif /* !INT64_MAX */ + +#ifndef SIZE_MAX +#ifdef __SIZE_MAX__ +#define SIZE_MAX __SIZE_MAX__ +#elif defined(UINTPTR_MAX) +#define SIZE_MAX UINTPTR_MAX +#else /* ! __SIZE_MAX__ */ +#define SIZE_MAX MHD_UNSIGNED_TYPE_MAX_(size_t) +#endif /* ! __SIZE_MAX__ */ +#endif /* !SIZE_MAX */ + +#ifndef SSIZE_MAX +#ifdef __SSIZE_MAX__ +#define SSIZE_MAX __SSIZE_MAX__ +#elif defined(PTRDIFF_MAX) +#define SSIZE_MAX PTRDIFF_MAX +#elif defined(INTPTR_MAX) +#define SSIZE_MAX INTPTR_MAX +#else +#define SSIZE_MAN MHD_SIGNED_TYPE_MAX_(ssize_t) +#endif +#endif /* ! SSIZE_MAX */ + +#ifndef OFF_T_MAX +#ifdef OFF_MAX +#define OFF_T_MAX OFF_MAX +#elif defined(OFFT_MAX) +#define OFF_T_MAX OFFT_MAX +#elif defined(__APPLE__) && defined(__MACH__) +#define OFF_T_MAX INT64_MAX +#else +#define OFF_T_MAX MHD_SIGNED_TYPE_MAX_(off_t) +#endif +#endif /* !OFF_T_MAX */ + +#if defined(_LARGEFILE64_SOURCE) && !defined(OFF64_T_MAX) +#define OFF64_T_MAX MHD_SIGNED_TYPE_MAX_(uint64_t) +#endif /* _LARGEFILE64_SOURCE && !OFF64_T_MAX */ + +#ifndef TIME_T_MAX +#define TIME_T_MAX ((time_t) \ + ( MHD_TYPE_IS_SIGNED_(time_t) ? \ + MHD_SIGNED_TYPE_MAX_(time_t) : \ + MHD_UNSIGNED_TYPE_MAX_(time_t))) +#endif /* !TIME_T_MAX */ + +#ifndef TIMEVAL_TV_SEC_MAX +#ifndef _WIN32 +#define TIMEVAL_TV_SEC_MAX TIME_T_MAX +#else /* _WIN32 */ +#define TIMEVAL_TV_SEC_MAX LONG_MAX +#endif /* _WIN32 */ +#endif /* !TIMEVAL_TV_SEC_MAX */ + +#endif /* MHD_LIMITS_H */ diff --git a/src/lib/mhd_locks.h b/src/lib/mhd_locks.h @@ -0,0 +1,183 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2016 Karlson2k (Evgeny Grin) + + 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/mhd_locks.h + * @brief Header for platform-independent locks abstraction + * @author Karlson2k (Evgeny Grin) + * @author Christian Grothoff + * + * Provides basic abstraction for locks/mutex. + * Any functions can be implemented as macro on some platforms + * unless explicitly marked otherwise. + * Any function argument can be skipped in macro, so avoid + * variable modification in function parameters. + * + * @warning Unlike pthread functions, most of functions return + * nonzero on success. + */ + +#ifndef MHD_LOCKS_H +#define MHD_LOCKS_H 1 + +#include "mhd_options.h" + +#if defined(MHD_USE_W32_THREADS) +# define MHD_W32_MUTEX_ 1 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif /* !WIN32_LEAN_AND_MEAN */ +# include <windows.h> +#elif defined(HAVE_PTHREAD_H) && defined(MHD_USE_POSIX_THREADS) +# define MHD_PTHREAD_MUTEX_ 1 +# undef HAVE_CONFIG_H +# include <pthread.h> +# define HAVE_CONFIG_H 1 +#else +# error No base mutex API is available. +#endif + +#ifndef MHD_PANIC +# include <stdio.h> +# include <stdlib.h> +/* Simple implementation of MHD_PANIC, to be used outside lib */ +# define MHD_PANIC(msg) do { fprintf (stderr, \ + "Abnormal termination at %d line in file %s: %s\n", \ + (int)__LINE__, __FILE__, msg); abort();} while(0) +#endif /* ! MHD_PANIC */ + +#if defined(MHD_PTHREAD_MUTEX_) + typedef pthread_mutex_t MHD_mutex_; +#elif defined(MHD_W32_MUTEX_) + typedef CRITICAL_SECTION MHD_mutex_; +#endif + +#if defined(MHD_PTHREAD_MUTEX_) +/** + * Initialise new mutex. + * @param pmutex pointer to the mutex + * @return nonzero on success, zero otherwise + */ +#define MHD_mutex_init_(pmutex) (!(pthread_mutex_init((pmutex), NULL))) +#elif defined(MHD_W32_MUTEX_) +/** + * Initialise new mutex. + * @param pmutex pointer to mutex + * @return nonzero on success, zero otherwise + */ +#define MHD_mutex_init_(pmutex) (InitializeCriticalSectionAndSpinCount((pmutex),16)) +#endif + +#if defined(MHD_PTHREAD_MUTEX_) +# if defined(PTHREAD_MUTEX_INITIALIZER) +/** + * Define static mutex and statically initialise it. + */ +# define MHD_MUTEX_STATIC_DEFN_INIT_(m) static MHD_mutex_ m = PTHREAD_MUTEX_INITIALIZER +# endif /* PTHREAD_MUTEX_INITIALIZER */ +#endif + +#if defined(MHD_PTHREAD_MUTEX_) +/** + * Destroy previously initialised mutex. + * @param pmutex pointer to mutex + * @return nonzero on success, zero otherwise + */ +#define MHD_mutex_destroy_(pmutex) (!(pthread_mutex_destroy((pmutex)))) +#elif defined(MHD_W32_MUTEX_) +/** + * Destroy previously initialised mutex. + * @param pmutex pointer to mutex + * @return Always nonzero + */ +#define MHD_mutex_destroy_(pmutex) (DeleteCriticalSection((pmutex)), !0) +#endif + +/** + * Destroy previously initialised mutex and abort execution + * if error is detected. + * @param pmutex pointer to mutex + */ +#define MHD_mutex_destroy_chk_(pmutex) do { \ + if (!MHD_mutex_destroy_(pmutex)) \ + MHD_PANIC(_("Failed to destroy mutex.\n")); \ + } while(0) + + +#if defined(MHD_PTHREAD_MUTEX_) +/** + * Acquire lock on previously initialised mutex. + * If mutex was already locked by other thread, function + * blocks until mutex becomes available. + * @param pmutex pointer to mutex + * @return nonzero on success, zero otherwise + */ +#define MHD_mutex_lock_(pmutex) (!(pthread_mutex_lock((pmutex)))) +#elif defined(MHD_W32_MUTEX_) +/** + * Acquire lock on previously initialised mutex. + * If mutex was already locked by other thread, function + * blocks until mutex becomes available. + * @param pmutex pointer to mutex + * @return Always nonzero + */ +#define MHD_mutex_lock_(pmutex) (EnterCriticalSection((pmutex)), !0) +#endif + +/** + * Acquire lock on previously initialised mutex. + * If mutex was already locked by other thread, function + * blocks until mutex becomes available. + * If error is detected, execution will be aborted. + * @param pmutex pointer to mutex + */ +#define MHD_mutex_lock_chk_(pmutex) do { \ + if (!MHD_mutex_lock_(pmutex)) \ + MHD_PANIC(_("Failed to lock mutex.\n")); \ + } while(0) + +#if defined(MHD_PTHREAD_MUTEX_) +/** + * Unlock previously initialised and locked mutex. + * @param pmutex pointer to mutex + * @return nonzero on success, zero otherwise + */ +#define MHD_mutex_unlock_(pmutex) (!(pthread_mutex_unlock((pmutex)))) +#elif defined(MHD_W32_MUTEX_) +/** + * Unlock previously initialised and locked mutex. + * @param pmutex pointer to mutex + * @return Always nonzero + */ +#define MHD_mutex_unlock_(pmutex) (LeaveCriticalSection((pmutex)), !0) +#endif + +/** + * Unlock previously initialised and locked mutex. + * If error is detected, execution will be aborted. + * @param pmutex pointer to mutex + */ +#define MHD_mutex_unlock_chk_(pmutex) do { \ + if (!MHD_mutex_unlock_(pmutex)) \ + MHD_PANIC(_("Failed to unlock mutex.\n")); \ + } while(0) + + +#endif /* ! MHD_LOCKS_H */ diff --git a/src/lib/mhd_mono_clock.c b/src/lib/mhd_mono_clock.c @@ -0,0 +1,377 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2015 Karlson2k (Evgeny Grin) + + 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/mhd_mono_clock.h + * @brief internal monotonic clock functions implementations + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_mono_clock.h" + +#if defined(_WIN32) && ! defined(__CYGWIN__) && defined(HAVE_CLOCK_GETTIME) +/* Prefer native clock source over wrappers */ +#undef HAVE_CLOCK_GETTIME +#endif /* _WIN32 && ! __CYGWIN__ && HAVE_CLOCK_GETTIME */ + +#ifdef HAVE_CLOCK_GETTIME +#include <time.h> +#endif /* HAVE_CLOCK_GETTIME */ + +#ifdef HAVE_GETHRTIME +#ifdef HAVE_SYS_TIME_H +/* Solaris defines gethrtime() in sys/time.h */ +#include <sys/time.h> +#endif /* HAVE_SYS_TIME_H */ +#ifdef HAVE_TIME_H +/* HP-UX defines gethrtime() in time.h */ +#include <time.h> +#endif /* HAVE_TIME_H */ +#endif /* HAVE_GETHRTIME */ + +#ifdef HAVE_CLOCK_GET_TIME +#include <mach/mach.h> +/* for host_get_clock_service(), mach_host_self(), mach_task_self() */ +#include <mach/clock.h> +/* for clock_get_time() */ + +#define _MHD_INVALID_CLOCK_SERV ((clock_serv_t) -2) + +static clock_serv_t mono_clock_service = _MHD_INVALID_CLOCK_SERV; +#endif /* HAVE_CLOCK_GET_TIME */ + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +/* Do not include unneeded parts of W32 headers. */ +#define WIN32_LEAN_AND_MEAN 1 +#endif /* !WIN32_LEAN_AND_MEAN */ +#include <windows.h> +#include <stdint.h> +#endif /* _WIN32 */ + +#ifdef HAVE_CLOCK_GETTIME +#ifdef CLOCK_REALTIME +#define _MHD_UNWANTED_CLOCK CLOCK_REALTIME +#else /* !CLOCK_REALTIME */ +#define _MHD_UNWANTED_CLOCK ((clockid_t) -2) +#endif /* !CLOCK_REALTIME */ + +static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK; +#endif /* HAVE_CLOCK_GETTIME */ + +/* sync clocks; reduce chance of value wrap */ +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GET_TIME) || defined(HAVE_GETHRTIME) +static time_t mono_clock_start; +#endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GET_TIME || HAVE_GETHRTIME */ +static time_t sys_clock_start; +#ifdef HAVE_GETHRTIME +static hrtime_t hrtime_start; +#endif /* HAVE_GETHRTIME */ +#ifdef _WIN32 +#if _WIN32_WINNT >= 0x0600 +static uint64_t tick_start; +#else /* _WIN32_WINNT < 0x0600 */ +static int64_t perf_freq; +static int64_t perf_start; +#endif /* _WIN32_WINNT < 0x0600 */ +#endif /* _WIN32 */ + + + +/** + * Type of monotonic clock source + */ +enum _MHD_mono_clock_source +{ + /** + * No monotonic clock + */ + _MHD_CLOCK_NO_SOURCE = 0, + + /** + * clock_gettime() with specific clock + */ + _MHD_CLOCK_GETTIME, + + /** + * clock_get_time() with specific clock service + */ + _MHD_CLOCK_GET_TIME, + + /** + * gethrtime() / 1000000000 + */ + _MHD_CLOCK_GETHRTIME, + + /** + * GetTickCount64() / 1000 + */ + _MHD_CLOCK_GETTICKCOUNT64, + + /** + * QueryPerformanceCounter() / QueryPerformanceFrequency() + */ + _MHD_CLOCK_PERFCOUNTER +}; + + +/** + * Initialise monotonic seconds counter. + */ +void +MHD_monotonic_sec_counter_init (void) +{ +#ifdef HAVE_CLOCK_GET_TIME + mach_timespec_t cur_time; +#endif /* HAVE_CLOCK_GET_TIME */ + enum _MHD_mono_clock_source mono_clock_source = _MHD_CLOCK_NO_SOURCE; +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + + mono_clock_id = _MHD_UNWANTED_CLOCK; +#endif /* HAVE_CLOCK_GETTIME */ +#ifdef HAVE_CLOCK_GET_TIME + mono_clock_service = _MHD_INVALID_CLOCK_SERV; +#endif /* HAVE_CLOCK_GET_TIME */ + + /* just a little syntactic trick to get the + various following ifdef's to work out nicely */ + if (0) + { + } + else +#ifdef HAVE_CLOCK_GETTIME +#ifdef CLOCK_MONOTONIC_COARSE + /* Linux-specific fast value-getting clock */ + /* Can be affected by frequency adjustment and don't count time in suspend, */ + /* but preferred since it's fast */ + if (0 == clock_gettime (CLOCK_MONOTONIC_COARSE, + &ts)) + { + mono_clock_id = CLOCK_MONOTONIC_COARSE; + mono_clock_start = ts.tv_sec; + mono_clock_source = _MHD_CLOCK_GETTIME; + } + else +#endif /* CLOCK_MONOTONIC_COARSE */ +#ifdef CLOCK_MONOTONIC_FAST + /* FreeBSD/DragonFly fast value-getting clock */ + /* Can be affected by frequency adjustment, but preferred since it's fast */ + if (0 == clock_gettime (CLOCK_MONOTONIC_FAST, + &ts)) + { + mono_clock_id = CLOCK_MONOTONIC_FAST; + mono_clock_start = ts.tv_sec; + mono_clock_source = _MHD_CLOCK_GETTIME; + } + else +#endif /* CLOCK_MONOTONIC_COARSE */ +#ifdef CLOCK_MONOTONIC_RAW + /* Linux-specific clock */ + /* Not affected by frequency adjustment, but don't count time in suspend */ + if (0 == clock_gettime (CLOCK_MONOTONIC_RAW, + &ts)) + { + mono_clock_id = CLOCK_MONOTONIC_RAW; + mono_clock_start = ts.tv_sec; + mono_clock_source = _MHD_CLOCK_GETTIME; + } + else +#endif /* CLOCK_MONOTONIC_RAW */ +#ifdef CLOCK_BOOTTIME + /* Linux-specific clock */ + /* Count time in suspend so it's real monotonic on Linux, */ + /* but can be slower value-getting than other clocks */ + if (0 == clock_gettime (CLOCK_BOOTTIME, + &ts)) + { + mono_clock_id = CLOCK_BOOTTIME; + mono_clock_start = ts.tv_sec; + mono_clock_source = _MHD_CLOCK_GETTIME; + } + else +#endif /* CLOCK_BOOTTIME */ +#ifdef CLOCK_MONOTONIC + /* Monotonic clock */ + /* Widely supported, may be affected by frequency adjustment */ + /* On Linux it's not truly monotonic as it doesn't count time in suspend */ + if (0 == clock_gettime (CLOCK_MONOTONIC, + &ts)) + { + mono_clock_id = CLOCK_MONOTONIC; + mono_clock_start = ts.tv_sec; + mono_clock_source = _MHD_CLOCK_GETTIME; + } + else +#endif /* CLOCK_BOOTTIME */ +#endif /* HAVE_CLOCK_GETTIME */ +#ifdef HAVE_CLOCK_GET_TIME + /* Darwin-specific monotonic clock */ + /* Should be monotonic as clock_set_time function always unconditionally */ + /* failed on latest kernels */ + if ( (KERN_SUCCESS == host_get_clock_service (mach_host_self(), + SYSTEM_CLOCK, + &mono_clock_service)) && + (KERN_SUCCESS == clock_get_time (mono_clock_service, + &cur_time)) ) + { + mono_clock_start = cur_time.tv_sec; + mono_clock_source = _MHD_CLOCK_GET_TIME; + } + else +#endif /* HAVE_CLOCK_GET_TIME */ +#ifdef _WIN32 +#if _WIN32_WINNT >= 0x0600 + /* W32 Vista or later specific monotonic clock */ + /* Available since Vista, ~15ms accuracy */ + if (1) + { + tick_start = GetTickCount64 (); + mono_clock_source = _MHD_CLOCK_GETTICKCOUNT64; + } + else +#else /* _WIN32_WINNT < 0x0600 */ + /* W32 specific monotonic clock */ + /* Available on Windows 2000 and later */ + if (1) + { + LARGE_INTEGER freq; + LARGE_INTEGER perf_counter; + + QueryPerformanceFrequency (&freq); /* never fail on XP and later */ + QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ + perf_freq = freq.QuadPart; + perf_start = perf_counter.QuadPart; + mono_clock_source = _MHD_CLOCK_PERFCOUNTER; + } + else +#endif /* _WIN32_WINNT < 0x0600 */ +#endif /* _WIN32 */ +#ifdef HAVE_CLOCK_GETTIME +#ifdef CLOCK_HIGHRES + /* Solaris-specific monotonic high-resolution clock */ + /* Not preferred due to be potentially resource-hungry */ + if (0 == clock_gettime (CLOCK_HIGHRES, + &ts)) + { + mono_clock_id = CLOCK_HIGHRES; + mono_clock_start = ts.tv_sec; + mono_clock_source = _MHD_CLOCK_GETTIME; + } + else +#endif /* CLOCK_HIGHRES */ +#endif /* HAVE_CLOCK_GETTIME */ +#ifdef HAVE_GETHRTIME + /* HP-UX and Solaris monotonic clock */ + /* Not preferred due to be potentially resource-hungry */ + if (1) + { + hrtime_start = gethrtime (); + mono_clock_source = _MHD_CLOCK_GETHRTIME; + } + else +#endif /* HAVE_GETHRTIME */ + { + /* no suitable clock source was found */ + mono_clock_source = _MHD_CLOCK_NO_SOURCE; + } + +#ifdef HAVE_CLOCK_GET_TIME + if ( (_MHD_CLOCK_GET_TIME != mono_clock_source) && + (_MHD_INVALID_CLOCK_SERV != mono_clock_service) ) + { + /* clock service was initialised but clock_get_time failed */ + mach_port_deallocate (mach_task_self(), + mono_clock_service); + mono_clock_service = _MHD_INVALID_CLOCK_SERV; + } +#else + (void) mono_clock_source; /* avoid compiler warning */ +#endif /* HAVE_CLOCK_GET_TIME */ + + sys_clock_start = time (NULL); +} + + +/** + * Deinitialise monotonic seconds counter by freeing any allocated resources + */ +void +MHD_monotonic_sec_counter_finish (void) +{ +#ifdef HAVE_CLOCK_GET_TIME + if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) + { + mach_port_deallocate (mach_task_self(), + mono_clock_service); + mono_clock_service = _MHD_INVALID_CLOCK_SERV; + } +#endif /* HAVE_CLOCK_GET_TIME */ +} + + +/** + * Monotonic seconds counter, useful for timeout calculation. + * Tries to be not affected by manually setting the system real time + * clock or adjustments by NTP synchronization. + * + * @return number of seconds from some fixed moment + */ +time_t +MHD_monotonic_sec_counter (void) +{ +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + + if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) && + (0 == clock_gettime (mono_clock_id , + &ts)) ) + return ts.tv_sec - mono_clock_start; +#endif /* HAVE_CLOCK_GETTIME */ +#ifdef HAVE_CLOCK_GET_TIME + if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) + { + mach_timespec_t cur_time; + + if (KERN_SUCCESS == clock_get_time(mono_clock_service, + &cur_time)) + return cur_time.tv_sec - mono_clock_start; + } +#endif /* HAVE_CLOCK_GET_TIME */ +#if defined(_WIN32) +#if _WIN32_WINNT >= 0x0600 + if (1) + return (time_t)(((uint64_t)(GetTickCount64() - tick_start)) / 1000); +#else /* _WIN32_WINNT < 0x0600 */ + if (0 != perf_freq) + { + LARGE_INTEGER perf_counter; + + QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ + return (time_t)(((uint64_t)(perf_counter.QuadPart - perf_start)) / perf_freq); + } +#endif /* _WIN32_WINNT < 0x0600 */ +#endif /* _WIN32 */ +#ifdef HAVE_GETHRTIME + if (1) + return (time_t)(((uint64_t) (gethrtime () - hrtime_start)) / 1000000000); +#endif /* HAVE_GETHRTIME */ + + return time (NULL) - sys_clock_start; +} diff --git a/src/lib/mhd_mono_clock.h b/src/lib/mhd_mono_clock.h @@ -0,0 +1,60 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2015 Karlson2k (Evgeny Grin) + + 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/mhd_mono_clock.h + * @brief internal monotonic clock functions declarations + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_MONO_CLOCK_H +#define MHD_MONO_CLOCK_H 1 +#include "mhd_options.h" + +#if defined(HAVE_TIME_H) +#include <time.h> +#elif defined(HAVE_SYS_TYPES_H) +#include <sys/types.h> +#endif + +/** + * Initialise monotonic seconds counter. + */ +void +MHD_monotonic_sec_counter_init(void); + + +/** + * Deinitialise monotonic seconds counter by freeing any allocated resources + */ +void +MHD_monotonic_sec_counter_finish(void); + + +/** + * Monotonic seconds counter, useful for timeout calculation. + * Tries to be not affected by manually setting the system real time + * clock or adjustments by NTP synchronization. + * + * @return number of seconds from some fixed moment + */ +time_t +MHD_monotonic_sec_counter(void); + +#endif /* MHD_MONO_CLOCK_H */ diff --git a/src/microhttpd/mhd_sockets.c b/src/lib/mhd_sockets.c diff --git a/src/lib/mhd_sockets.h b/src/lib/mhd_sockets.h @@ -0,0 +1,760 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2014-2016 Karlson2k (Evgeny Grin) + + 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/mhd_sockets.c + * @brief Header for platform-independent sockets abstraction + * @author Karlson2k (Evgeny Grin) + * + * Provides basic abstraction for sockets. + * Any functions can be implemented as macro on some platforms + * unless explicitly marked otherwise. + * Any function argument can be skipped in macro, so avoid + * variable modification in function parameters. + */ + +#ifndef MHD_SOCKETS_H +#define MHD_SOCKETS_H 1 +#include "mhd_options.h" + +#include <errno.h> + +#if !defined(MHD_POSIX_SOCKETS) && !defined(MHD_WINSOCK_SOCKETS) +# if !defined(_WIN32) || defined(__CYGWIN__) +# define MHD_POSIX_SOCKETS 1 +# else /* defined(_WIN32) && !defined(__CYGWIN__) */ +# define MHD_WINSOCK_SOCKETS 1 +# endif /* defined(_WIN32) && !defined(__CYGWIN__) */ +#endif /* !MHD_POSIX_SOCKETS && !MHD_WINSOCK_SOCKETS */ + +/* + * MHD require headers that define socket type, socket basic functions + * (socket(), accept(), listen(), bind(), send(), recv(), select()), socket + * parameters like SOCK_CLOEXEC, SOCK_NONBLOCK, additional socket functions + * (poll(), epoll(), accept4()), struct timeval and other types, required + * for socket function. + */ +#if defined(MHD_POSIX_SOCKETS) +# ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> /* required on old platforms */ +# endif +# ifdef HAVE_SYS_SOCKET_H +# include <sys/socket.h> +# endif +# if defined(__VXWORKS__) || defined(__vxworks) || defined(OS_VXWORKS) +# ifdef HAVE_SOCKLIB_H +# include <sockLib.h> +# endif /* HAVE_SOCKLIB_H */ +# ifdef HAVE_INETLIB_H +# include <inetLib.h> +# endif /* HAVE_INETLIB_H */ +# include <strings.h> /* required for FD_SET (bzero() function) */ +# endif /* __VXWORKS__ || __vxworks || OS_VXWORKS */ +# ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +# endif /* HAVE_NETINET_IN_H */ +# ifdef HAVE_ARPA_INET_H +# include <arpa/inet.h> +# endif +# ifdef HAVE_NET_IF_H +# include <net/if.h> +# endif +# ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +# endif +# ifdef HAVE_TIME_H +# include <time.h> +# endif +# ifdef HAVE_NETDB_H +# include <netdb.h> +# endif +# ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +# endif +# ifdef EPOLL_SUPPORT +# include <sys/epoll.h> +# endif +# ifdef HAVE_NETINET_TCP_H + /* for TCP_FASTOPEN and TCP_CORK */ +# include <netinet/tcp.h> +# endif +# ifdef HAVE_STRING_H +# include <string.h> /* for strerror() */ +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif /* !WIN32_LEAN_AND_MEAN */ +# include <winsock2.h> +# include <ws2tcpip.h> +#endif /* MHD_WINSOCK_SOCKETS */ + +#if defined(HAVE_POLL_H) && defined(HAVE_POLL) +# include <poll.h> +#endif + +#include <stddef.h> +#if defined(_MSC_FULL_VER) && !defined (_SSIZE_T_DEFINED) +# include <stdint.h> +# define _SSIZE_T_DEFINED + typedef intptr_t ssize_t; +#endif /* !_SSIZE_T_DEFINED */ + +#include "mhd_limits.h" + +#ifdef _MHD_FD_SETSIZE_IS_DEFAULT +# define _MHD_SYS_DEFAULT_FD_SETSIZE FD_SETSIZE +#else /* ! _MHD_FD_SETSIZE_IS_DEFAULT */ +# include "sysfdsetsize.h" +# define _MHD_SYS_DEFAULT_FD_SETSIZE get_system_fdsetsize_value() +#endif /* ! _MHD_FD_SETSIZE_IS_DEFAULT */ + +#ifndef MHD_PANIC +# include <stdio.h> +# include <stdlib.h> +/* Simple implementation of MHD_PANIC, to be used outside lib */ +# define MHD_PANIC(msg) do { fprintf (stderr, \ + "Abnormal termination at %d line in file %s: %s\n", \ + (int)__LINE__, __FILE__, msg); abort();} while(0) +#endif /* ! MHD_PANIC */ + +#ifndef MHD_SOCKET_DEFINED +/** + * MHD_socket is type for socket FDs + */ +# if defined(MHD_POSIX_SOCKETS) + typedef int MHD_socket; +# define MHD_INVALID_SOCKET (-1) +# elif defined(MHD_WINSOCK_SOCKETS) + typedef SOCKET MHD_socket; +# define MHD_INVALID_SOCKET (INVALID_SOCKET) +# endif /* MHD_WINSOCK_SOCKETS */ + +# define MHD_SOCKET_DEFINED 1 +#endif /* ! MHD_SOCKET_DEFINED */ + +#ifdef SOCK_CLOEXEC +# define MAYBE_SOCK_CLOEXEC SOCK_CLOEXEC +#else /* ! SOCK_CLOEXEC */ +# define MAYBE_SOCK_CLOEXEC 0 +#endif /* ! SOCK_CLOEXEC */ + +#ifdef HAVE_SOCK_NONBLOCK +# define MAYBE_SOCK_NONBLOCK SOCK_NONBLOCK +#else /* ! HAVE_SOCK_NONBLOCK */ +# define MAYBE_SOCK_NONBLOCK 0 +#endif /* ! HAVE_SOCK_NONBLOCK */ + +#ifdef MSG_NOSIGNAL +# define MAYBE_MSG_NOSIGNAL MSG_NOSIGNAL +#else /* ! MSG_NOSIGNAL */ +# define MAYBE_MSG_NOSIGNAL 0 +#endif /* ! MSG_NOSIGNAL */ + +#if !defined(SHUT_WR) && defined(SD_SEND) +# define SHUT_WR SD_SEND +#endif +#if !defined(SHUT_RD) && defined(SD_RECEIVE) +# define SHUT_RD SD_RECEIVE +#endif +#if !defined(SHUT_RDWR) && defined(SD_BOTH) +# define SHUT_RDWR SD_BOTH +#endif + +#if HAVE_ACCEPT4+0 != 0 && (defined(HAVE_SOCK_NONBLOCK) || defined(SOCK_CLOEXEC)) +# define USE_ACCEPT4 1 +#endif + +#if defined(HAVE_EPOLL_CREATE1) && defined(EPOLL_CLOEXEC) +# define USE_EPOLL_CREATE1 1 +#endif /* HAVE_EPOLL_CREATE1 && EPOLL_CLOEXEC */ + +#ifdef TCP_FASTOPEN +/** + * Default TCP fastopen queue size. + */ +#define MHD_TCP_FASTOPEN_QUEUE_SIZE_DEFAULT 10 +#endif + + +/** + * MHD_SCKT_OPT_BOOL_ is type for bool parameters for setsockopt()/getsockopt() + */ +#ifdef MHD_POSIX_SOCKETS + typedef int MHD_SCKT_OPT_BOOL_; +#else /* MHD_WINSOCK_SOCKETS */ + typedef BOOL MHD_SCKT_OPT_BOOL_; +#endif /* MHD_WINSOCK_SOCKETS */ + +/** + * MHD_SCKT_SEND_SIZE_ is type used to specify size for send and recv + * functions + */ +#if !defined(MHD_WINSOCK_SOCKETS) + typedef size_t MHD_SCKT_SEND_SIZE_; +#else + typedef int MHD_SCKT_SEND_SIZE_; +#endif + +/** + * MHD_SCKT_SEND_MAX_SIZE_ is maximum send()/recv() size value. + */ +#if !defined(MHD_WINSOCK_SOCKETS) +# define MHD_SCKT_SEND_MAX_SIZE_ SSIZE_MAX +#else +# define MHD_SCKT_SEND_MAX_SIZE_ INT_MAX +#endif + +/** + * MHD_socket_close_(fd) close any FDs (non-W32) / close only socket + * FDs (W32). Note that on HP-UNIX, this function may leak the FD if + * errno is set to EINTR. Do not use HP-UNIX. + * + * @param fd descriptor to close + * @return boolean true on success (error codes like EINTR and EIO are + * counted as success, only EBADF counts as an error!), + * boolean false otherwise. + */ +#if !defined(MHD_WINSOCK_SOCKETS) +# define MHD_socket_close_(fd) ((0 == close((fd))) || (EBADF != errno)) +#else +# define MHD_socket_close_(fd) (0 == closesocket((fd))) +#endif + +/** + * MHD_socket_close_chk_(fd) close socket and abort execution + * if error is detected. + * @param fd socket to close + */ +#define MHD_socket_close_chk_(fd) do { \ + if (!MHD_socket_close_(fd)) \ + MHD_PANIC(_("Close socket failed.\n")); \ + } while(0) + + +/** + * MHD_send_ is wrapper for system's send() + * @param s the socket to use + * @param b the buffer with data to send + * @param l the length of data in @a b + * @return ssize_t type value + */ +#define MHD_send_(s,b,l) \ + ((ssize_t)send((s),(const void*)(b),((MHD_SCKT_SEND_SIZE_)l), MAYBE_MSG_NOSIGNAL)) + + +/** + * MHD_recv_ is wrapper for system's recv() + * @param s the socket to use + * @param b the buffer for data to receive + * @param l the length of @a b + * @return ssize_t type value + */ +#define MHD_recv_(s,b,l) \ + ((ssize_t)recv((s),(void*)(b),((MHD_SCKT_SEND_SIZE_)l), 0)) + + +/** + * Check whether FD can be added to fd_set with specified FD_SETSIZE. + * @param fd the fd to check + * @param pset the pointer to fd_set to check or NULL to check + * whether FD can be used with fd_sets. + * @param setsize the value of FD_SETSIZE. + * @return boolean true if FD can be added to fd_set, + * boolean false otherwise. + */ +#if defined(MHD_POSIX_SOCKETS) +# define MHD_SCKT_FD_FITS_FDSET_SETSIZE_(fd,pset,setsize) ((fd) < ((MHD_socket)setsize)) +#elif defined(MHD_WINSOCK_SOCKETS) +# define MHD_SCKT_FD_FITS_FDSET_SETSIZE_(fd,pset,setsize) ( ((void*)(pset)==(void*)0) || \ + (((fd_set*)(pset))->fd_count < ((unsigned)setsize)) || \ + (FD_ISSET((fd),(pset))) ) +#endif + +/** + * Check whether FD can be added to fd_set with current FD_SETSIZE. + * @param fd the fd to check + * @param pset the pointer to fd_set to check or NULL to check + * whether FD can be used with fd_sets. + * @return boolean true if FD can be added to fd_set, + * boolean false otherwise. + */ +#define MHD_SCKT_FD_FITS_FDSET_(fd,pset) MHD_SCKT_FD_FITS_FDSET_SETSIZE_((fd),(pset),FD_SETSIZE) + +/** + * Add FD to fd_set with specified FD_SETSIZE. + * @param fd the fd to add + * @param pset the valid pointer to fd_set. + * @param setsize the value of FD_SETSIZE. + * @note To work on W32 with value of FD_SETSIZE different from currently defined value, + * system definition of FD_SET() is not used. + */ +#if defined(MHD_POSIX_SOCKETS) +# define MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd,pset,setsize) FD_SET((fd),(pset)) +#elif defined(MHD_WINSOCK_SOCKETS) +# define MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd,pset,setsize) \ + do { \ + u_int _i_ = 0; \ + fd_set* const _s_ = (fd_set*)(pset); \ + while((_i_ < _s_->fd_count) && ((fd) != _s_->fd_array[_i_])) {++_i_;} \ + if ((_i_ == _s_->fd_count)) {_s_->fd_array[_s_->fd_count++] = (fd);} \ + } while(0) +#endif + + /* MHD_SYS_select_ is wrapper macro for system select() function */ +#if !defined(MHD_WINSOCK_SOCKETS) +# define MHD_SYS_select_(n,r,w,e,t) select((n),(r),(w),(e),(t)) +#else +# define MHD_SYS_select_(n,r,w,e,t) \ +( ( (((void*)(r) == (void*)0) || ((fd_set*)(r))->fd_count == 0) && \ + (((void*)(w) == (void*)0) || ((fd_set*)(w))->fd_count == 0) && \ + (((void*)(e) == (void*)0) || ((fd_set*)(e))->fd_count == 0) ) ? \ + ( ((void*)(t) == (void*)0) ? 0 : \ + (Sleep(((struct timeval*)(t))->tv_sec * 1000 + \ + ((struct timeval*)(t))->tv_usec / 1000), 0) ) : \ + (select((int)0,(r),(w),(e),(t))) ) +#endif + +#if defined(HAVE_POLL) +/* MHD_sys_poll_ is wrapper macro for system poll() function */ +# if !defined(MHD_WINSOCK_SOCKETS) +# define MHD_sys_poll_ poll +# else /* MHD_WINSOCK_SOCKETS */ +# define MHD_sys_poll_ WSAPoll +# endif /* MHD_WINSOCK_SOCKETS */ + +# ifdef POLLPRI +# define MHD_POLLPRI_OR_ZERO POLLPRI +# else /* ! POLLPRI */ +# define MHD_POLLPRI_OR_ZERO 0 +# endif /* ! POLLPRI */ +# ifdef POLLRDBAND +# define MHD_POLLRDBAND_OR_ZERO POLLRDBAND +# else /* ! POLLRDBAND */ +# define MHD_POLLRDBAND_OR_ZERO 0 +# endif /* ! POLLRDBAND */ +# ifdef POLLNVAL +# define MHD_POLLNVAL_OR_ZERO POLLNVAL +# else /* ! POLLNVAL */ +# define MHD_POLLNVAL_OR_ZERO 0 +# endif /* ! POLLNVAL */ + +/* MHD_POLL_EVENTS_ERR_DISC is 'events' mask for errors and disconnect. + * Note: Out-of-band data is treated as error. */ +# if defined(_WIN32) && ! defined(__CYGWIN__) +# define MHD_POLL_EVENTS_ERR_DISC POLLRDBAND +# elif defined(__linux__) +# define MHD_POLL_EVENTS_ERR_DISC POLLPRI +# else /* ! __linux__ */ +# define MHD_POLL_EVENTS_ERR_DISC (MHD_POLLPRI_OR_ZERO | MHD_POLLRDBAND_OR_ZERO) +# endif /* ! __linux__ */ +/* MHD_POLL_REVENTS_ERR_DISC is 'revents' mask for errors and disconnect. + * Note: Out-of-band data is treated as error. */ +# define MHD_POLL_REVENTS_ERR_DISC \ + (MHD_POLLPRI_OR_ZERO | MHD_POLLRDBAND_OR_ZERO | MHD_POLLNVAL_OR_ZERO | POLLERR | POLLHUP) +/* MHD_POLL_REVENTS_ERRROR is 'revents' mask for errors. + * Note: Out-of-band data is treated as error. */ +# define MHD_POLL_REVENTS_ERRROR \ + (MHD_POLLPRI_OR_ZERO | MHD_POLLRDBAND_OR_ZERO | MHD_POLLNVAL_OR_ZERO | POLLERR) +#endif /* HAVE_POLL */ + +#define MHD_SCKT_MISSING_ERR_CODE_ 31450 + +#if defined(MHD_POSIX_SOCKETS) +# if defined(EAGAIN) +# define MHD_SCKT_EAGAIN_ EAGAIN +# elif defined(EWOULDBLOCK) +# define MHD_SCKT_EAGAIN_ EWOULDBLOCK +# else /* !EAGAIN && !EWOULDBLOCK */ +# define MHD_SCKT_EAGAIN_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* !EAGAIN && !EWOULDBLOCK */ +# if defined(EWOULDBLOCK) +# define MHD_SCKT_EWOULDBLOCK_ EWOULDBLOCK +# elif defined(EAGAIN) +# define MHD_SCKT_EWOULDBLOCK_ EAGAIN +# else /* !EWOULDBLOCK && !EAGAIN */ +# define MHD_SCKT_EWOULDBLOCK_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* !EWOULDBLOCK && !EAGAIN */ +# ifdef EINTR +# define MHD_SCKT_EINTR_ EINTR +# else /* ! EINTR */ +# define MHD_SCKT_EINTR_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! EINTR */ +# ifdef ECONNRESET +# define MHD_SCKT_ECONNRESET_ ECONNRESET +# else /* ! ECONNRESET */ +# define MHD_SCKT_ECONNRESET_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! ECONNRESET */ +# ifdef ECONNABORTED +# define MHD_SCKT_ECONNABORTED_ ECONNABORTED +# else /* ! ECONNABORTED */ +# define MHD_SCKT_ECONNABORTED_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! ECONNABORTED */ +# ifdef ENOTCONN +# define MHD_SCKT_ENOTCONN_ ENOTCONN +# else /* ! ENOTCONN */ +# define MHD_SCKT_ENOTCONN_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! ENOTCONN */ +# ifdef EMFILE +# define MHD_SCKT_EMFILE_ EMFILE +# else /* ! EMFILE */ +# define MHD_SCKT_EMFILE_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! EMFILE */ +# ifdef ENFILE +# define MHD_SCKT_ENFILE_ ENFILE +# else /* ! ENFILE */ +# define MHD_SCKT_ENFILE_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! ENFILE */ +# ifdef ENOMEM +# define MHD_SCKT_ENOMEM_ ENOMEM +# else /* ! ENOMEM */ +# define MHD_SCKT_ENOMEM_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! ENOMEM */ +# ifdef ENOBUFS +# define MHD_SCKT_ENOBUFS_ ENOBUFS +# else /* ! ENOBUFS */ +# define MHD_SCKT_ENOBUFS_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! ENOBUFS */ +# ifdef EBADF +# define MHD_SCKT_EBADF_ EBADF +# else /* ! EBADF */ +# define MHD_SCKT_EBADF_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! EBADF */ +# ifdef ENOTSOCK +# define MHD_SCKT_ENOTSOCK_ ENOTSOCK +# else /* ! ENOTSOCK */ +# define MHD_SCKT_ENOTSOCK_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! ENOTSOCK */ +# ifdef EINVAL +# define MHD_SCKT_EINVAL_ EINVAL +# else /* ! EINVAL */ +# define MHD_SCKT_EINVAL_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! EINVAL */ +# ifdef EFAULT +# define MHD_SCKT_EFAUL_ EFAULT +# else /* ! EFAULT */ +# define MHD_SCKT_EFAUL_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! EFAULT */ +# ifdef ENOSYS +# define MHD_SCKT_ENOSYS_ ENOSYS +# else /* ! ENOSYS */ +# define MHD_SCKT_ENOSYS_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! ENOSYS */ +# ifdef ENOTSUP +# define MHD_SCKT_ENOTSUP_ ENOTSUP +# else /* ! ENOTSUP */ +# define MHD_SCKT_ENOTSUP_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! ENOTSUP */ +# ifdef EOPNOTSUPP +# define MHD_SCKT_EOPNOTSUPP_ EOPNOTSUPP +# else /* ! EOPNOTSUPP */ +# define MHD_SCKT_EOPNOTSUPP_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! EOPNOTSUPP */ +# ifdef EACCES +# define MHD_SCKT_EACCESS_ EACCES +# else /* ! EACCES */ +# define MHD_SCKT_EACCESS_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! EACCES */ +# ifdef ENETDOWN +# define MHD_SCKT_ENETDOWN_ ENETDOWN +# else /* ! ENETDOWN */ +# define MHD_SCKT_ENETDOWN_ MHD_SCKT_MISSING_ERR_CODE_ +# endif /* ! ENETDOWN */ +#elif defined(MHD_WINSOCK_SOCKETS) +# define MHD_SCKT_EAGAIN_ WSAEWOULDBLOCK +# define MHD_SCKT_EWOULDBLOCK_ WSAEWOULDBLOCK +# define MHD_SCKT_EINTR_ WSAEINTR +# define MHD_SCKT_ECONNRESET_ WSAECONNRESET +# define MHD_SCKT_ECONNABORTED_ WSAECONNABORTED +# define MHD_SCKT_ENOTCONN_ WSAENOTCONN +# define MHD_SCKT_EMFILE_ WSAEMFILE +# define MHD_SCKT_ENFILE_ MHD_SCKT_MISSING_ERR_CODE_ +# define MHD_SCKT_ENOMEM_ MHD_SCKT_MISSING_ERR_CODE_ +# define MHD_SCKT_ENOBUFS_ WSAENOBUFS +# define MHD_SCKT_EBADF_ WSAEBADF +# define MHD_SCKT_ENOTSOCK_ WSAENOTSOCK +# define MHD_SCKT_EINVAL_ WSAEINVAL +# define MHD_SCKT_EFAUL_ WSAEFAULT +# define MHD_SCKT_ENOSYS_ MHD_SCKT_MISSING_ERR_CODE_ +# define MHD_SCKT_ENOTSUP_ MHD_SCKT_MISSING_ERR_CODE_ +# define MHD_SCKT_EOPNOTSUPP_ WSAEOPNOTSUPP +# define MHD_SCKT_EACCESS_ WSAEACCES +# define MHD_SCKT_ENETDOWN_ WSAENETDOWN +#endif + +/** + * MHD_socket_error_ return system native error code for last socket error. + * @return system error code for last socket error. + */ +#if defined(MHD_POSIX_SOCKETS) +# define MHD_socket_get_error_() (errno) +#elif defined(MHD_WINSOCK_SOCKETS) +# define MHD_socket_get_error_() WSAGetLastError() +#endif + +#ifdef MHD_WINSOCK_SOCKETS + /* POSIX-W32 sockets compatibility functions */ + +/** + * Return pointer to string description of specified WinSock error + * @param err the WinSock error code. + * @return pointer to string description of specified WinSock error. + */ + const char* MHD_W32_strerror_winsock_(int err); +#endif /* MHD_WINSOCK_SOCKETS */ + +/* MHD_socket_last_strerr_ is description string of specified socket error code */ +#if defined(MHD_POSIX_SOCKETS) +# define MHD_socket_strerr_(err) strerror((err)) +#elif defined(MHD_WINSOCK_SOCKETS) +# define MHD_socket_strerr_(err) MHD_W32_strerror_winsock_((err)) +#endif + +/* MHD_socket_last_strerr_ is description string of last errno (non-W32) / + * description string of last socket error (W32) */ +#define MHD_socket_last_strerr_() MHD_socket_strerr_(MHD_socket_get_error_()) + +/** + * MHD_socket_fset_error_() set socket system native error code. + */ +#if defined(MHD_POSIX_SOCKETS) +# define MHD_socket_fset_error_(err) (errno = (err)) +#elif defined(MHD_WINSOCK_SOCKETS) +# define MHD_socket_fset_error_(err) (WSASetLastError((err))) +#endif + +/** + * MHD_socket_try_set_error_() set socket system native error code if + * specified code is defined on system. + * @return non-zero if specified @a err code is defined on system + * and error was set; + * zero if specified @a err code is not defined on system + * and error was not set. + */ +#define MHD_socket_try_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ != (err)) ? \ + (MHD_socket_fset_error_((err)), !0) : 0 ) + +/** + * MHD_socket_set_error_() set socket system native error code to + * specified code or replacement code if specified code is not + * defined on system. + */ +#if defined(MHD_POSIX_SOCKETS) +# if defined(ENOSYS) +# define MHD_socket_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ == (err)) ? \ + (errno = ENOSYS) : (errno = (err)) ) +# elif defined(EOPNOTSUPP) +# define MHD_socket_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ == (err)) ? \ + (errno = EOPNOTSUPP) : (errno = (err)) ) +# elif defined (EFAULT) +# define MHD_socket_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ == (err)) ? \ + (errno = EFAULT) : (errno = (err)) ) +# elif defined (EINVAL) +# define MHD_socket_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ == (err)) ? \ + (errno = EINVAL) : (errno = (err)) ) +# else /* !EOPNOTSUPP && !EFAULT && !EINVAL */ +# warning No suitable replacement for missing socket error code is found. Edit this file and add replacement code which is defined on system. +# define MHD_socket_set_error_(err) (errno = (err)) +# endif /* !EOPNOTSUPP && !EFAULT && !EINVAL*/ +#elif defined(MHD_WINSOCK_SOCKETS) +# define MHD_socket_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ == (err)) ? \ + (WSASetLastError((WSAEOPNOTSUPP))) : \ + (WSASetLastError((err))) ) +#endif + +/** + * Check whether given socket error is equal to specified system + * native MHD_SCKT_E*_ code. + * If platform don't have specific error code, result is + * always boolean false. + * @return boolean true if @a code is real error code and + * @a err equals to MHD_SCKT_E*_ @a code; + * boolean false otherwise + */ +#define MHD_SCKT_ERR_IS_(err,code) ( (MHD_SCKT_MISSING_ERR_CODE_ != (code)) && \ + ((code) == (err)) ) + +/** + * Check whether last socket error is equal to specified system + * native MHD_SCKT_E*_ code. + * If platform don't have specific error code, result is + * always boolean false. + * @return boolean true if @a code is real error code and + * last socket error equals to MHD_SCKT_E*_ @a code; + * boolean false otherwise + */ +#define MHD_SCKT_LAST_ERR_IS_(code) MHD_SCKT_ERR_IS_(MHD_socket_get_error_() ,(code)) + +/* Specific error code checks */ + +/** + * Check whether given socket error is equal to system's + * socket error codes for EINTR. + * @return boolean true if @a err is equal to sockets' EINTR code; + * boolean false otherwise. + */ +#define MHD_SCKT_ERR_IS_EINTR_(err) MHD_SCKT_ERR_IS_((err),MHD_SCKT_EINTR_) + +/** + * Check whether given socket error is equal to system's + * socket error codes for EAGAIN or EWOULDBLOCK. + * @return boolean true if @a err is equal to sockets' EAGAIN or EWOULDBLOCK codes; + * boolean false otherwise. + */ +#if MHD_SCKT_EAGAIN_ == MHD_SCKT_EWOULDBLOCK_ +# define MHD_SCKT_ERR_IS_EAGAIN_(err) MHD_SCKT_ERR_IS_((err),MHD_SCKT_EAGAIN_) +#else /* MHD_SCKT_EAGAIN_ != MHD_SCKT_EWOULDBLOCK_ */ +# define MHD_SCKT_ERR_IS_EAGAIN_(err) ( MHD_SCKT_ERR_IS_((err),MHD_SCKT_EAGAIN_) || \ + MHD_SCKT_ERR_IS_((err),MHD_SCKT_EWOULDBLOCK_) ) +#endif /* MHD_SCKT_EAGAIN_ != MHD_SCKT_EWOULDBLOCK_ */ + +/** + * Check whether given socket error is any kind of "low resource" error. + * @return boolean true if @a err is any kind of "low resource" error, + * boolean false otherwise. + */ +#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err) ( MHD_SCKT_ERR_IS_((err),MHD_SCKT_EMFILE_) || \ + MHD_SCKT_ERR_IS_((err),MHD_SCKT_ENFILE_) || \ + MHD_SCKT_ERR_IS_((err),MHD_SCKT_ENOMEM_) || \ + MHD_SCKT_ERR_IS_((err),MHD_SCKT_ENOBUFS_) ) + +/** + * Check whether is given socket error is type of "incoming connection + * was disconnected before 'accept()' is called". + * @return boolean true is @a err match described socket error code, + * boolean false otherwise. + */ +#if defined(MHD_POSIX_SOCKETS) +# define MHD_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT_(err) MHD_SCKT_ERR_IS_((err),MHD_SCKT_ECONNABORTED_) +#elif defined(MHD_WINSOCK_SOCKETS) +# define MHD_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT_(err) MHD_SCKT_ERR_IS_((err),MHD_SCKT_ECONNRESET_) +#endif + +/** + * Check whether is given socket error is type of "connection was terminated + * by remote side". + * @return boolean true is @a err match described socket error code, + * boolean false otherwise. + */ +#define MHD_SCKT_ERR_IS_REMOTE_DISCNN_(err) ( MHD_SCKT_ERR_IS_((err),MHD_SCKT_ECONNRESET_) || \ + MHD_SCKT_ERR_IS_((err),MHD_SCKT_ECONNABORTED_)) + +/* Specific error code set */ + +/** + * Set socket's error code to ENOMEM or equivalent if ENOMEM is not + * available on platform. + */ +#if MHD_SCKT_MISSING_ERR_CODE_ != MHD_SCKT_ENOMEM_ +# define MHD_socket_set_error_to_ENOMEM() MHD_socket_set_error_(MHD_SCKT_ENOMEM_) +#elif MHD_SCKT_MISSING_ERR_CODE_ != MHD_SCKT_ENOBUFS_ +# define MHD_socket_set_error_to_ENOMEM() MHD_socket_set_error_(MHD_SCKT_ENOBUFS_) +#else +# warning No suitable replacement for ENOMEM error codes is found. Edit this file and add replacement code which is defined on system. +# define MHD_socket_set_error_to_ENOMEM() MHD_socket_set_error_(MHD_SCKT_ENOMEM_) +#endif + +/* Socket functions */ + +#if defined(AF_LOCAL) +# define MHD_SCKT_LOCAL AF_LOCAL +#elif defined(AF_UNIX) +# define MHD_SCKT_LOCAL AF_UNIX +#endif /* AF_UNIX */ + +#if defined(MHD_POSIX_SOCKETS) && defined(MHD_SCKT_LOCAL) +# define MHD_socket_pair_(fdarr) (!socketpair(MHD_SCKT_LOCAL, SOCK_STREAM, 0, (fdarr))) +# if defined(HAVE_SOCK_NONBLOCK) +# define MHD_socket_pair_nblk_(fdarr) (!socketpair(MHD_SCKT_LOCAL, SOCK_STREAM | SOCK_NONBLOCK, 0, (fdarr))) +# endif /* HAVE_SOCK_NONBLOCK*/ +#elif defined(MHD_WINSOCK_SOCKETS) + /** + * Create pair of mutually connected TCP/IP sockets on loopback address + * @param sockets_pair array to receive resulted sockets + * @param non_blk if set to non-zero value, sockets created in non-blocking mode + * otherwise sockets will be in blocking mode + * @return non-zero if succeeded, zero otherwise + */ + int MHD_W32_socket_pair_(SOCKET sockets_pair[2], int non_blk); + +# define MHD_socket_pair_(fdarr) MHD_W32_socket_pair_((fdarr), 0) +# define MHD_socket_pair_nblk_(fdarr) MHD_W32_socket_pair_((fdarr), 1) +#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 non-zero if succeeded, zero otherwise + */ +int +MHD_add_to_fd_set_ (MHD_socket fd, + fd_set *set, + MHD_socket *max_fd, + unsigned int fd_setsize); + + +/** + * Change socket options to be non-blocking. + * + * @param sock socket to manipulate + * @return non-zero if succeeded, zero otherwise + */ +int +MHD_socket_nonblocking_ (MHD_socket sock); + + +/** + * Change socket options to be non-inheritable. + * + * @param sock socket to manipulate + * @return non-zero if succeeded, zero otherwise + * @warning Does not set socket error on W32. + */ +int +MHD_socket_noninheritable_ (MHD_socket sock); + + +#if defined(SOL_SOCKET) && defined(SO_NOSIGPIPE) + static const int _MHD_socket_int_one = 1; +/** + * Change socket options to no signal on remote disconnect. + * + * @param sock socket to manipulate + * @return non-zero if succeeded, zero otherwise + */ +# define MHD_socket_nosignal_(sock) \ + (!setsockopt((sock),SOL_SOCKET,SO_NOSIGPIPE,&_MHD_socket_int_one,sizeof(_MHD_socket_int_one))) +#endif /* SOL_SOCKET && SO_NOSIGPIPE */ + +/** + * Create a listen socket, with noninheritable flag if possible. + * + * @param use_ipv6 if set to non-zero IPv6 is used + * @return created socket or MHD_INVALID_SOCKET in case of errors + */ +MHD_socket +MHD_socket_create_listen_ (bool use_ipv6); + +#endif /* ! MHD_SOCKETS_H */ diff --git a/src/lib/mhd_str.c b/src/lib/mhd_str.c @@ -0,0 +1,758 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2015, 2016 Karlson2k (Evgeny Grin) + + 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/mhd_str.c + * @brief Functions implementations for string manipulating + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_str.h" + +#ifdef HAVE_STDBOOL_H +#include <stdbool.h> +#endif + +#include "mhd_limits.h" + +#ifdef MHD_FAVOR_SMALL_CODE +#ifdef _MHD_static_inline +#undef _MHD_static_inline +#endif /* _MHD_static_inline */ +/* Do not force inlining and do not use macro functions, use normal static + functions instead. + This may give more flexibility for size optimizations. */ +#define _MHD_static_inline static +#ifndef INLINE_FUNC +#define INLINE_FUNC 1 +#endif /* !INLINE_FUNC */ +#endif /* MHD_FAVOR_SMALL_CODE */ + +/* + * Block of functions/macros that use US-ASCII charset as required by HTTP + * standards. Not affected by current locale settings. + */ + +#ifdef INLINE_FUNC + +#if 0 /* Disable unused functions. */ +/** + * Check whether character is lower case letter in US-ASCII + * + * @param c character to check + * @return non-zero if character is lower case letter, zero otherwise + */ +_MHD_static_inline bool +isasciilower (char c) +{ + return (c >= 'a') && (c <= 'z'); +} +#endif /* Disable unused functions. */ + + +/** + * Check whether character is upper case letter in US-ASCII + * + * @param c character to check + * @return non-zero if character is upper case letter, zero otherwise + */ +_MHD_static_inline bool +isasciiupper (char c) +{ + return (c >= 'A') && (c <= 'Z'); +} + + +#if 0 /* Disable unused functions. */ +/** + * Check whether character is letter in US-ASCII + * + * @param c character to check + * @return non-zero if character is letter in US-ASCII, zero otherwise + */ +_MHD_static_inline bool +isasciialpha (char c) +{ + return isasciilower (c) || isasciiupper (c); +} +#endif /* Disable unused functions. */ + + +/** + * Check whether character is decimal digit in US-ASCII + * + * @param c character to check + * @return non-zero if character is decimal digit, zero otherwise + */ +_MHD_static_inline bool +isasciidigit (char c) +{ + return (c >= '0') && (c <= '9'); +} + +#if 0 /* Disable unused functions. */ +/** + * Check whether character is hexadecimal digit in US-ASCII + * + * @param c character to check + * @return non-zero if character is decimal digit, zero otherwise + */ +_MHD_static_inline bool +isasciixdigit (char c) +{ + return isasciidigit (c) || + ( (c >= 'A') && (c <= 'F') ) || + ( (c >= 'a') && (c <= 'f') ); +} + + +/** + * Check whether character is decimal digit or letter in US-ASCII + * + * @param c character to check + * @return non-zero if character is decimal digit or letter, zero otherwise + */ +_MHD_static_inline bool +isasciialnum (char c) +{ + return isasciialpha (c) || isasciidigit (c); +} +#endif /* Disable unused functions. */ + + +/** + * Convert US-ASCII character to lower case. + * If character is upper case letter in US-ASCII than it's converted to lower + * case analog. If character is NOT upper case letter than it's returned + * unmodified. + * + * @param c character to convert + * @return converted to lower case character + */ +_MHD_static_inline char +toasciilower (char c) +{ + return isasciiupper (c) ? (c - 'A' + 'a') : c; +} + + +#if 0 /* Disable unused functions. */ +/** + * Convert US-ASCII character to upper case. + * If character is lower case letter in US-ASCII than it's converted to upper + * case analog. If character is NOT lower case letter than it's returned + * unmodified. + * + * @param c character to convert + * @return converted to upper case character + */ +_MHD_static_inline char +toasciiupper (char c) +{ + return isasciilower (c) ? (c - 'a' + 'A') : c; +} +#endif /* Disable unused functions. */ + + +#if defined(MHD_FAVOR_SMALL_CODE) /* Used only in MHD_str_to_uvalue_n_() */ +/** + * Convert US-ASCII decimal digit to its value. + * + * @param c character to convert + * @return value of decimal digit or -1 if @ c is not decimal digit + */ +_MHD_static_inline int +todigitvalue (char c) +{ + if (isasciidigit (c)) + return (unsigned char)(c - '0'); + + return -1; +} +#endif /* MHD_FAVOR_SMALL_CODE */ + + +/** + * Convert US-ASCII hexadecimal digit to its value. + * + * @param c character to convert + * @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit + */ +_MHD_static_inline int +toxdigitvalue (char c) +{ + if (isasciidigit (c)) + return (unsigned char)(c - '0'); + if ( (c >= 'A') && (c <= 'F') ) + return (unsigned char)(c - 'A' + 10); + if ( (c >= 'a') && (c <= 'f') ) + return (unsigned char)(c - 'a' + 10); + + return -1; +} +#else /* !INLINE_FUNC */ + + +/** + * Checks whether character is lower case letter in US-ASCII + * + * @param c character to check + * @return boolean true if character is lower case letter, + * boolean false otherwise + */ +#define isasciilower(c) (((char)(c)) >= 'a' && ((char)(c)) <= 'z') + + +/** + * Checks whether character is upper case letter in US-ASCII + * + * @param c character to check + * @return boolean true if character is upper case letter, + * boolean false otherwise + */ +#define isasciiupper(c) (((char)(c)) >= 'A' && ((char)(c)) <= 'Z') + + +/** + * Checks whether character is letter in US-ASCII + * + * @param c character to check + * @return boolean true if character is letter, boolean false + * otherwise + */ +#define isasciialpha(c) (isasciilower(c) || isasciiupper(c)) + + +/** + * Check whether character is decimal digit in US-ASCII + * + * @param c character to check + * @return boolean true if character is decimal digit, boolean false + * otherwise + */ +#define isasciidigit(c) (((char)(c)) >= '0' && ((char)(c)) <= '9') + + +/** + * Check whether character is hexadecimal digit in US-ASCII + * + * @param c character to check + * @return boolean true if character is hexadecimal digit, + * boolean false otherwise + */ +#define isasciixdigit(c) (isasciidigit((c)) || \ + (((char)(c)) >= 'A' && ((char)(c)) <= 'F') || \ + (((char)(c)) >= 'a' && ((char)(c)) <= 'f') ) + + +/** + * Check whether character is decimal digit or letter in US-ASCII + * + * @param c character to check + * @return boolean true if character is decimal digit or letter, + * boolean false otherwise + */ +#define isasciialnum(c) (isasciialpha(c) || isasciidigit(c)) + + +/** + * Convert US-ASCII character to lower case. + * If character is upper case letter in US-ASCII than it's converted to lower + * case analog. If character is NOT upper case letter than it's returned + * unmodified. + * + * @param c character to convert + * @return converted to lower case character + */ +#define toasciilower(c) ((isasciiupper(c)) ? (((char)(c)) - 'A' + 'a') : ((char)(c))) + + +/** + * Convert US-ASCII character to upper case. + * If character is lower case letter in US-ASCII than it's converted to upper + * case analog. If character is NOT lower case letter than it's returned + * unmodified. + * + * @param c character to convert + * @return converted to upper case character + */ +#define toasciiupper(c) ((isasciilower(c)) ? (((char)(c)) - 'a' + 'A') : ((char)(c))) + + +/** + * Convert US-ASCII decimal digit to its value. + * + * @param c character to convert + * @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit + */ +#define todigitvalue(c) (isasciidigit(c) ? (int)(((char)(c)) - '0') : (int)(-1)) + + +/** + * Convert US-ASCII hexadecimal digit to its value. + * @param c character to convert + * @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit + */ +#define toxdigitvalue(c) ( isasciidigit(c) ? (int)(((char)(c)) - '0') : \ + ( (((char)(c)) >= 'A' && ((char)(c)) <= 'F') ? \ + (int)(((unsigned char)(c)) - 'A' + 10) : \ + ( (((char)(c)) >= 'a' && ((char)(c)) <= 'f') ? \ + (int)(((unsigned char)(c)) - 'a' + 10) : (int)(-1) ))) +#endif /* !INLINE_FUNC */ + + +#ifndef MHD_FAVOR_SMALL_CODE +/** + * Check two string for equality, ignoring case of US-ASCII letters. + * + * @param str1 first string to compare + * @param str2 second string to compare + * @return non-zero if two strings are equal, zero otherwise. + */ +int +MHD_str_equal_caseless_ (const char * str1, + const char * str2) +{ + while (0 != (*str1)) + { + const char c1 = *str1; + const char c2 = *str2; + if ( (c1 != c2) && + (toasciilower (c1) != toasciilower (c2)) ) + return 0; + str1++; + str2++; + } + return 0 == (*str2); +} +#endif /* ! MHD_FAVOR_SMALL_CODE */ + + +/** + * Check two string for equality, ignoring case of US-ASCII letters and + * checking not more than @a maxlen characters. + * Compares up to first terminating null character, but not more than + * first @a maxlen characters. + * + * @param str1 first string to compare + * @param str2 second string to compare + * @param maxlen maximum number of characters to compare + * @return non-zero if two strings are equal, zero otherwise. + */ +int +MHD_str_equal_caseless_n_ (const char * const str1, + const char * const str2, + size_t maxlen) +{ + size_t i; + + for (i = 0; i < maxlen; ++i) + { + const char c1 = str1[i]; + const char c2 = str2[i]; + if (0 == c2) + return 0 == c1; + if ( (c1 != c2) && + (toasciilower (c1) != toasciilower (c2)) ) + return 0; + } + return !0; +} + + +/** + * Check whether @a str has case-insensitive @a token. + * Token could be surrounded by spaces and tabs and delimited by comma. + * Match succeed if substring between start, end (of string) or comma + * contains only case-insensitive token and optional spaces and tabs. + * @warning token must not contain null-charters except optional + * terminating null-character. + * @param str the string to check + * @param token the token to find + * @param token_len length of token, not including optional terminating + * null-character. + * @return non-zero if two strings are equal, zero otherwise. + */ +bool +MHD_str_has_token_caseless_ (const char * str, + const char * const token, + size_t token_len) +{ + if (0 == token_len) + return false; + + while (0 != *str) + { + size_t i; + /* Skip all whitespaces and empty tokens. */ + while (' ' == *str || '\t' == *str || ',' == *str) str++; + + /* Check for token match. */ + i = 0; + while (1) + { + const char sc = *(str++); + const char tc = token[i++]; + + if (0 == sc) + return false; + if ( (sc != tc) && + (toasciilower (sc) != toasciilower (tc)) ) + break; + if (i >= token_len) + { + /* Check whether substring match token fully or + * has additional unmatched chars at tail. */ + while (' ' == *str || '\t' == *str) str++; + /* End of (sub)string? */ + if (0 == *str || ',' == *str) + return true; + /* Unmatched chars at end of substring. */ + break; + } + } + /* Find next substring. */ + while (0 != *str && ',' != *str) str++; + } + return false; +} + +#ifndef MHD_FAVOR_SMALL_CODE +/* Use individual function for each case */ + +/** + * Convert decimal US-ASCII digits in string to number in uint64_t. + * Conversion stopped at first non-digit character. + * + * @param str string to convert + * @param[out] out_val pointer to uint64_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint64_t or @a out_val is NULL + */ +size_t +MHD_str_to_uint64_ (const char *str, + uint64_t *out_val) +{ + const char * const start = str; + uint64_t res; + + if (!str || !out_val || !isasciidigit(str[0])) + return 0; + + res = 0; + do + { + const int digit = (unsigned char)(*str) - '0'; + if ( (res > (UINT64_MAX / 10)) || + ( (res == (UINT64_MAX / 10)) && + ((uint64_t)digit > (UINT64_MAX % 10)) ) ) + return 0; + + res *= 10; + res += digit; + str++; + } while (isasciidigit (*str)); + + *out_val = res; + return str - start; +} + + +/** + * Convert not more then @a maxlen decimal US-ASCII digits in string to + * number in uint64_t. + * Conversion stopped at first non-digit character or after @a maxlen + * digits. + * + * @param str string to convert + * @param maxlen maximum number of characters to process + * @param[out] out_val pointer to uint64_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint64_t or @a out_val is NULL + */ +size_t +MHD_str_to_uint64_n_ (const char * str, + size_t maxlen, + uint64_t *out_val) +{ + uint64_t res; + size_t i; + + if (!str || !maxlen || !out_val || !isasciidigit (str[0])) + return 0; + + res = 0; + i = 0; + do + { + const int digit = (unsigned char)str[i] - '0'; + + if ( (res > (UINT64_MAX / 10)) || + ( (res == (UINT64_MAX / 10)) && + ((uint64_t)digit > (UINT64_MAX % 10)) ) ) + return 0; + + res *= 10; + res += digit; + i++; + } while ( (i < maxlen) && + isasciidigit (str[i]) ); + + *out_val= res; + return i; +} + + +/** + * Convert hexadecimal US-ASCII digits in string to number in uint32_t. + * Conversion stopped at first non-digit character. + * + * @param str string to convert + * @param[out] out_val pointer to uint32_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint32_t or @a out_val is NULL + */ +size_t +MHD_strx_to_uint32_ (const char * str, + uint32_t *out_val) +{ + const char * const start = str; + uint32_t res; + int digit; + + if (!str || !out_val) + return 0; + + res = 0; + digit = toxdigitvalue (*str); + while (digit >= 0) + { + if ( (res < (UINT32_MAX / 16)) || + (res == (UINT32_MAX / 16) && (uint32_t)digit <= (UINT32_MAX % 16)) ) + { + res *= 16; + res += digit; + } + else + return 0; + str++; + digit = toxdigitvalue (*str); + } + + if (str - start > 0) + *out_val = res; + return str - start; +} + + +/** + * Convert not more then @a maxlen hexadecimal US-ASCII digits in string + * to number in uint32_t. + * Conversion stopped at first non-digit character or after @a maxlen + * digits. + * + * @param str string to convert + * @param maxlen maximum number of characters to process + * @param[out] out_val pointer to uint32_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint32_t or @a out_val is NULL + */ +size_t +MHD_strx_to_uint32_n_ (const char *str, + size_t maxlen, + uint32_t *out_val) +{ + size_t i; + uint32_t res; + int digit; + if (!str || !out_val) + return 0; + + res = 0; + i = 0; + while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0) + { + if ( (res > (UINT32_MAX / 16)) || + (res == (UINT32_MAX / 16) && (uint32_t)digit > (UINT32_MAX % 16)) ) + return 0; + + res *= 16; + res += digit; + i++; + } + + if (i) + *out_val = res; + return i; +} + + +/** + * Convert hexadecimal US-ASCII digits in string to number in uint64_t. + * Conversion stopped at first non-digit character. + * + * @param str string to convert + * @param[out] out_val pointer to uint64_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint64_t or @a out_val is NULL + */ +size_t +MHD_strx_to_uint64_ (const char *str, + uint64_t *out_val) +{ + const char * const start = str; + uint64_t res; + int digit; + if (!str || !out_val) + return 0; + + res = 0; + digit = toxdigitvalue (*str); + while (digit >= 0) + { + if ( (res < (UINT64_MAX / 16)) || + (res == (UINT64_MAX / 16) && (uint64_t)digit <= (UINT64_MAX % 16)) ) + { + res *= 16; + res += digit; + } + else + return 0; + str++; + digit = toxdigitvalue (*str); + } + + if (str - start > 0) + *out_val = res; + return str - start; +} + + +/** + * Convert not more then @a maxlen hexadecimal US-ASCII digits in string + * to number in uint64_t. + * Conversion stopped at first non-digit character or after @a maxlen + * digits. + * + * @param str string to convert + * @param maxlen maximum number of characters to process + * @param[out] out_val pointer to uint64_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint64_t or @a out_val is NULL + */ +size_t +MHD_strx_to_uint64_n_ (const char * str, + size_t maxlen, + uint64_t *out_val) +{ + size_t i; + uint64_t res; + int digit; + if (!str || !out_val) + return 0; + + res = 0; + i = 0; + while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0) + { + if ( (res > (UINT64_MAX / 16)) || + (res == (UINT64_MAX / 16) && (uint64_t)digit > (UINT64_MAX % 16)) ) + return 0; + + res *= 16; + res += digit; + i++; + } + + if (i) + *out_val = res; + return i; +} + +#else /* MHD_FAVOR_SMALL_CODE */ + +/** + * Generic function for converting not more then @a maxlen + * hexadecimal or decimal US-ASCII digits in string to number. + * Conversion stopped at first non-digit character or after @a maxlen + * digits. + * To be used only within macro. + * + * @param str the string to convert + * @param maxlen the maximum number of characters to process + * @param out_val the pointer to variable to store result of conversion + * @param val_size the size of variable pointed by @a out_val, in bytes, 4 or 8 + * @param max_val the maximum decoded number + * @param base the numeric base, 10 or 16 + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then @max_val, @val_size is not 16/32 or @a out_val is NULL + */ +size_t +MHD_str_to_uvalue_n_ (const char *str, + size_t maxlen, + void * out_val, + size_t val_size, + uint64_t max_val, + int base) +{ + size_t i; + uint64_t res; + int digit; + const uint64_t max_v_div_b = max_val / base; + const uint64_t max_v_mod_b = max_val % base; + /* 'digit->value' must be function, not macro */ + int (*const dfunc)(char) = (base == 16) ? + toxdigitvalue : todigitvalue; + + if ( !str || !out_val || + (base != 16 && base != 10) ) + return 0; + + res = 0; + i = 0; + while (maxlen > i && 0 <= (digit = dfunc (str[i]))) + { + if ( ((max_v_div_b) < res) || + ((max_v_div_b) == res && (max_v_mod_b) < (uint64_t)digit) ) + return 0; + + res *= base; + res += digit; + i++; + } + + if (i) + { + if (8 == val_size) + *(uint64_t*)out_val = res; + else if (4 == val_size) + *(uint32_t*)out_val = (uint32_t)res; + else + return 0; + } + return i; +} +#endif /* MHD_FAVOR_SMALL_CODE */ diff --git a/src/lib/mhd_str.h b/src/lib/mhd_str.h @@ -0,0 +1,266 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2015, 2016 Karlson2k (Evgeny Grin) + + 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/mhd_str.h + * @brief Header for string manipulating helpers + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_STR_H +#define MHD_STR_H 1 + +#include "mhd_options.h" + +#include <stdint.h> +#include <stdlib.h> +#ifdef HAVE_STDBOOL_H +#include <stdbool.h> +#endif /* HAVE_STDBOOL_H */ + +#ifdef MHD_FAVOR_SMALL_CODE +#include "mhd_limits.h" +#endif /* MHD_FAVOR_SMALL_CODE */ + +#ifndef MHD_STATICSTR_LEN_ +/** + * Determine length of static string / macro strings at compile time. + */ +#define MHD_STATICSTR_LEN_(macro) (sizeof(macro)/sizeof(char) - 1) +#endif /* ! MHD_STATICSTR_LEN_ */ + +/* + * Block of functions/macros that use US-ASCII charset as required by HTTP + * standards. Not affected by current locale settings. + */ + +#ifndef MHD_FAVOR_SMALL_CODE +/** + * Check two string for equality, ignoring case of US-ASCII letters. + * @param str1 first string to compare + * @param str2 second string to compare + * @return non-zero if two strings are equal, zero otherwise. + */ +int +MHD_str_equal_caseless_ (const char * str1, + const char * str2); +#else /* MHD_FAVOR_SMALL_CODE */ +/* Reuse MHD_str_equal_caseless_n_() to reduce size */ +#define MHD_str_equal_caseless_(s1,s2) MHD_str_equal_caseless_n_((s1),(s2), SIZE_MAX) +#endif /* MHD_FAVOR_SMALL_CODE */ + + +/** + * Check two string for equality, ignoring case of US-ASCII letters and + * checking not more than @a maxlen characters. + * Compares up to first terminating null character, but not more than + * first @a maxlen characters. + * @param str1 first string to compare + * @param str2 second string to compare + * @param maxlen maximum number of characters to compare + * @return non-zero if two strings are equal, zero otherwise. + */ +int +MHD_str_equal_caseless_n_ (const char * const str1, + const char * const str2, + size_t maxlen); + + +/** + * Check whether @a str has case-insensitive @a token. + * Token could be surrounded by spaces and tabs and delimited by comma. + * Match succeed if substring between start, end of string or comma + * contains only case-insensitive token and optional spaces and tabs. + * @warning token must not contain null-charters except optional + * terminating null-character. + * @param str the string to check + * @param token the token to find + * @param token_len length of token, not including optional terminating + * null-character. + * @return non-zero if two strings are equal, zero otherwise. + */ +bool +MHD_str_has_token_caseless_ (const char * str, + const char * const token, + size_t token_len); + +/** + * Check whether @a str has case-insensitive static @a tkn. + * Token could be surrounded by spaces and tabs and delimited by comma. + * Match succeed if substring between start, end of string or comma + * contains only case-insensitive token and optional spaces and tabs. + * @warning tkn must be static string + * @param str the string to check + * @param tkn the static string of token to find + * @return non-zero if two strings are equal, zero otherwise. + */ +#define MHD_str_has_s_token_caseless_(str,tkn) \ + MHD_str_has_token_caseless_((str),(tkn),MHD_STATICSTR_LEN_(tkn)) + +#ifndef MHD_FAVOR_SMALL_CODE +/* Use individual function for each case to improve speed */ + +/** + * Convert decimal US-ASCII digits in string to number in uint64_t. + * Conversion stopped at first non-digit character. + * @param str string to convert + * @param out_val pointer to uint64_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint64_t or @a out_val is NULL + */ +size_t +MHD_str_to_uint64_ (const char * str, + uint64_t * out_val); + +/** + * Convert not more then @a maxlen decimal US-ASCII digits in string to + * number in uint64_t. + * Conversion stopped at first non-digit character or after @a maxlen + * digits. + * @param str string to convert + * @param maxlen maximum number of characters to process + * @param out_val pointer to uint64_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint64_t or @a out_val is NULL + */ +size_t +MHD_str_to_uint64_n_ (const char * str, + size_t maxlen, + uint64_t * out_val); + + +/** + * Convert hexadecimal US-ASCII digits in string to number in uint32_t. + * Conversion stopped at first non-digit character. + * @param str string to convert + * @param out_val pointer to uint32_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint32_t or @a out_val is NULL + */ +size_t +MHD_strx_to_uint32_ (const char * str, + uint32_t * out_val); + + +/** + * Convert not more then @a maxlen hexadecimal US-ASCII digits in string + * to number in uint32_t. + * Conversion stopped at first non-digit character or after @a maxlen + * digits. + * @param str string to convert + * @param maxlen maximum number of characters to process + * @param out_val pointer to uint32_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint32_t or @a out_val is NULL + */ +size_t +MHD_strx_to_uint32_n_ (const char * str, + size_t maxlen, + uint32_t * out_val); + + +/** + * Convert hexadecimal US-ASCII digits in string to number in uint64_t. + * Conversion stopped at first non-digit character. + * @param str string to convert + * @param out_val pointer to uint64_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint64_t or @a out_val is NULL + */ +size_t +MHD_strx_to_uint64_ (const char * str, + uint64_t * out_val); + + +/** + * Convert not more then @a maxlen hexadecimal US-ASCII digits in string + * to number in uint64_t. + * Conversion stopped at first non-digit character or after @a maxlen + * digits. + * @param str string to convert + * @param maxlen maximum number of characters to process + * @param out_val pointer to uint64_t to store result of conversion + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then possible to store in uint64_t or @a out_val is NULL + */ +size_t +MHD_strx_to_uint64_n_ (const char * str, + size_t maxlen, + uint64_t * out_val); + +#else /* MHD_FAVOR_SMALL_CODE */ +/* Use one universal function and macros to reduce size */ + +/** + * Generic function for converting not more then @a maxlen + * hexadecimal or decimal US-ASCII digits in string to number. + * Conversion stopped at first non-digit character or after @a maxlen + * digits. + * To be used only within macro. + * @param str the string to convert + * @param maxlen the maximum number of characters to process + * @param out_val the pointer to uint64_t to store result of conversion + * @param val_size the size of variable pointed by @a out_val + * @param max_val the maximum decoded number + * @param base the numeric base, 10 or 16 + * @return non-zero number of characters processed on succeed, + * zero if no digit is found, resulting value is larger + * then @ max_val or @a out_val is NULL + */ +size_t +MHD_str_to_uvalue_n_ (const char * str, + size_t maxlen, + void * out_val, + size_t val_size, + uint64_t max_val, + int base); + +#define MHD_str_to_uint64_(s,ov) MHD_str_to_uvalue_n_((s),SIZE_MAX,(ov),\ + sizeof(uint64_t),UINT64_MAX,10) + +#define MHD_str_to_uint64_n_(s,ml,ov) MHD_str_to_uvalue_n_((s),(ml),(ov),\ + sizeof(uint64_t),UINT64_MAX,10) + +#define MHD_strx_to_sizet_(s,ov) MHD_str_to_uvalue_n_((s),SIZE_MAX,(ov),\ + sizeof(size_t),SIZE_MAX,16) + +#define MHD_strx_to_sizet_n_(s,ml,ov) MHD_str_to_uvalue_n_((s),(ml),(ov),\ + sizeof(size_t),SIZE_MAX,16) + +#define MHD_strx_to_uint32_(s,ov) MHD_str_to_uvalue_n_((s),SIZE_MAX,(ov),\ + sizeof(uint32_t),UINT32_MAX,16) + +#define MHD_strx_to_uint32_n_(s,ml,ov) MHD_str_to_uvalue_n_((s),(ml),(ov),\ + sizeof(uint32_t),UINT32_MAX,16) + +#define MHD_strx_to_uint64_(s,ov) MHD_str_to_uvalue_n_((s),SIZE_MAX,(ov),\ + sizeof(uint64_t),UINT64_MAX,16) + +#define MHD_strx_to_uint64_n_(s,ml,ov) MHD_str_to_uvalue_n_((s),(ml),(ov),\ + sizeof(uint64_t),UINT64_MAX,16) + +#endif /* MHD_FAVOR_SMALL_CODE */ + +#endif /* MHD_STR_H */ diff --git a/src/lib/mhd_threads.c b/src/lib/mhd_threads.c @@ -0,0 +1,363 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2016 Karlson2k (Evgeny Grin) + + 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/mhd_threads.c + * @brief Implementation for thread functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_threads.h" +#ifdef MHD_USE_W32_THREADS +#include "mhd_limits.h" +#include <process.h> +#endif +#ifdef MHD_USE_THREAD_NAME_ +#include <stdlib.h> +#ifdef HAVE_PTHREAD_NP_H +#include <pthread_np.h> +#endif /* HAVE_PTHREAD_NP_H */ +#endif /* MHD_USE_THREAD_NAME_ */ +#include <errno.h> + + +#ifndef MHD_USE_THREAD_NAME_ + +#define MHD_set_thread_name_(t, n) (void) +#define MHD_set_cur_thread_name_(n) (void) + +#else /* MHD_USE_THREAD_NAME_ */ + +#if defined(MHD_USE_POSIX_THREADS) +#if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) +# define MHD_USE_THREAD_ATTR_SETNAME 1 +#endif /* HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD || HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI */ + +#if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) \ + || defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) + +/** + * Set thread name + * + * @param thread_id ID of thread + * @param thread_name name to set + * @return non-zero on success, zero otherwise + */ +static int +MHD_set_thread_name_(const MHD_thread_ID_ thread_id, + const char *thread_name) +{ + if (NULL == thread_name) + return 0; + +#if defined(HAVE_PTHREAD_SETNAME_NP_GNU) + return !pthread_setname_np (thread_id, thread_name); +#elif defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) + /* FreeBSD and OpenBSD use different name and void return type */ + pthread_set_name_np (thread_id, thread_name); + return !0; +#elif defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) + /* NetBSD use 3 arguments: second argument is string in printf-like format, + * third argument is single argument for printf; + * OSF1 use 3 arguments too, but last one always must be zero (NULL). + * MHD doesn't use '%' in thread names, so both form are used in same way. + */ + return !pthread_setname_np (thread_id, thread_name, 0); +#endif /* HAVE_PTHREAD_SETNAME_NP_NETBSD */ +} + + +#ifndef __QNXNTO__ +/** + * Set current thread name + * @param n name to set + * @return non-zero on success, zero otherwise + */ +#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_(pthread_self(),(n)) +#else /* __QNXNTO__ */ +/* Special case for QNX Neutrino - using zero for thread ID sets name faster. */ +#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_(0,(n)) +#endif /* __QNXNTO__ */ +#elif defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) + +/** + * Set current thread name + * @param n name to set + * @return non-zero on success, zero otherwise + */ +#define MHD_set_cur_thread_name_(n) (!(pthread_setname_np((n)))) +#endif /* HAVE_PTHREAD_SETNAME_NP_DARWIN */ + +#elif defined(MHD_USE_W32_THREADS) +#ifndef _MSC_FULL_VER +/* Thread name available only for VC-compiler */ +#else /* _MSC_FULL_VER */ +/** + * Set thread name + * + * @param thread_id ID of thread, -1 for current thread + * @param thread_name name to set + * @return non-zero on success, zero otherwise + */ +static int +MHD_set_thread_name_(const MHD_thread_ID_ thread_id, + const char *thread_name) +{ + static const DWORD VC_SETNAME_EXC = 0x406D1388; +#pragma pack(push,8) + struct thread_info_struct + { + DWORD type; /* Must be 0x1000. */ + LPCSTR name; /* Pointer to name (in user address space). */ + DWORD ID; /* Thread ID (-1 = caller thread). */ + DWORD flags; /* Reserved for future use, must be zero. */ + } thread_info; +#pragma pack(pop) + + if (NULL == thread_name) + return 0; + + thread_info.type = 0x1000; + thread_info.name = thread_name; + thread_info.ID = thread_id; + thread_info.flags = 0; + + __try + { /* This exception is intercepted by debugger */ + RaiseException (VC_SETNAME_EXC, + 0, + sizeof (thread_info) / sizeof(ULONG_PTR), + (ULONG_PTR *) &thread_info); + } + __except (EXCEPTION_EXECUTE_HANDLER) + {} + + return !0; +} + + +/** + * Set current thread name + * @param n name to set + * @return non-zero on success, zero otherwise + */ +#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_(-1,(n)) +#endif /* _MSC_FULL_VER */ +#endif /* MHD_USE_W32_THREADS */ + +#endif /* MHD_USE_THREAD_NAME_ */ + + +/** + * Create a thread and set the attributes according to our options. + * + * @param thread handle to initialize + * @param stack_size size of stack for new thread, 0 for default + * @param start_routine main function of thread + * @param arg argument for start_routine + * @return non-zero on success; zero otherwise (with errno set) + */ +int +MHD_create_thread_ (MHD_thread_handle_ID_ *thread, + size_t stack_size, + MHD_THREAD_START_ROUTINE_ start_routine, + void *arg) +{ +#if defined(MHD_USE_POSIX_THREADS) + int res; + + if (0 != stack_size) + { + pthread_attr_t attr; + res = pthread_attr_init (&attr); + if (0 == res) + { + res = pthread_attr_setstacksize (&attr, + stack_size); + if (0 == res) + res = pthread_create (&(thread->handle), + &attr, + start_routine, + arg); + pthread_attr_destroy (&attr); + } + } + else + res = pthread_create (&(thread->handle), + NULL, + start_routine, + arg); + + if (0 != res) + errno = res; + + return !res; +#elif defined(MHD_USE_W32_THREADS) +#if SIZE_MAX != UINT_MAX + if (stack_size > UINT_MAX) + { + errno = EINVAL; + return 0; + } +#endif /* SIZE_MAX != UINT_MAX */ + + thread->handle = (MHD_thread_handle_) + _beginthreadex (NULL, + (unsigned int) stack_size, + start_routine, + arg, + 0, + NULL); + + if ((MHD_thread_handle_)-1 == thread->handle) + return 0; + + return !0; +#endif +} + +#ifdef MHD_USE_THREAD_NAME_ + +#ifndef MHD_USE_THREAD_ATTR_SETNAME +struct MHD_named_helper_param_ +{ + /** + * Real thread start routine + */ + MHD_THREAD_START_ROUTINE_ start_routine; + + /** + * Argument for thread start routine + */ + void *arg; + + /** + * Name for thread + */ + const char *name; +}; + + +static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_ +named_thread_starter (void *data) +{ + struct MHD_named_helper_param_ * const param = + (struct MHD_named_helper_param_ *) data; + void * arg; + MHD_THREAD_START_ROUTINE_ thr_func; + + if (NULL == data) + return (MHD_THRD_RTRN_TYPE_)0; + + MHD_set_cur_thread_name_ (param->name); + + arg = param->arg; + thr_func = param->start_routine; + free(data); + + return thr_func(arg); +} +#endif /* ! MHD_USE_THREAD_ATTR_SETNAME */ + + +/** + * Create a named thread and set the attributes according to our options. + * + * @param thread handle to initialize + * @param thread_name name for new thread + * @param stack_size size of stack for new thread, 0 for default + * @param start_routine main function of thread + * @param arg argument for start_routine + * @return non-zero on success; zero otherwise (with errno set) + */ +int +MHD_create_named_thread_ (MHD_thread_handle_ID_ *thread, + const char* thread_name, + size_t stack_size, + MHD_THREAD_START_ROUTINE_ start_routine, + void *arg) +{ +#if defined(MHD_USE_THREAD_ATTR_SETNAME) + int res; + pthread_attr_t attr; + + res = pthread_attr_init (&attr); + if (0 == res) + { +#if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) + /* NetBSD use 3 arguments: second argument is string in printf-like format, + * third argument is single argument for printf; + * OSF1 use 3 arguments too, but last one always must be zero (NULL). + * MHD doesn't use '%' in thread names, so both form are used in same way. + */ + res = pthread_attr_setname_np (&attr, thread_name, 0); +#elif defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) + res = pthread_attr_setname_np (&attr, thread_name); +#else +#error No pthread_attr_setname_np() function. +#endif + if (res == 0 && 0 != stack_size) + res = pthread_attr_setstacksize (&attr, + stack_size); + if (0 == res) + res = pthread_create (&(thread->handle), + &attr, + start_routine, + arg); + pthread_attr_destroy (&attr); + } + if (0 != res) + errno = res; + + return !res; +#else /* ! MHD_USE_THREAD_ATTR_SETNAME */ + struct MHD_named_helper_param_ *param; + + if (NULL == thread_name) + { + errno = EINVAL; + return 0; + } + + param = malloc (sizeof (struct MHD_named_helper_param_)); + if (NULL == param) + return 0; + + param->start_routine = start_routine; + param->arg = arg; + param->name = thread_name; + + /* Set thread name in thread itself to avoid problems with + * threads which terminated before name is set in other thread. + */ + if (! MHD_create_thread_(thread, + stack_size, + &named_thread_starter, + (void*)param)) + { + free (param); + return 0; + } + + return !0; +#endif /* ! MHD_USE_THREAD_ATTR_SETNAME */ +} + +#endif /* MHD_USE_THREAD_NAME_ */ diff --git a/src/lib/mhd_threads.h b/src/lib/mhd_threads.h @@ -0,0 +1,229 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2016 Karlson2k (Evgeny Grin) + + 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/mhd_threads.h + * @brief Header for platform-independent threads abstraction + * @author Karlson2k (Evgeny Grin) + * + * Provides basic abstraction for threads. + * Any functions can be implemented as macro on some platforms + * unless explicitly marked otherwise. + * Any function argument can be skipped in macro, so avoid + * variable modification in function parameters. + * + * @warning Unlike pthread functions, most of functions return + * nonzero on success. + */ + +#ifndef MHD_THREADS_H +#define MHD_THREADS_H 1 + +#include "mhd_options.h" +#ifdef HAVE_STDDEF_H +# include <stddef.h> /* for size_t */ +#else /* ! HAVE_STDDEF_H */ +# include <stdlib.h> /* for size_t */ +#endif /* ! HAVE_STDDEF_H */ + +#if defined(MHD_USE_POSIX_THREADS) +# undef HAVE_CONFIG_H +# include <pthread.h> +# define HAVE_CONFIG_H 1 +#elif defined(MHD_USE_W32_THREADS) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif /* !WIN32_LEAN_AND_MEAN */ +# include <windows.h> +#else +# error No threading API is available. +#endif + +#ifndef MHD_NO_THREAD_NAMES +# if defined(MHD_USE_POSIX_THREADS) +# if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) || \ + defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) || defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) || \ + defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) +# define MHD_USE_THREAD_NAME_ +# endif /* HAVE_PTHREAD_SETNAME_NP */ +# elif defined(MHD_USE_W32_THREADS) +# ifdef _MSC_FULL_VER + /* Thread names only available with VC compiler */ +# define MHD_USE_THREAD_NAME_ +# endif /* _MSC_FULL_VER */ +# endif +#endif + +#if defined(MHD_USE_POSIX_THREADS) + typedef pthread_t MHD_thread_handle_; +#elif defined(MHD_USE_W32_THREADS) + typedef HANDLE MHD_thread_handle_; +#endif + +#if defined(MHD_USE_POSIX_THREADS) +# define MHD_THRD_RTRN_TYPE_ void* +# define MHD_THRD_CALL_SPEC_ +#elif defined(MHD_USE_W32_THREADS) +# define MHD_THRD_RTRN_TYPE_ unsigned +# define MHD_THRD_CALL_SPEC_ __stdcall +#endif + +#if defined(MHD_USE_POSIX_THREADS) + typedef pthread_t MHD_thread_ID_; +#elif defined(MHD_USE_W32_THREADS) + typedef DWORD MHD_thread_ID_; +#endif + +/* Depending on implementation, pthread_create() MAY set thread ID into + * provided pointer and after it start thread OR start thread and after + * if set thread ID. In latter case, to avoid data races, additional + * pthread_self() call is required in thread routine. Is some platform + * is known for setting thread ID BEFORE starting thread macro + * MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD could be defined + * to save some resources. */ +#if defined(MHD_USE_POSIX_THREADS) +# ifdef MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD + union _MHD_thread_handle_ID_ + { + MHD_thread_handle_ handle; /**< To be used in other threads */ + MHD_thread_ID_ ID; /**< To be used in thread itself */ + }; + typedef union _MHD_thread_handle_ID_ MHD_thread_handle_ID_; +# else /* ! MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD */ + struct _MHD_thread_handle_ID_ + { + MHD_thread_handle_ handle; /**< To be used in other threads */ + MHD_thread_ID_ ID; /**< To be used in thread itself */ + }; + typedef struct _MHD_thread_handle_ID_ MHD_thread_handle_ID_; +# endif /* ! MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD */ +#elif defined(MHD_USE_W32_THREADS) + struct _MHD_thread_handle_ID_ + { + MHD_thread_handle_ handle; /**< To be used in other threads */ + MHD_thread_ID_ ID; /**< To be used in thread itself */ + }; + typedef struct _MHD_thread_handle_ID_ MHD_thread_handle_ID_; +#endif + +#if defined(MHD_USE_POSIX_THREADS) +/** + * Wait until specified thread is ended and free thread handle on success. + * @param thread handle to watch + * @return nonzero on success, zero otherwise + */ +#define MHD_join_thread_(thread) (!pthread_join((thread), NULL)) +#elif defined(MHD_USE_W32_THREADS) +/** + * Wait until specified thread is ended and free thread handle on success. + * @param thread handle to watch + * @return nonzero on success, zero otherwise + */ +#define MHD_join_thread_(thread) (WAIT_OBJECT_0 == WaitForSingleObject((thread), INFINITE) ? (CloseHandle((thread)), !0) : 0) +#endif + +#if defined(MHD_USE_POSIX_THREADS) +/** + * Check whether provided thread ID match current thread. + * @param ID thread ID to match + * @return nonzero on match, zero otherwise + */ +#define MHD_thread_ID_match_current_(ID) (pthread_equal((ID), pthread_self())) +#elif defined(MHD_USE_W32_THREADS) +/** + * Check whether provided thread ID match current thread. + * @param ID thread ID to match + * @return nonzero on match, zero otherwise + */ +#define MHD_thread_ID_match_current_(ID) (GetCurrentThreadId() == (ID)) +#endif + +#if defined(MHD_USE_POSIX_THREADS) +# ifdef MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD +/** + * Initialise thread ID. + * @param thread_handle_ID_ptr pointer to thread handle-ID + */ +#define MHD_thread_init_(thread_handle_ID_ptr) (void)0 +# else /* ! MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD */ +/** + * Initialise thread ID. + * @param thread_handle_ID_ptr pointer to thread handle-ID + */ +#define MHD_thread_init_(thread_handle_ID_ptr) ((thread_handle_ID_ptr)->ID=pthread_self()) +# endif /* ! MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD */ +#elif defined(MHD_USE_W32_THREADS) +/** + * Initialise thread ID. + * @param thread_handle_ID_ptr pointer to thread handle-ID + */ +#define MHD_thread_init_(thread_handle_ID_ptr) ((thread_handle_ID_ptr)->ID=GetCurrentThreadId()) +#endif + +/** + * 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_ *MHD_THREAD_START_ROUTINE_)(void *cls); + + +/** + * Create a thread and set the attributes according to our options. + * + * If thread is created, thread handle must be freed by MHD_join_thread_(). + * + * @param thread handle to initialize + * @param stack_size size of stack for new thread, 0 for default + * @param start_routine main function of thread + * @param arg argument for start_routine + * @return non-zero on success; zero otherwise + */ +int +MHD_create_thread_ (MHD_thread_handle_ID_ *thread, + size_t stack_size, + MHD_THREAD_START_ROUTINE_ start_routine, + void *arg); + +#ifndef MHD_USE_THREAD_NAME_ +#define MHD_create_named_thread_(t,n,s,r,a) MHD_create_thread_((t),(s),(r),(a)) +#else /* MHD_USE_THREAD_NAME_ */ +/** + * Create a named thread and set the attributes according to our options. + * + * @param thread handle to initialize + * @param thread_name name for new thread + * @param stack_size size of stack for new thread, 0 for default + * @param start_routine main function of thread + * @param arg argument for start_routine + * @return non-zero on success; zero otherwise + */ +int +MHD_create_named_thread_ (MHD_thread_handle_ID_ *thread, + const char* thread_name, + size_t stack_size, + MHD_THREAD_START_ROUTINE_ start_routine, + void *arg); + +#endif /* MHD_USE_THREAD_NAME_ */ + +#endif /* ! MHD_THREADS_H */ diff --git a/src/lib/sysfdsetsize.c b/src/lib/sysfdsetsize.c @@ -0,0 +1,80 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2015 Karlson2k (Evgeny Grin) + + 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/sysfdsetsize.c + * @brief Helper for obtaining FD_SETSIZE system default value + * @author Karlson2k (Evgeny Grin) + */ + + +#include "MHD_config.h" + +#ifdef FD_SETSIZE +/* FD_SETSIZE was defined before system headers. */ +/* To get system value of FD_SETSIZE, undefine FD_SETSIZE + here. */ +#undef FD_SETSIZE +#endif /* FD_SETSIZE */ + +#include <stdlib.h> +#if defined(__VXWORKS__) || defined(__vxworks) || defined(OS_VXWORKS) +#include <sockLib.h> +#endif /* OS_VXWORKS */ +#if HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif /* HAVE_SYS_SELECT_H */ +#if HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif /* HAVE_SYS_TYPES_H */ +#if HAVE_SYS_TIME_H +#include <sys/time.h> +#endif /* HAVE_SYS_TIME_H */ +#if HAVE_TIME_H +#include <time.h> +#endif /* HAVE_TIME_H */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#if HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif /* HAVE_SYS_SOCKET_H */ + +#if defined(_WIN32) && !defined(__CYGWIN__) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif /* !WIN32_LEAN_AND_MEAN */ +#include <winsock2.h> +#endif /* _WIN32 && !__CYGWIN__ */ + +#ifndef FD_SETSIZE +#error FD_SETSIZE must be defined in system headers +#endif /* !FD_SETSIZE */ + +#include "sysfdsetsize.h" + +/** + * Get system default value of FD_SETSIZE + * @return system default value of FD_SETSIZE + */ +int +get_system_fdsetsize_value (void) +{ + return FD_SETSIZE; +} diff --git a/src/lib/sysfdsetsize.h b/src/lib/sysfdsetsize.h @@ -0,0 +1,36 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2015 Karlson2k (Evgeny Grin) + + 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/sysfdsetsize.h + * @brief Helper for obtaining FD_SETSIZE system default value + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef SYSFDSETSIZE_H +#define SYSFDSETSIZE_H 1 + +/** + * Get system default value of FD_SETSIZE + * @return system default value of FD_SETSIZE + */ +int +get_system_fdsetsize_value (void); + +#endif /* !SYSFDSETSIZE_H */ diff --git a/src/lib/tsearch.c b/src/lib/tsearch.c @@ -0,0 +1,143 @@ +/* + * Tree search generalized from Knuth (6.2.2) Algorithm T just like + * the AT&T man page says. + * + * The node_t structure is for internal use only, lint doesn't grok it. + * + * Written by reading the System V Interface Definition, not the code. + * + * Totally public domain. + */ + +#include "tsearch.h" +#include <stdlib.h> + + +typedef struct node +{ + const void *key; + struct node *llink; + struct node *rlink; +} node_t; + + +/* $NetBSD: tsearch.c,v 1.5 2005/11/29 03:12:00 christos Exp $ */ +/* find or insert datum into search tree */ +void * +tsearch (const void *vkey, /* key to be located */ + void **vrootp, /* address of tree root */ + int (*compar)(const void *, const void *)) +{ + node_t *q; + node_t **rootp = (node_t **)vrootp; + + if (NULL == rootp) + return NULL; + + while (*rootp != NULL) + { /* Knuth's T1: */ + int r; + + if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */ + return *rootp; /* we found it! */ + + rootp = (r < 0) ? + &(*rootp)->llink : /* T3: follow left branch */ + &(*rootp)->rlink; /* T4: follow right branch */ + } + + q = malloc (sizeof(node_t)); /* T5: key not found */ + if (q) + { /* make new node */ + *rootp = q; /* link new node to old */ + q->key = vkey; /* initialize new node */ + q->llink = q->rlink = NULL; + } + return q; +} + + +/* $NetBSD: tfind.c,v 1.5 2005/03/23 08:16:53 kleink Exp $ */ +/* find a node, or return NULL */ +void * +tfind (const void *vkey, /* key to be found */ + void * const *vrootp, /* address of the tree root */ + int (*compar)(const void *, const void *)) +{ + node_t * const *rootp = (node_t * const*)vrootp; + + if (NULL == rootp) + return NULL; + + while (*rootp != NULL) + { /* T1: */ + int r; + + if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */ + return *rootp; /* key found */ + rootp = (r < 0) ? + &(*rootp)->llink : /* T3: follow left branch */ + &(*rootp)->rlink; /* T4: follow right branch */ + } + return NULL; +} + + +/* $NetBSD: tdelete.c,v 1.2 1999/09/16 11:45:37 lukem Exp $ */ +/* + * delete node with given key + * + * vkey: key to be deleted + * vrootp: address of the root of the tree + * compar: function to carry out node comparisons + */ +void * +tdelete (const void * __restrict vkey, + void ** __restrict vrootp, + int (*compar)(const void *, const void *)) +{ + node_t **rootp = (node_t **)vrootp; + node_t *p; + node_t *q; + node_t *r; + int cmp; + + if (rootp == NULL || (p = *rootp) == NULL) + return NULL; + + while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0) + { + p = *rootp; + rootp = (cmp < 0) ? + &(*rootp)->llink : /* follow llink branch */ + &(*rootp)->rlink; /* follow rlink branch */ + if (*rootp == NULL) + return NULL; /* key not found */ + } + r = (*rootp)->rlink; /* D1: */ + if ((q = (*rootp)->llink) == NULL) /* Left NULL? */ + { + q = r; + } + else if (r != NULL) + { /* Right link is NULL? */ + if (r->llink == NULL) + { /* D2: Find successor */ + r->llink = q; + q = r; + } + else + { /* D3: Find NULL link */ + for (q = r->llink; q->llink != NULL; q = r->llink) + r = q; + r->llink = q->rlink; + q->llink = (*rootp)->llink; + q->rlink = (*rootp)->rlink; + } + } + free(*rootp); /* D4: Free node */ + *rootp = q; /* link parent to new node */ + return p; +} + +/* end of tsearch.c */ diff --git a/src/lib/tsearch.h b/src/lib/tsearch.h @@ -0,0 +1,38 @@ +/*- + * Written by J.T. Conklin <jtc@netbsd.org> + * Public domain. + * + * $NetBSD: search.h,v 1.12 1999/02/22 10:34:28 christos Exp $ + * $FreeBSD: release/9.0.0/include/search.h 105250 2002-10-16 14:29:23Z robert $ + */ + +#ifndef _TSEARCH_H_ +#define _TSEARCH_H_ + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ + + +void * +tdelete (const void * __restrict, + void ** __restrict, + int (*)(const void *, const void *)); + + +void * +tfind (const void *, + void * const *, + int (*)(const void *, const void *)); + + +void * +tsearch (const void *, + void **, + int (*)(const void *, const void *)); + +#if defined(__cplusplus) +}; +#endif /* __cplusplus */ + +#endif /* !_TSEARCH_H_ */ diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -5526,7 +5526,17 @@ MHD_start_daemon_va (unsigned int flags, (0 == (*pflags & MHD_USE_NO_LISTEN_SOCKET)) ) { /* try to open listen socket */ - listen_fd = MHD_socket_create_listen_(*pflags & MHD_USE_IPv6); + int domain; + +#ifdef HAVE_INET6 + domain = (*pflags & MHD_USE_IPv6) ? PF_INET6 : PF_INET; +#else /* ! HAVE_INET6 */ + if (*pflags & MHD_USE_IPv6) + goto free_and_fail; + domain = PF_INET; +#endif /* ! HAVE_INET6 */ + + listen_fd = MHD_socket_create_listen_(domain); if (MHD_INVALID_SOCKET == listen_fd) { #ifdef HAVE_MESSAGES diff --git a/src/microhttpd/mhd_sockets.c b/src/microhttpd/mhd_sockets.c @@ -464,31 +464,22 @@ MHD_socket_noninheritable_ (MHD_socket sock) /** * Create a listen socket, with noninheritable flag if possible. * - * @param use_ipv6 if set to non-zero IPv6 is used + * @param pf protocol family to use * @return created socket or MHD_INVALID_SOCKET in case of errors */ MHD_socket -MHD_socket_create_listen_ (bool use_ipv6) +MHD_socket_create_listen_ (int pf) { - int domain; MHD_socket fd; int cloexec_set; -#ifdef HAVE_INET6 - domain = (use_ipv6) ? PF_INET6 : PF_INET; -#else /* ! HAVE_INET6 */ - if (use_ipv6) - return MHD_INVALID_SOCKET; - domain = PF_INET; -#endif /* ! HAVE_INET6 */ - #if defined(MHD_POSIX_SOCKETS) && defined(SOCK_CLOEXEC) - fd = socket (domain, + fd = socket (pf, SOCK_STREAM | SOCK_CLOEXEC, 0); cloexec_set = !0; #elif defined(MHD_WINSOCK_SOCKETS) && defined (WSA_FLAG_NO_HANDLE_INHERIT) - fd = WSASocketW (domain, + fd = WSASocketW (pf, SOCK_STREAM, 0, NULL, @@ -500,7 +491,7 @@ MHD_socket_create_listen_ (bool use_ipv6) #endif /* !SOCK_CLOEXEC */ if (MHD_INVALID_SOCKET == fd) { - fd = socket (domain, + fd = socket (pf, SOCK_STREAM, 0); cloexec_set = 0;