blob: 95945d3c5a9b4668e37bc23d8787803bb1edeeef [file] [log] [blame]
/* Copyright (c) 2013-2017 the Civetweb developers
* Copyright (c) 2004-2013 Sergey Lyubka
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#if defined(_WIN32)
#if !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
#endif
#ifndef _WIN32_WINNT /* defined for tdm-gcc so we can use getnameinfo */
#define _WIN32_WINNT 0x0501
#endif
#else
#if defined(__GNUC__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE /* for setgroups() */
#endif
#if defined(__linux__) && !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */
#endif
#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */
#endif
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */
#endif
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */
#endif
#ifdef __sun
#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */
#define __inline inline /* not recognized on older compiler versions */
#endif
#endif
#if defined(USE_LUA)
#define USE_TIMERS
#endif
#if defined(_MSC_VER)
/* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */
#pragma warning(disable : 4306)
/* conditional expression is constant: introduced by FD_SET(..) */
#pragma warning(disable : 4127)
/* non-constant aggregate initializer: issued due to missing C99 support */
#pragma warning(disable : 4204)
/* padding added after data member */
#pragma warning(disable : 4820)
/* not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */
#pragma warning(disable : 4668)
/* no function prototype given: converting '()' to '(void)' */
#pragma warning(disable : 4255)
/* function has been selected for automatic inline expansion */
#pragma warning(disable : 4711)
#endif
/* This code uses static_assert to check some conditions.
* Unfortunately some compilers still do not support it, so we have a
* replacement function here. */
#if defined(_MSC_VER) && (_MSC_VER >= 1600)
#define mg_static_assert static_assert
#elif defined(__cplusplus) && (__cplusplus >= 201103L)
#define mg_static_assert static_assert
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
#define mg_static_assert _Static_assert
#else
char static_assert_replacement[1];
#define mg_static_assert(cond, txt) \
extern char static_assert_replacement[(cond) ? 1 : -1]
#endif
mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8,
"int data type size check");
mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8,
"pointer data type size check");
mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
/* Alternative queue is well tested and should be the new default */
#ifdef NO_ALTERNATIVE_QUEUE
#ifdef ALTERNATIVE_QUEUE
#error "Define ALTERNATIVE_QUEUE or NO_ALTERNATIVE_QUEUE or none, but not both"
#endif
#else
#define ALTERNATIVE_QUEUE
#endif
/* DTL -- including winsock2.h works better if lean and mean */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#if defined(__SYMBIAN32__)
/* According to https://en.wikipedia.org/wiki/Symbian#History,
* Symbian is no longer maintained since 2014-01-01.
* Recent versions of CivetWeb are no longer tested for Symbian.
* It makes no sense, to support an abandoned operating system.
* All remaining "#ifdef __SYMBIAN__" cases will be droped from
* the code sooner or later.
*/
#pragma message \
"Symbian is no longer maintained. CivetWeb will drop Symbian support."
#define NO_SSL /* SSL is not supported */
#define NO_CGI /* CGI is not supported */
#define PATH_MAX FILENAME_MAX
#endif /* __SYMBIAN32__ */
#ifndef CIVETWEB_HEADER_INCLUDED
/* Include the header file here, so the CivetWeb interface is defined for the
* entire implementation, including the following forward definitions. */
#include "civetweb.h"
#endif
#ifndef IGNORE_UNUSED_RESULT
#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1))
#endif
#if defined(__GNUC__) || defined(__MINGW32__)
/* GCC unused function attribute seems fundamentally broken.
* Several attempts to tell the compiler "THIS FUNCTION MAY BE USED
* OR UNUSED" for individual functions failed.
* Either the compiler creates an "unused-function" warning if a
* function is not marked with __attribute__((unused)).
* On the other hand, if the function is marked with this attribute,
* but is used, the compiler raises a completely idiotic
* "used-but-marked-unused" warning - and
* #pragma GCC diagnostic ignored "-Wused-but-marked-unused"
* raises error: unknown option after ‘#pragma GCC diagnostic’.
* Disable this warning completely, until the GCC guys sober up
* again.
*/
#pragma GCC diagnostic ignored "-Wunused-function"
#define FUNCTION_MAY_BE_UNUSED /* __attribute__((unused)) */
#else
#define FUNCTION_MAY_BE_UNUSED
#endif
#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#endif /* !_WIN32_WCE */
#ifdef __clang__
/* When using -Weverything, clang does not accept it's own headers
* in a release build configuration. Disable what is too much in
* -Weverything. */
#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
#endif
#if defined(__GNUC__) || defined(__MINGW32__)
/* Who on earth came to the conclusion, using __DATE__ should rise
* an "expansion of date or time macro is not reproducible"
* warning. That's exactly what was intended by using this macro.
* Just disable this nonsense warning. */
/* And disabling them does not work either:
* #pragma clang diagnostic ignored "-Wno-error=date-time"
* #pragma clang diagnostic ignored "-Wdate-time"
* So we just have to disable ALL warnings for some lines
* of code.
*/
#endif
#ifdef __MACH__ /* Apple OSX section */
#ifdef __clang__
#if (__clang_major__ == 3) && ((__clang_minor__ == 7) || (__clang_minor__ == 8))
/* Avoid warnings for Xcode 7. It seems it does no longer exist in Xcode 8 */
#pragma clang diagnostic ignored "-Wno-reserved-id-macro"
#pragma clang diagnostic ignored "-Wno-keyword-macro"
#endif
#endif
#define CLOCK_MONOTONIC (1)
#define CLOCK_REALTIME (2)
#include <sys/errno.h>
#include <sys/time.h>
#include <mach/clock.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <assert.h>
/* clock_gettime is not implemented on OSX prior to 10.12 */
static int
_civet_clock_gettime(int clk_id, struct timespec *t)
{
memset(t, 0, sizeof(*t));
if (clk_id == CLOCK_REALTIME) {
struct timeval now;
int rv = gettimeofday(&now, NULL);
if (rv) {
return rv;
}
t->tv_sec = now.tv_sec;
t->tv_nsec = now.tv_usec * 1000;
return 0;
} else if (clk_id == CLOCK_MONOTONIC) {
static uint64_t clock_start_time = 0;
static mach_timebase_info_data_t timebase_ifo = {0, 0};
uint64_t now = mach_absolute_time();
if (clock_start_time == 0) {
kern_return_t mach_status = mach_timebase_info(&timebase_ifo);
#if defined(DEBUG)
assert(mach_status == KERN_SUCCESS);
#else
/* appease "unused variable" warning for release builds */
(void)mach_status;
#endif
clock_start_time = now;
}
now = (uint64_t)((double)(now - clock_start_time)
* (double)timebase_ifo.numer
/ (double)timebase_ifo.denom);
t->tv_sec = now / 1000000000;
t->tv_nsec = now % 1000000000;
return 0;
}
return -1; /* EINVAL - Clock ID is unknown */
}
/* if clock_gettime is declared, then __CLOCK_AVAILABILITY will be defined */
#ifdef __CLOCK_AVAILABILITY
/* If we compiled with Mac OSX 10.12 or later, then clock_gettime will be
* declared but it may be NULL at runtime. So we need to check before using
* it. */
static int
_civet_safe_clock_gettime(int clk_id, struct timespec *t)
{
if (clock_gettime) {
return clock_gettime(clk_id, t);
}
return _civet_clock_gettime(clk_id, t);
}
#define clock_gettime _civet_safe_clock_gettime
#else
#define clock_gettime _civet_clock_gettime
#endif
#endif
#include <time.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
#ifndef INT64_MAX
#define INT64_MAX (9223372036854775807)
#endif
#ifndef MAX_WORKER_THREADS
#define MAX_WORKER_THREADS (1024 * 64)
#endif
#ifndef SOCKET_TIMEOUT_QUANTUM /* in ms */
#define SOCKET_TIMEOUT_QUANTUM (2000)
#endif
#define SHUTDOWN_RD (0)
#define SHUTDOWN_WR (1)
#define SHUTDOWN_BOTH (2)
mg_static_assert(MAX_WORKER_THREADS >= 1,
"worker threads must be a positive number");
mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8,
"size_t data type size check");
#if defined(_WIN32) \
&& !defined(__SYMBIAN32__) /* WINDOWS / UNIX include block */
#include <windows.h>
#include <winsock2.h> /* DTL add for SO_EXCLUSIVE */
#include <ws2tcpip.h>
typedef const char *SOCK_OPT_TYPE;
#if !defined(PATH_MAX)
#define PATH_MAX (MAX_PATH)
#endif
#if !defined(PATH_MAX)
#define PATH_MAX (4096)
#endif
mg_static_assert(PATH_MAX >= 1, "path length must be a positive number");
#ifndef _IN_PORT_T
#ifndef in_port_t
#define in_port_t u_short
#endif
#endif
#ifndef _WIN32_WCE
#include <process.h>
#include <direct.h>
#include <io.h>
#else /* _WIN32_WCE */
#define NO_CGI /* WinCE has no pipes */
#define NO_POPEN /* WinCE has no popen */
typedef long off_t;
#define errno ((int)(GetLastError()))
#define strerror(x) (_ultoa(x, (char *)_alloca(sizeof(x) * 3), 10))
#endif /* _WIN32_WCE */
#define MAKEUQUAD(lo, hi) \
((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32))
#define RATE_DIFF (10000000) /* 100 nsecs */
#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de))
#define SYS2UNIX_TIME(lo, hi) \
((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF))
/* Visual Studio 6 does not know __func__ or __FUNCTION__
* The rest of MS compilers use __FUNCTION__, not C99 __func__
* Also use _strtoui64 on modern M$ compilers */
#if defined(_MSC_VER)
#if (_MSC_VER < 1300)
#define STRX(x) #x
#define STR(x) STRX(x)
#define __func__ __FILE__ ":" STR(__LINE__)
#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x))
#define strtoll(x, y, z) (_atoi64(x))
#else
#define __func__ __FUNCTION__
#define strtoull(x, y, z) (_strtoui64(x, y, z))
#define strtoll(x, y, z) (_strtoi64(x, y, z))
#endif
#endif /* _MSC_VER */
#define ERRNO ((int)(GetLastError()))
#define NO_SOCKLEN_T
#if defined(_WIN64) || defined(__MINGW64__)
#define SSL_LIB "ssleay64.dll"
#define CRYPTO_LIB "libeay64.dll"
#else
#define SSL_LIB "ssleay32.dll"
#define CRYPTO_LIB "libeay32.dll"
#endif
#define O_NONBLOCK (0)
#ifndef W_OK
#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */
#endif
#if !defined(EWOULDBLOCK)
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif /* !EWOULDBLOCK */
#define _POSIX_
#define INT64_FMT "I64d"
#define UINT64_FMT "I64u"
#define WINCDECL __cdecl
#define vsnprintf_impl _vsnprintf
#define access _access
#define mg_sleep(x) (Sleep(x))
#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
#ifndef popen
#define popen(x, y) (_popen(x, y))
#endif
#ifndef pclose
#define pclose(x) (_pclose(x))
#endif
#define close(x) (_close(x))
#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y)))
#define RTLD_LAZY (0)
#define fseeko(x, y, z) ((_lseeki64(_fileno(x), (y), (z)) == -1) ? -1 : 0)
#define fdopen(x, y) (_fdopen((x), (y)))
#define write(x, y, z) (_write((x), (y), (unsigned)z))
#define read(x, y, z) (_read((x), (y), (unsigned)z))
#define flockfile(x) (EnterCriticalSection(&global_log_file_lock))
#define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock))
#define sleep(x) (Sleep((x)*1000))
#define rmdir(x) (_rmdir(x))
#define timegm(x) (_mkgmtime(x))
#if !defined(fileno)
#define fileno(x) (_fileno(x))
#endif /* !fileno MINGW #defines fileno */
typedef HANDLE pthread_mutex_t;
typedef DWORD pthread_key_t;
typedef HANDLE pthread_t;
typedef struct {
CRITICAL_SECTION threadIdSec;
struct mg_workerTLS *waiting_thread; /* The chain of threads */
} pthread_cond_t;
#ifndef __clockid_t_defined
typedef DWORD clockid_t;
#endif
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC (1)
#endif
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME (2)
#endif
#ifndef CLOCK_THREAD
#define CLOCK_THREAD (3)
#endif
#ifndef CLOCK_PROCESS
#define CLOCK_PROCESS (4)
#endif
#if defined(_MSC_VER) && (_MSC_VER >= 1900)
#define _TIMESPEC_DEFINED
#endif
#ifndef _TIMESPEC_DEFINED
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
#endif
#if !defined(WIN_PTHREADS_TIME_H)
#define MUST_IMPLEMENT_CLOCK_GETTIME
#endif
#ifdef MUST_IMPLEMENT_CLOCK_GETTIME
#define clock_gettime mg_clock_gettime
static int
clock_gettime(clockid_t clk_id, struct timespec *tp)
{
FILETIME ft;
ULARGE_INTEGER li, li2;
BOOL ok = FALSE;
double d;
static double perfcnt_per_sec = 0.0;
if (tp) {
memset(tp, 0, sizeof(*tp));
if (clk_id == CLOCK_REALTIME) {
/* BEGIN: CLOCK_REALTIME = wall clock (date and time) */
GetSystemTimeAsFileTime(&ft);
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */
tp->tv_sec = (time_t)(li.QuadPart / 10000000);
tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
ok = TRUE;
/* END: CLOCK_REALTIME */
} else if (clk_id == CLOCK_MONOTONIC) {
/* BEGIN: CLOCK_MONOTONIC = stopwatch (time differences) */
if (perfcnt_per_sec == 0.0) {
QueryPerformanceFrequency((LARGE_INTEGER *)&li);
perfcnt_per_sec = 1.0 / li.QuadPart;
}
if (perfcnt_per_sec != 0.0) {
QueryPerformanceCounter((LARGE_INTEGER *)&li);
d = li.QuadPart * perfcnt_per_sec;
tp->tv_sec = (time_t)d;
d -= tp->tv_sec;
tp->tv_nsec = (long)(d * 1.0E9);
ok = TRUE;
}
/* END: CLOCK_MONOTONIC */
} else if (clk_id == CLOCK_THREAD) {
/* BEGIN: CLOCK_THREAD = CPU usage of thread */
FILETIME t_create, t_exit, t_kernel, t_user;
if (GetThreadTimes(GetCurrentThread(),
&t_create,
&t_exit,
&t_kernel,
&t_user)) {
li.LowPart = t_user.dwLowDateTime;
li.HighPart = t_user.dwHighDateTime;
li2.LowPart = t_kernel.dwLowDateTime;
li2.HighPart = t_kernel.dwHighDateTime;
li.QuadPart += li2.QuadPart;
tp->tv_sec = (time_t)(li.QuadPart / 10000000);
tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
ok = TRUE;
}
/* END: CLOCK_THREAD */
} else if (clk_id == CLOCK_PROCESS) {
/* BEGIN: CLOCK_PROCESS = CPU usage of process */
FILETIME t_create, t_exit, t_kernel, t_user;
if (GetProcessTimes(GetCurrentProcess(),
&t_create,
&t_exit,
&t_kernel,
&t_user)) {
li.LowPart = t_user.dwLowDateTime;
li.HighPart = t_user.dwHighDateTime;
li2.LowPart = t_kernel.dwLowDateTime;
li2.HighPart = t_kernel.dwHighDateTime;
li.QuadPart += li2.QuadPart;
tp->tv_sec = (time_t)(li.QuadPart / 10000000);
tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
ok = TRUE;
}
/* END: CLOCK_PROCESS */
} else {
/* BEGIN: unknown clock */
/* ok = FALSE; already set by init */
/* END: unknown clock */
}
}
return ok ? 0 : -1;
}
#endif
#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */
static int pthread_mutex_lock(pthread_mutex_t *);
static int pthread_mutex_unlock(pthread_mutex_t *);
static void path_to_unicode(const struct mg_connection *conn,
const char *path,
wchar_t *wbuf,
size_t wbuf_len);
/* All file operations need to be rewritten to solve #246. */
#include "file_ops.inl"
struct mg_file;
static const char *
mg_fgets(char *buf, size_t size, struct mg_file *filep, char **p);
/* POSIX dirent interface */
struct dirent {
char d_name[PATH_MAX];
};
typedef struct DIR {
HANDLE handle;
WIN32_FIND_DATAW info;
struct dirent result;
} DIR;
#if defined(_WIN32) && !defined(POLLIN)
#ifndef HAVE_POLL
struct pollfd {
SOCKET fd;
short events;
short revents;
};
#define POLLIN (0x0300)
#endif
#endif
/* Mark required libraries */
#if defined(_MSC_VER)
#pragma comment(lib, "Ws2_32.lib")
#endif
#else /* defined(_WIN32) && !defined(__SYMBIAN32__) - \
WINDOWS / UNIX include block */
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <stdint.h>
#include <inttypes.h>
#include <netdb.h>
#include <netinet/tcp.h>
typedef const void *SOCK_OPT_TYPE;
#if defined(ANDROID)
typedef unsigned short int in_port_t;
#endif
#include <pwd.h>
#include <unistd.h>
#include <grp.h>
#include <dirent.h>
#define vsnprintf_impl vsnprintf
#if !defined(NO_SSL_DL) && !defined(NO_SSL)
#include <dlfcn.h>
#endif
#include <pthread.h>
#if defined(__MACH__)
#define SSL_LIB "libssl.dylib"
#define CRYPTO_LIB "libcrypto.dylib"
#else
#if !defined(SSL_LIB)
#define SSL_LIB "libssl.so"
#endif
#if !defined(CRYPTO_LIB)
#define CRYPTO_LIB "libcrypto.so"
#endif
#endif
#ifndef O_BINARY
#define O_BINARY (0)
#endif /* O_BINARY */
#define closesocket(a) (close(a))
#define mg_mkdir(conn, path, mode) (mkdir(path, mode))
#define mg_remove(conn, x) (remove(x))
#define mg_sleep(x) (usleep((x)*1000))
#define mg_opendir(conn, x) (opendir(x))
#define mg_closedir(x) (closedir(x))
#define mg_readdir(x) (readdir(x))
#define ERRNO (errno)
#define INVALID_SOCKET (-1)
#define INT64_FMT PRId64
#define UINT64_FMT PRIu64
typedef int SOCKET;
#define WINCDECL
#if defined(__hpux)
/* HPUX 11 does not have monotonic, fall back to realtime */
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC CLOCK_REALTIME
#endif
/* HPUX defines socklen_t incorrectly as size_t which is 64bit on
* Itanium. Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED
* the prototypes use int* rather than socklen_t* which matches the
* actual library expectation. When called with the wrong size arg
* accept() returns a zero client inet addr and check_acl() always
* fails. Since socklen_t is widely used below, just force replace
* their typedef with int. - DTL
*/
#define socklen_t int
#endif /* hpux */
#endif /* defined(_WIN32) && !defined(__SYMBIAN32__) - \
WINDOWS / UNIX include block */
/* va_copy should always be a macro, C99 and C++11 - DTL */
#ifndef va_copy
#define va_copy(x, y) ((x) = (y))
#endif
#ifdef _WIN32
/* Create substitutes for POSIX functions in Win32. */
#if defined(__MINGW32__)
/* Show no warning in case system functions are not used. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
static CRITICAL_SECTION global_log_file_lock;
FUNCTION_MAY_BE_UNUSED
static DWORD
pthread_self(void)
{
return GetCurrentThreadId();
}
FUNCTION_MAY_BE_UNUSED
static int
pthread_key_create(
pthread_key_t *key,
void (*_ignored)(void *) /* destructor not supported for Windows */
)
{
(void)_ignored;
if ((key != 0)) {
*key = TlsAlloc();
return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1;
}
return -2;
}
FUNCTION_MAY_BE_UNUSED
static int
pthread_key_delete(pthread_key_t key)
{
return TlsFree(key) ? 0 : 1;
}
FUNCTION_MAY_BE_UNUSED
static int
pthread_setspecific(pthread_key_t key, void *value)
{
return TlsSetValue(key, value) ? 0 : 1;
}
FUNCTION_MAY_BE_UNUSED
static void *
pthread_getspecific(pthread_key_t key)
{
return TlsGetValue(key);
}
#if defined(__MINGW32__)
/* Enable unused function warning again */
#pragma GCC diagnostic pop
#endif
static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL;
#else
static pthread_mutexattr_t pthread_mutex_attr;
#endif /* _WIN32 */
#define PASSWORDS_FILE_NAME ".htpasswd"
#define CGI_ENVIRONMENT_SIZE (4096)
#define MAX_CGI_ENVIR_VARS (256)
#define MG_BUF_LEN (8192)
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#if defined(_WIN32_WCE)
/* Create substitutes for POSIX functions in Win32. */
#if defined(__MINGW32__)
/* Show no warning in case system functions are not used. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
FUNCTION_MAY_BE_UNUSED
static time_t
time(time_t *ptime)
{
time_t t;
SYSTEMTIME st;
FILETIME ft;
GetSystemTime(&st);
SystemTimeToFileTime(&st, &ft);
t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
if (ptime != NULL) {
*ptime = t;
}
return t;
}
FUNCTION_MAY_BE_UNUSED
static struct tm *
localtime_s(const time_t *ptime, struct tm *ptm)
{
int64_t t = ((int64_t)*ptime) * RATE_DIFF + EPOCH_DIFF;
FILETIME ft, lft;
SYSTEMTIME st;
TIME_ZONE_INFORMATION tzinfo;
if (ptm == NULL) {
return NULL;
}
*(int64_t *)&ft = t;
FileTimeToLocalFileTime(&ft, &lft);
FileTimeToSystemTime(&lft, &st);
ptm->tm_year = st.wYear - 1900;
ptm->tm_mon = st.wMonth - 1;
ptm->tm_wday = st.wDayOfWeek;
ptm->tm_mday = st.wDay;
ptm->tm_hour = st.wHour;
ptm->tm_min = st.wMinute;
ptm->tm_sec = st.wSecond;
ptm->tm_yday = 0; /* hope nobody uses this */
ptm->tm_isdst =
(GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT) ? 1 : 0;
return ptm;
}
FUNCTION_MAY_BE_UNUSED
static struct tm *
gmtime_s(const time_t *ptime, struct tm *ptm)
{
/* FIXME(lsm): fix this. */
return localtime_s(ptime, ptm);
}
static int mg_atomic_inc(volatile int *addr);
static struct tm tm_array[MAX_WORKER_THREADS];
static int tm_index = 0;
FUNCTION_MAY_BE_UNUSED
static struct tm *
localtime(const time_t *ptime)
{
int i = mg_atomic_inc(&tm_index) % (sizeof(tm_array) / sizeof(tm_array[0]));
return localtime_s(ptime, tm_array + i);
}
FUNCTION_MAY_BE_UNUSED
static struct tm *
gmtime(const time_t *ptime)
{
int i = mg_atomic_inc(&tm_index) % ARRAY_SIZE(tm_array);
return gmtime_s(ptime, tm_array + i);
}
FUNCTION_MAY_BE_UNUSED
static size_t
strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm)
{
/* TODO: (void)mg_snprintf(NULL, dst, dst_size, "implement strftime()
* for WinCE"); */
return 0;
}
#define _beginthreadex(psec, stack, func, prm, flags, ptid) \
(uintptr_t) CreateThread(psec, stack, func, prm, flags, ptid)
#define remove(f) mg_remove(NULL, f)
FUNCTION_MAY_BE_UNUSED
static int
rename(const char *a, const char *b)
{
wchar_t wa[PATH_MAX];
wchar_t wb[PATH_MAX];
path_to_unicode(NULL, a, wa, ARRAY_SIZE(wa));
path_to_unicode(NULL, b, wb, ARRAY_SIZE(wb));
return MoveFileW(wa, wb) ? 0 : -1;
}
struct stat {
int64_t st_size;
time_t st_mtime;
};
FUNCTION_MAY_BE_UNUSED
static int
stat(const char *name, struct stat *st)
{
wchar_t wbuf[PATH_MAX];
WIN32_FILE_ATTRIBUTE_DATA attr;
time_t creation_time, write_time;
path_to_unicode(NULL, name, wbuf, ARRAY_SIZE(wbuf));
memset(&attr, 0, sizeof(attr));
GetFileAttributesExW(wbuf, GetFileExInfoStandard, &attr);
st->st_size =
(((int64_t)attr.nFileSizeHigh) << 32) + (int64_t)attr.nFileSizeLow;
write_time = SYS2UNIX_TIME(attr.ftLastWriteTime.dwLowDateTime,
attr.ftLastWriteTime.dwHighDateTime);
creation_time = SYS2UNIX_TIME(attr.ftCreationTime.dwLowDateTime,
attr.ftCreationTime.dwHighDateTime);
if (creation_time > write_time) {
st->st_mtime = creation_time;
} else {
st->st_mtime = write_time;
}
return 0;
}
#define access(x, a) 1 /* not required anyway */
/* WinCE-TODO: define stat, remove, rename, _rmdir, _lseeki64 */
/* Values from errno.h in Windows SDK (Visual Studio). */
#define EEXIST 17
#define EACCES 13
#define ENOENT 2
#if defined(__MINGW32__)
/* Enable unused function warning again */
#pragma GCC diagnostic pop
#endif
#endif /* defined(_WIN32_WCE) */
#if defined(__GNUC__) || defined(__MINGW32__)
/* Show no warning in case system functions are not used. */
#define GCC_VERSION \
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= 40500
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif /* GCC_VERSION >= 40500 */
#endif /* defined(__GNUC__) || defined(__MINGW32__) */
#if defined(__clang__)
/* Show no warning in case system functions are not used. */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#endif
static pthread_mutex_t global_lock_mutex;
#if defined(_WIN32) && !defined(__SYMBIAN32__)
/* Forward declaration for Windows */
FUNCTION_MAY_BE_UNUSED
static int pthread_mutex_lock(pthread_mutex_t *mutex);
FUNCTION_MAY_BE_UNUSED
static int pthread_mutex_unlock(pthread_mutex_t *mutex);
#endif
FUNCTION_MAY_BE_UNUSED
static void
mg_global_lock(void)
{
(void)pthread_mutex_lock(&global_lock_mutex);
}
FUNCTION_MAY_BE_UNUSED
static void
mg_global_unlock(void)
{
(void)pthread_mutex_unlock(&global_lock_mutex);
}
FUNCTION_MAY_BE_UNUSED
static int
mg_atomic_inc(volatile int *addr)
{
int ret;
#if defined(_WIN32) && !defined(__SYMBIAN32__) && !defined(NO_ATOMICS)
/* Depending on the SDK, this function uses either
* (volatile unsigned int *) or (volatile LONG *),
* so whatever you use, the other SDK is likely to raise a warning. */
ret = InterlockedIncrement((volatile long *)addr);
#elif defined(__GNUC__) \
&& ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
&& !defined(NO_ATOMICS)
ret = __sync_add_and_fetch(addr, 1);
#else
mg_global_lock();
ret = (++(*addr));
mg_global_unlock();
#endif
return ret;
}
FUNCTION_MAY_BE_UNUSED
static int
mg_atomic_dec(volatile int *addr)
{
int ret;
#if defined(_WIN32) && !defined(__SYMBIAN32__) && !defined(NO_ATOMICS)
/* Depending on the SDK, this function uses either
* (volatile unsigned int *) or (volatile LONG *),
* so whatever you use, the other SDK is likely to raise a warning. */
ret = InterlockedDecrement((volatile long *)addr);
#elif defined(__GNUC__) \
&& ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
&& !defined(NO_ATOMICS)
ret = __sync_sub_and_fetch(addr, 1);
#else
mg_global_lock();
ret = (--(*addr));
mg_global_unlock();
#endif
return ret;
}
#if defined(USE_SERVER_STATS)
static int64_t
mg_atomic_add(volatile int64_t *addr, int64_t value)
{
int64_t ret;
#if defined(_WIN64) && !defined(__SYMBIAN32__) && !defined(NO_ATOMICS)
ret = InterlockedAdd64(addr, value);
#elif defined(__GNUC__) \
&& ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
&& !defined(NO_ATOMICS)
ret = __sync_add_and_fetch(addr, value);
#else
mg_global_lock();
*addr += value;
ret = (*addr);
mg_global_unlock();
#endif
return ret;
}
#endif
#if defined(__GNUC__)
/* Show no warning in case system functions are not used. */
#if GCC_VERSION >= 40500
#pragma GCC diagnostic pop
#endif /* GCC_VERSION >= 40500 */
#endif /* defined(__GNUC__) */
#if defined(__clang__)
/* Show no warning in case system functions are not used. */
#pragma clang diagnostic pop
#endif
#if defined(USE_SERVER_STATS)
struct mg_memory_stat {
volatile int64_t totalMemUsed;
volatile int64_t maxMemUsed;
volatile int blockCount;
};
static struct mg_memory_stat *get_memory_stat(struct mg_context *ctx);
static void *
mg_malloc_ex(size_t size,
struct mg_context *ctx,
const char *file,
unsigned line)
{
void *data = malloc(size + 2 * sizeof(uintptr_t));
void *memory = 0;
struct mg_memory_stat *mstat = get_memory_stat(ctx);
#if defined(MEMORY_DEBUGGING)
char mallocStr[256];
#else
(void)file;
(void)line;
#endif
if (data) {
int64_t mmem = mg_atomic_add(&mstat->totalMemUsed, (int64_t)size);
if (mmem > mstat->maxMemUsed) {
/* could use atomic compare exchange, but this
* seems overkill for statistics data */
mstat->maxMemUsed = mmem;
}
mg_atomic_inc(&mstat->blockCount);
((uintptr_t *)data)[0] = size;
((uintptr_t *)data)[1] = (uintptr_t)mstat;
memory = (void *)(((char *)data) + 2 * sizeof(uintptr_t));
}
#if defined(MEMORY_DEBUGGING)
sprintf(mallocStr,
"MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n",
memory,
(unsigned long)size,
(unsigned long)mstat->totalMemUsed,
(unsigned long)mstat->blockCount,
file,
line);
#if defined(_WIN32)
OutputDebugStringA(mallocStr);
#else
DEBUG_TRACE("%s", mallocStr);
#endif
#endif
return memory;
}
static void *
mg_calloc_ex(size_t count,
size_t size,
struct mg_context *ctx,
const char *file,
unsigned line)
{
void *data = mg_malloc_ex(size * count, ctx, file, line);
if (data) {
memset(data, 0, size * count);
}
return data;
}
static void
mg_free_ex(void *memory, const char *file, unsigned line)
{
void *data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
#if defined(MEMORY_DEBUGGING)
char mallocStr[256];
#else
(void)file;
(void)line;
#endif
if (memory) {
uintptr_t size = ((uintptr_t *)data)[0];
struct mg_memory_stat *mstat =
(struct mg_memory_stat *)(((uintptr_t *)data)[1]);
mg_atomic_add(&mstat->totalMemUsed, -(int64_t)size);
mg_atomic_dec(&mstat->blockCount);
#if defined(MEMORY_DEBUGGING)
sprintf(mallocStr,
"MEM: %p %5lu free %7lu %4lu --- %s:%u\n",
memory,
(unsigned long)size,
(unsigned long)mstat->totalMemUsed,
(unsigned long)mstat->blockCount,
file,
line);
#if defined(_WIN32)
OutputDebugStringA(mallocStr);
#else
DEBUG_TRACE("%s", mallocStr);
#endif
#endif
free(data);
}
}
static void *
mg_realloc_ex(void *memory,
size_t newsize,
struct mg_context *ctx,
const char *file,
unsigned line)
{
void *data;
void *_realloc;
uintptr_t oldsize;
#if defined(MEMORY_DEBUGGING)
char mallocStr[256];
#else
(void)file;
(void)line;
#endif
if (newsize) {
if (memory) {
/* Reallocate existing block */
struct mg_memory_stat *mstat;
data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
oldsize = ((uintptr_t *)data)[0];
mstat = (struct mg_memory_stat *)((uintptr_t *)data)[1];
_realloc = realloc(data, newsize + 2 * sizeof(uintptr_t));
if (_realloc) {
data = _realloc;
mg_atomic_add(&mstat->totalMemUsed, -(int64_t)oldsize);
#if defined(MEMORY_DEBUGGING)
sprintf(mallocStr,
"MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n",
memory,
(unsigned long)oldsize,
(unsigned long)mstat->totalMemUsed,
(unsigned long)mstat->blockCount,
file,
line);
#if defined(_WIN32)
OutputDebugStringA(mallocStr);
#else
DEBUG_TRACE("%s", mallocStr);
#endif
#endif
mg_atomic_add(&mstat->totalMemUsed, (int64_t)newsize);
#if defined(MEMORY_DEBUGGING)
sprintf(mallocStr,
"MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n",
memory,
(unsigned long)newsize,
(unsigned long)mstat->totalMemUsed,
(unsigned long)mstat->blockCount,
file,
line);
#if defined(_WIN32)
OutputDebugStringA(mallocStr);
#else
DEBUG_TRACE("%s", mallocStr);
#endif
#endif
*(uintptr_t *)data = newsize;
data = (void *)(((char *)data) + 2 * sizeof(uintptr_t));
} else {
#if defined(MEMORY_DEBUGGING)
#if defined(_WIN32)
OutputDebugStringA("MEM: realloc failed\n");
#else
DEBUG_TRACE("%s", "MEM: realloc failed\n");
#endif
#endif
return _realloc;
}
} else {
/* Allocate new block */
data = mg_malloc_ex(newsize, ctx, file, line);
}
} else {
/* Free existing block */
data = 0;
mg_free_ex(memory, file, line);
}
return data;
}
#define mg_malloc(a) mg_malloc_ex(a, NULL, __FILE__, __LINE__)
#define mg_calloc(a, b) mg_calloc_ex(a, b, NULL, __FILE__, __LINE__)
#define mg_realloc(a, b) mg_realloc_ex(a, b, NULL, __FILE__, __LINE__)
#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__)
#define mg_malloc_ctx(a, c) mg_malloc_ex(a, c, __FILE__, __LINE__)
#define mg_calloc_ctx(a, b, c) mg_calloc_ex(a, b, c, __FILE__, __LINE__)
#define mg_realloc_ctx(a, b, c) mg_realloc_ex(a, b, c, __FILE__, __LINE__)
#else /* USE_SERVER_STATS */
static __inline void *
mg_malloc(size_t a)
{
return malloc(a);
}
static __inline void *
mg_calloc(size_t a, size_t b)
{
return calloc(a, b);
}
static __inline void *
mg_realloc(void *a, size_t b)
{
return realloc(a, b);
}
static __inline void
mg_free(void *a)
{
free(a);
}
#define mg_malloc_ctx(a, c) mg_malloc(a)
#define mg_calloc_ctx(a, b, c) mg_calloc(a, b)
#define mg_realloc_ctx(a, b, c) mg_realloc(a, b)
#define mg_free_ctx(a, c) mg_free(a)
#endif /* USE_SERVER_STATS */
static void mg_vsnprintf(const struct mg_connection *conn,
int *truncated,
char *buf,
size_t buflen,
const char *fmt,
va_list ap);
static void mg_snprintf(const struct mg_connection *conn,
int *truncated,
char *buf,
size_t buflen,
PRINTF_FORMAT_STRING(const char *fmt),
...) PRINTF_ARGS(5, 6);
/* This following lines are just meant as a reminder to use the mg-functions
* for memory management */
#ifdef malloc
#undef malloc
#endif
#ifdef calloc
#undef calloc
#endif
#ifdef realloc
#undef realloc
#endif
#ifdef free
#undef free
#endif
#ifdef snprintf
#undef snprintf
#endif
#ifdef vsnprintf
#undef vsnprintf
#endif
#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
#define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
#ifdef _WIN32 /* vsnprintf must not be used in any system, * \ \ \ \
* but this define only works well for Windows. */
#define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf
#endif
/* mg_init_library counter */
static int mg_init_library_called = 0;
#if !defined(NO_SSL)
static int mg_ssl_initialized = 0;
#endif
static pthread_key_t sTlsKey; /* Thread local storage index */
static int thread_idx_max = 0;
struct mg_workerTLS {
int is_master;
unsigned long thread_idx;
#if defined(_WIN32) && !defined(__SYMBIAN32__)
HANDLE pthread_cond_helper_mutex;
struct mg_workerTLS *next_waiting_thread;
#endif
};
#if defined(__GNUC__) || defined(__MINGW32__)
/* Show no warning in case system functions are not used. */
#if GCC_VERSION >= 40500
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif /* GCC_VERSION >= 40500 */
#endif /* defined(__GNUC__) || defined(__MINGW32__) */
#if defined(__clang__)
/* Show no warning in case system functions are not used. */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#endif
/* Get a unique thread ID as unsigned long, independent from the data type
* of thread IDs defined by the operating system API.
* If two calls to mg_current_thread_id return the same value, they calls
* are done from the same thread. If they return different values, they are
* done from different threads. (Provided this function is used in the same
* process context and threads are not repeatedly created and deleted, but
* CivetWeb does not do that).
* This function must match the signature required for SSL id callbacks:
* CRYPTO_set_id_callback
*/
FUNCTION_MAY_BE_UNUSED
static unsigned long
mg_current_thread_id(void)
{
#ifdef _WIN32
return GetCurrentThreadId();
#else
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
/* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)"
* or not, so one of the two conditions will be unreachable by construction.
* Unfortunately the C standard does not define a way to check this at
* compile time, since the #if preprocessor conditions can not use the sizeof
* operator as an argument. */
#endif
if (sizeof(pthread_t) > sizeof(unsigned long)) {
/* This is the problematic case for CRYPTO_set_id_callback:
* The OS pthread_t can not be cast to unsigned long. */
struct mg_workerTLS *tls =
(struct mg_workerTLS *)pthread_getspecific(sTlsKey);
if (tls == NULL) {
/* SSL called from an unknown thread: Create some thread index.
*/
tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS));
tls->is_master = -2; /* -2 means "3rd party thread" */
tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
pthread_setspecific(sTlsKey, tls);
}
return tls->thread_idx;
} else {
/* pthread_t may be any data type, so a simple cast to unsigned long
* can rise a warning/error, depending on the platform.
* Here memcpy is used as an anything-to-anything cast. */
unsigned long ret = 0;
pthread_t t = pthread_self();
memcpy(&ret, &t, sizeof(pthread_t));
return ret;
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif
}
FUNCTION_MAY_BE_UNUSED
static uint64_t
mg_get_current_time_ns(void)
{
struct timespec tsnow;
clock_gettime(CLOCK_REALTIME, &tsnow);
return (((uint64_t)tsnow.tv_sec) * 1000000000) + (uint64_t)tsnow.tv_nsec;
}
#if defined(__GNUC__)
/* Show no warning in case system functions are not used. */
#if GCC_VERSION >= 40500
#pragma GCC diagnostic pop
#endif /* GCC_VERSION >= 40500 */
#endif /* defined(__GNUC__) */
#if defined(__clang__)
/* Show no warning in case system functions are not used. */
#pragma clang diagnostic pop
#endif
#if !defined(DEBUG_TRACE)
#if defined(DEBUG)
static void DEBUG_TRACE_FUNC(const char *func,
unsigned line,
PRINTF_FORMAT_STRING(const char *fmt),
...) PRINTF_ARGS(3, 4);
static void
DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...)
{
va_list args;
uint64_t nsnow;
static uint64_t nslast;
struct timespec tsnow;
/* Get some operating system independent thread id */
unsigned long thread_id = mg_current_thread_id();
clock_gettime(CLOCK_REALTIME, &tsnow);
nsnow = ((uint64_t)tsnow.tv_sec) * ((uint64_t)1000000000)
+ ((uint64_t)tsnow.tv_nsec);
if (!nslast) {
nslast = nsnow;
}
flockfile(stdout);
printf("*** %lu.%09lu %12" INT64_FMT " %lu %s:%u: ",
(unsigned long)tsnow.tv_sec,
(unsigned long)tsnow.tv_nsec,
nsnow - nslast,
thread_id,
func,
line);
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
putchar('\n');
fflush(stdout);
funlockfile(stdout);
nslast = nsnow;
}
#define DEBUG_TRACE(fmt, ...) \
DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__)
#else
#define DEBUG_TRACE(fmt, ...) \
do { \
} while (0)
#endif /* DEBUG */
#endif /* DEBUG_TRACE */
#define MD5_STATIC static
#include "md5.inl"
/* Darwin prior to 7.0 and Win32 do not have socklen_t */
#ifdef NO_SOCKLEN_T
typedef int socklen_t;
#endif /* NO_SOCKLEN_T */
#define _DARWIN_UNLIMITED_SELECT
#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */
#if !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL (0)
#endif
#if !defined(SOMAXCONN)
#define SOMAXCONN (100)
#endif
/* Size of the accepted socket queue */
#if !defined(MGSQLEN)
#define MGSQLEN (20)
#endif
#if defined(NO_SSL)
typedef struct SSL SSL; /* dummy for SSL argument to push/pull */
typedef struct SSL_CTX SSL_CTX;
#else
#if defined(NO_SSL_DL)
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/engine.h>
#include <openssl/conf.h>
#include <openssl/dh.h>
#include <openssl/bn.h>
#include <openssl/opensslv.h>
#else
/* SSL loaded dynamically from DLL.
* I put the prototypes here to be independent from OpenSSL source
* installation. */
typedef struct ssl_st SSL;
typedef struct ssl_method_st SSL_METHOD;
typedef struct ssl_ctx_st SSL_CTX;
typedef struct x509_store_ctx_st X509_STORE_CTX;
typedef struct x509_name X509_NAME;
typedef struct asn1_integer ASN1_INTEGER;
typedef struct bignum BIGNUM;
typedef struct ossl_init_settings_st OPENSSL_INIT_SETTINGS;
typedef struct evp_md EVP_MD;
typedef struct x509 X509;
#define SSL_CTRL_OPTIONS (32)
#define SSL_CTRL_CLEAR_OPTIONS (77)
#define SSL_CTRL_SET_ECDH_AUTO (94)
#define OPENSSL_INIT_NO_LOAD_SSL_STRINGS 0x00100000L
#define OPENSSL_INIT_LOAD_SSL_STRINGS 0x00200000L
#define OPENSSL_INIT_LOAD_CRYPTO_STRINGS 0x00000002L
#define SSL_VERIFY_NONE (0)
#define SSL_VERIFY_PEER (1)
#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT (2)
#define SSL_VERIFY_CLIENT_ONCE (4)
#define SSL_OP_ALL ((long)(0x80000BFFUL))
#define SSL_OP_NO_SSLv2 (0x01000000L)
#define SSL_OP_NO_SSLv3 (0x02000000L)
#define SSL_OP_NO_TLSv1 (0x04000000L)
#define SSL_OP_NO_TLSv1_2 (0x08000000L)
#define SSL_OP_NO_TLSv1_1 (0x10000000L)
#define SSL_OP_SINGLE_DH_USE (0x00100000L)
#define SSL_OP_CIPHER_SERVER_PREFERENCE (0x00400000L)
#define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (0x00010000L)
#define SSL_OP_NO_COMPRESSION (0x00020000L)
#define SSL_CB_HANDSHAKE_START (0x10)
#define SSL_CB_HANDSHAKE_DONE (0x20)
#define SSL_ERROR_NONE (0)
#define SSL_ERROR_SSL (1)
#define SSL_ERROR_WANT_READ (2)
#define SSL_ERROR_WANT_WRITE (3)
#define SSL_ERROR_WANT_X509_LOOKUP (4)
#define SSL_ERROR_SYSCALL (5) /* see errno */
#define SSL_ERROR_ZERO_RETURN (6)
#define SSL_ERROR_WANT_CONNECT (7)
#define SSL_ERROR_WANT_ACCEPT (8)
struct ssl_func {
const char *name; /* SSL function name */
void (*ptr)(void); /* Function pointer */
};
#ifdef OPENSSL_API_1_1
#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr)
#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr)
#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr)
#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr)
#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr)
#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr)
#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr)
#define SSL_new (*(SSL * (*)(SSL_CTX *))ssl_sw[7].ptr)
#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *))ssl_sw[8].ptr)
#define TLS_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr)
#define OPENSSL_init_ssl \
(*(int (*)(uint64_t opts, \
const OPENSSL_INIT_SETTINGS *settings))ssl_sw[10].ptr)
#define SSL_CTX_use_PrivateKey_file \
(*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr)
#define SSL_CTX_use_certificate_file \
(*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr)
#define SSL_CTX_set_default_passwd_cb \
(*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr)
#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr)
#define SSL_CTX_use_certificate_chain_file \
(*(int (*)(SSL_CTX *, const char *))ssl_sw[15].ptr)
#define TLS_client_method (*(SSL_METHOD * (*)(void))ssl_sw[16].ptr)
#define SSL_pending (*(int (*)(SSL *))ssl_sw[17].ptr)
#define SSL_CTX_set_verify \
(*(void (*)(SSL_CTX *, \
int, \
int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[18].ptr)
#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[19].ptr)
#define SSL_CTX_load_verify_locations \
(*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[20].ptr)
#define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[21].ptr)
#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[22].ptr)
#define SSL_get_peer_certificate (*(X509 * (*)(SSL *))ssl_sw[23].ptr)
#define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[24].ptr)
#define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *))ssl_sw[25].ptr)
#define SSL_CIPHER_get_name \
(*(const char *(*)(const SSL_CIPHER *))ssl_sw[26].ptr)
#define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[27].ptr)
#define SSL_CTX_set_session_id_context \
(*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[28].ptr)
#define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[29].ptr)
#define SSL_CTX_set_cipher_list \
(*(int (*)(SSL_CTX *, const char *))ssl_sw[30].ptr)
#define SSL_CTX_set_options \
(*(unsigned long (*)(SSL_CTX *, unsigned long))ssl_sw[31].ptr)
#define SSL_CTX_set_info_callback \
(*(void (*)(SSL_CTX * ctx, \
void (*callback)(SSL * s, int, int)))ssl_sw[32].ptr)
#define SSL_get_ex_data (*(char *(*)(SSL *, int))ssl_sw[33].ptr)
#define SSL_set_ex_data (*(void (*)(SSL *, int, char *))ssl_sw[34].ptr)
#define SSL_CTX_clear_options(ctx, op) \
SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL)
#define SSL_CTX_set_ecdh_auto(ctx, onoff) \
SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL)
#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore)
#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
#define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)arg))
#define SSL_get_app_data(s) (SSL_get_ex_data(s, 0))
#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[0].ptr)
#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[1].ptr)
#define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[2].ptr)
#define CONF_modules_unload (*(void (*)(int))crypto_sw[3].ptr)
#define X509_free (*(void (*)(X509 *))crypto_sw[4].ptr)
#define X509_get_subject_name (*(X509_NAME * (*)(X509 *))crypto_sw[5].ptr)
#define X509_get_issuer_name (*(X509_NAME * (*)(X509 *))crypto_sw[6].ptr)
#define X509_NAME_oneline \
(*(char *(*)(X509_NAME *, char *, int))crypto_sw[7].ptr)
#define X509_get_serialNumber (*(ASN1_INTEGER * (*)(X509 *))crypto_sw[8].ptr)
#define EVP_get_digestbyname \
(*(const EVP_MD *(*)(const char *))crypto_sw[9].ptr)
#define EVP_Digest \
(*(int (*)( \
const void *, size_t, void *, unsigned int *, const EVP_MD *, void *)) \
crypto_sw[10].ptr)
#define i2d_X509 (*(int (*)(X509 *, unsigned char **))crypto_sw[11].ptr)
#define BN_bn2hex (*(char *(*)(const BIGNUM *a))crypto_sw[12].ptr)
#define ASN1_INTEGER_to_BN \
(*(BIGNUM * (*)(const ASN1_INTEGER *ai, BIGNUM *bn))crypto_sw[13].ptr)
#define BN_free (*(void (*)(const BIGNUM *a))crypto_sw[14].ptr)
#define CRYPTO_free (*(void (*)(void *addr))crypto_sw[15].ptr)
#define OPENSSL_free(a) CRYPTO_free(a)
/* set_ssl_option() function updates this array.
* It loads SSL library dynamically and changes NULLs to the actual addresses
* of respective functions. The macros above (like SSL_connect()) are really
* just calling these functions indirectly via the pointer. */
static struct ssl_func ssl_sw[] = {{"SSL_free", NULL},
{"SSL_accept", NULL},
{"SSL_connect", NULL},
{"SSL_read", NULL},
{"SSL_write", NULL},
{"SSL_get_error", NULL},
{"SSL_set_fd", NULL},
{"SSL_new", NULL},
{"SSL_CTX_new", NULL},
{"TLS_server_method", NULL},
{"OPENSSL_init_ssl", NULL},
{"SSL_CTX_use_PrivateKey_file", NULL},
{"SSL_CTX_use_certificate_file", NULL},
{"SSL_CTX_set_default_passwd_cb", NULL},
{"SSL_CTX_free", NULL},
{"SSL_CTX_use_certificate_chain_file", NULL},
{"TLS_client_method", NULL},
{"SSL_pending", NULL},
{"SSL_CTX_set_verify", NULL},
{"SSL_shutdown", NULL},
{"SSL_CTX_load_verify_locations", NULL},
{"SSL_CTX_set_default_verify_paths", NULL},
{"SSL_CTX_set_verify_depth", NULL},
{"SSL_get_peer_certificate", NULL},
{"SSL_get_version", NULL},
{"SSL_get_current_cipher", NULL},
{"SSL_CIPHER_get_name", NULL},
{"SSL_CTX_check_private_key", NULL},
{"SSL_CTX_set_session_id_context", NULL},
{"SSL_CTX_ctrl", NULL},
{"SSL_CTX_set_cipher_list", NULL},
{"SSL_CTX_set_options", NULL},
{"SSL_CTX_set_info_callback", NULL},
{"SSL_get_ex_data", NULL},
{"SSL_set_ex_data", NULL},
{NULL, NULL}};
/* Similar array as ssl_sw. These functions could be located in different
* lib. */
static struct ssl_func crypto_sw[] = {{"ERR_get_error", NULL},
{"ERR_error_string", NULL},
{"ERR_remove_state", NULL},
{"CONF_modules_unload", NULL},
{"X509_free", NULL},
{"X509_get_subject_name", NULL},
{"X509_get_issuer_name", NULL},
{"X509_NAME_oneline", NULL},
{"X509_get_serialNumber", NULL},
{"EVP_get_digestbyname", NULL},
{"EVP_Digest", NULL},
{"i2d_X509", NULL},
{"BN_bn2hex", NULL},
{"ASN1_INTEGER_to_BN", NULL},
{"BN_free", NULL},
{"CRYPTO_free", NULL},
{NULL, NULL}};
#else
#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr)
#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr)
#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr)
#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr)
#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr)
#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr)
#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr)
#define SSL_new (*(SSL * (*)(SSL_CTX *))ssl_sw[7].ptr)
#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *))ssl_sw[8].ptr)
#define SSLv23_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr)
#define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr)
#define SSL_CTX_use_PrivateKey_file \
(*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr)
#define SSL_CTX_use_certificate_file \
(*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr)
#define SSL_CTX_set_default_passwd_cb \
(*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr)
#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr)
#define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr)
#define SSL_CTX_use_certificate_chain_file \
(*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr)
#define SSLv23_client_method (*(SSL_METHOD * (*)(void))ssl_sw[17].ptr)
#define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr)
#define SSL_CTX_set_verify \
(*(void (*)(SSL_CTX *, \
int, \
int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[19].ptr)
#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr)
#define SSL_CTX_load_verify_locations \
(*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[21].ptr)
#define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[22].ptr)
#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[23].ptr)
#define SSL_get_peer_certificate (*(X509 * (*)(SSL *))ssl_sw[24].ptr)
#define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[25].ptr)
#define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *))ssl_sw[26].ptr)
#define SSL_CIPHER_get_name \
(*(const char *(*)(const SSL_CIPHER *))ssl_sw[27].ptr)
#define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[28].ptr)
#define SSL_CTX_set_session_id_context \
(*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[29].ptr)
#define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[30].ptr)
#define SSL_CTX_set_cipher_list \
(*(int (*)(SSL_CTX *, const char *))ssl_sw[31].ptr)
#define SSL_CTX_set_info_callback \
(*(void (*)(SSL_CTX * ctx, \
void (*callback)(SSL * s, int, int)))ssl_sw[32].ptr)
#define SSL_get_ex_data (*(char *(*)(SSL *, int))ssl_sw[33].ptr)
#define SSL_set_ex_data (*(void (*)(SSL *, int, char *))ssl_sw[34].ptr)
#define SSL_CTX_set_options(ctx, op) \
SSL_CTX_ctrl((ctx), SSL_CTRL_OPTIONS, (op), NULL)
#define SSL_CTX_clear_options(ctx, op) \
SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL)
#define SSL_CTX_set_ecdh_auto(ctx, onoff) \
SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL)
#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore)
#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
#define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)arg))
#define SSL_get_app_data(s) (SSL_get_ex_data(s, 0))
#define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr)
#define CRYPTO_set_locking_callback \
(*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr)
#define CRYPTO_set_id_callback \
(*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr)
#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr)
#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr)
#define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[5].ptr)
#define ERR_free_strings (*(void (*)(void))crypto_sw[6].ptr)
#define ENGINE_cleanup (*(void (*)(void))crypto_sw[7].ptr)
#define CONF_modules_unload (*(void (*)(int))crypto_sw[8].ptr)
#define CRYPTO_cleanup_all_ex_data (*(void (*)(void))crypto_sw[9].ptr)
#define EVP_cleanup (*(void (*)(void))crypto_sw[10].ptr)
#define X509_free (*(void (*)(X509 *))crypto_sw[11].ptr)
#define X509_get_subject_name (*(X509_NAME * (*)(X509 *))crypto_sw[12].ptr)
#define X509_get_issuer_name (*(X509_NAME * (*)(X509 *))crypto_sw[13].ptr)
#define X509_NAME_oneline \
(*(char *(*)(X509_NAME *, char *, int))crypto_sw[14].ptr)
#define X509_get_serialNumber (*(ASN1_INTEGER * (*)(X509 *))crypto_sw[15].ptr)
#define i2c_ASN1_INTEGER \
(*(int (*)(ASN1_INTEGER *, unsigned char **))crypto_sw[16].ptr)
#define EVP_get_digestbyname \
(*(const EVP_MD *(*)(const char *))crypto_sw[17].ptr)
#define EVP_Digest \
(*(int (*)( \
const void *, size_t, void *, unsigned int *, const EVP_MD *, void *)) \
crypto_sw[18].ptr)
#define i2d_X509 (*(int (*)(X509 *, unsigned char **))crypto_sw[19].ptr)
#define BN_bn2hex (*(char *(*)(const BIGNUM *a))crypto_sw[20].ptr)
#define ASN1_INTEGER_to_BN \
(*(BIGNUM * (*)(const ASN1_INTEGER *ai, BIGNUM *bn))crypto_sw[21].ptr)
#define BN_free (*(void (*)(const BIGNUM *a))crypto_sw[22].ptr)
#define CRYPTO_free (*(void (*)(void *addr))crypto_sw[23].ptr)
#define OPENSSL_free(a) CRYPTO_free(a)
/* set_ssl_option() function updates this array.
* It loads SSL library dynamically and changes NULLs to the actual addresses
* of respective functions. The macros above (like SSL_connect()) are really
* just calling these functions indirectly via the pointer. */
static struct ssl_func ssl_sw[] = {{"SSL_free", NULL},
{"SSL_accept", NULL},
{"SSL_connect", NULL},
{"SSL_read", NULL},
{"SSL_write", NULL},
{"SSL_get_error", NULL},
{"SSL_set_fd", NULL},
{"SSL_new", NULL},
{"SSL_CTX_new", NULL},
{"SSLv23_server_method", NULL},
{"SSL_library_init", NULL},
{"SSL_CTX_use_PrivateKey_file", NULL},
{"SSL_CTX_use_certificate_file", NULL},
{"SSL_CTX_set_default_passwd_cb", NULL},
{"SSL_CTX_free", NULL},
{"SSL_load_error_strings", NULL},
{"SSL_CTX_use_certificate_chain_file", NULL},
{"SSLv23_client_method", NULL},
{"SSL_pending", NULL},
{"SSL_CTX_set_verify", NULL},
{"SSL_shutdown", NULL},
{"SSL_CTX_load_verify_locations", NULL},
{"SSL_CTX_set_default_verify_paths", NULL},
{"SSL_CTX_set_verify_depth", NULL},
{"SSL_get_peer_certificate", NULL},
{"SSL_get_version", NULL},
{"SSL_get_current_cipher", NULL},
{"SSL_CIPHER_get_name", NULL},
{"SSL_CTX_check_private_key", NULL},
{"SSL_CTX_set_session_id_context", NULL},
{"SSL_CTX_ctrl", NULL},
{"SSL_CTX_set_cipher_list", NULL},
{"SSL_CTX_set_info_callback", NULL},
{"SSL_get_ex_data", NULL},
{"SSL_set_ex_data", NULL},
{NULL, NULL}};
/* Similar array as ssl_sw. These functions could be located in different
* lib. */
static struct ssl_func crypto_sw[] = {{"CRYPTO_num_locks", NULL},
{"CRYPTO_set_locking_callback", NULL},
{"CRYPTO_set_id_callback", NULL},
{"ERR_get_error", NULL},
{"ERR_error_string", NULL},
{"ERR_remove_state", NULL},
{"ERR_free_strings", NULL},
{"ENGINE_cleanup", NULL},
{"CONF_modules_unload", NULL},
{"CRYPTO_cleanup_all_ex_data", NULL},
{"EVP_cleanup", NULL},
{"X509_free", NULL},
{"X509_get_subject_name", NULL},
{"X509_get_issuer_name", NULL},
{"X509_NAME_oneline", NULL},
{"X509_get_serialNumber", NULL},
{"i2c_ASN1_INTEGER", NULL},
{"EVP_get_digestbyname", NULL},
{"EVP_Digest", NULL},
{"i2d_X509", NULL},
{"BN_bn2hex", NULL},
{"ASN1_INTEGER_to_BN", NULL},
{"BN_free", NULL},
{"CRYPTO_free", NULL},
{NULL, NULL}};
#endif /* OPENSSL_API_1_1 */
#endif /* NO_SSL_DL */
#endif /* NO_SSL */
#if !defined(NO_CACHING)
static const char *month_names[] = {"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"};
#endif /* !NO_CACHING */
/* Unified socket address. For IPv6 support, add IPv6 address structure in
* the
* union u. */
union usa {
struct sockaddr sa;
struct sockaddr_in sin;
#if defined(USE_IPV6)
struct sockaddr_in6 sin6;
#endif
};
/* Describes a string (chunk of memory). */
struct vec {
const char *ptr;
size_t len;
};
struct mg_file_stat {
/* File properties filled by mg_stat: */
uint64_t size;
time_t last_modified;
int is_directory; /* Set to 1 if mg_stat is called for a directory */
int is_gzipped; /* Set to 1 if the content is gzipped, in which
* case we need a "Content-Eencoding: gzip" header */
int location; /* 0 = nowhere, 1 = on disk, 2 = in memory */
};
struct mg_file_in_memory {
char *p;
uint32_t pos;
char mode;
};
struct mg_file_access {
/* File properties filled by mg_fopen: */
FILE *fp;
/* TODO (low): Replace "membuf" implementation by a "file in memory"
* support library. Use some struct mg_file_in_memory *mf; instead of
* membuf char pointer. */
const char *membuf;
};
struct mg_file {
struct mg_file_stat stat;
struct mg_file_access access;
};
#define STRUCT_FILE_INITIALIZER \
{ \
{ \
(uint64_t)0, (time_t)0, 0, 0, 0 \
} \
, \
{ \
(FILE *) NULL, (const char *)NULL \
} \
}
/* Describes listening socket, or socket which was accept()-ed by the master
* thread and queued for future handling by the worker thread. */
struct socket {
SOCKET sock; /* Listening socket */
union usa lsa; /* Local socket address */
union usa rsa; /* Remote socket address */
unsigned char is_ssl; /* Is port SSL-ed */
unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
* port */
unsigned char in_use; /* Is valid */
};
/* NOTE(lsm): this enum shoulds be in sync with the config_options below. */
enum {
CGI_EXTENSIONS,
CGI_ENVIRONMENT,
PUT_DELETE_PASSWORDS_FILE,
CGI_INTERPRETER,
PROTECT_URI,
AUTHENTICATION_DOMAIN,
ENABLE_AUTH_DOMAIN_CHECK,
SSI_EXTENSIONS,
THROTTLE,
ACCESS_LOG_FILE,
ENABLE_DIRECTORY_LISTING,
ERROR_LOG_FILE,
GLOBAL_PASSWORDS_FILE,
INDEX_FILES,
ENABLE_KEEP_ALIVE,
ACCESS_CONTROL_LIST,
EXTRA_MIME_TYPES,
LISTENING_PORTS,
DOCUMENT_ROOT,
SSL_CERTIFICATE,
SSL_CERTIFICATE_CHAIN,
NUM_THREADS,
RUN_AS_USER,
URL_REWRITE_PATTERN,
HIDE_FILES,
REQUEST_TIMEOUT,
KEEP_ALIVE_TIMEOUT,
LINGER_TIMEOUT,
SSL_DO_VERIFY_PEER,
SSL_CA_PATH,
SSL_CA_FILE,
SSL_VERIFY_DEPTH,
SSL_DEFAULT_VERIFY_PATHS,
SSL_CIPHER_LIST,
SSL_PROTOCOL_VERSION,
SSL_SHORT_TRUST,
#if defined(USE_WEBSOCKET)
WEBSOCKET_TIMEOUT,
#endif
DECODE_URL,
#if defined(USE_LUA)
LUA_PRELOAD_FILE,
LUA_SCRIPT_EXTENSIONS,
LUA_SERVER_PAGE_EXTENSIONS,
#endif
#if defined(USE_DUKTAPE)
DUKTAPE_SCRIPT_EXTENSIONS,
#endif
#if defined(USE_WEBSOCKET)
WEBSOCKET_ROOT,
#endif
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
LUA_WEBSOCKET_EXTENSIONS,
#endif
ACCESS_CONTROL_ALLOW_ORIGIN,
ACCESS_CONTROL_ALLOW_METHODS,
ACCESS_CONTROL_ALLOW_HEADERS,
ERROR_PAGES,
CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
* socket option typedef TCP_NODELAY. */
#if !defined(NO_CACHING)
STATIC_FILE_MAX_AGE,
#endif
#if !defined(NO_SSL)
STRICT_HTTPS_MAX_AGE,
#endif
#if defined(__linux__)
ALLOW_SENDFILE_CALL,
#endif
#if defined(_WIN32)
CASE_SENSITIVE_FILES,
#endif
#if defined(USE_LUA)
LUA_BACKGROUND_SCRIPT,
LUA_BACKGROUND_SCRIPT_PARAMS,
#endif
ADDITIONAL_HEADER,
MAX_REQUEST_SIZE,
ALLOW_INDEX_SCRIPT_SUB_RES,
NUM_OPTIONS
};
/* Config option name, config types, default value */
static struct mg_option config_options[] = {
{"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
{"cgi_environment", CONFIG_TYPE_STRING_LIST, NULL},
{"put_delete_auth_file", CONFIG_TYPE_FILE, NULL},
{"cgi_interpreter", CONFIG_TYPE_FILE, NULL},
{"protect_uri", CONFIG_TYPE_STRING_LIST, NULL},
{"authentication_domain", CONFIG_TYPE_STRING, "mydomain.com"},
{"enable_auth_domain_check", CONFIG_TYPE_BOOLEAN, "yes"},
{"ssi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"},
{"throttle", CONFIG_TYPE_STRING_LIST, NULL},
{"access_log_file", CONFIG_TYPE_FILE, NULL},
{"enable_directory_listing", CONFIG_TYPE_BOOLEAN, "yes"},
{"error_log_file", CONFIG_TYPE_FILE, NULL},
{"global_auth_file", CONFIG_TYPE_FILE, NULL},
{"index_files",
CONFIG_TYPE_STRING_LIST,
#ifdef USE_LUA
"index.xhtml,index.html,index.htm,"
"index.lp,index.lsp,index.lua,index.cgi,"
"index.shtml,index.php"},
#else
"index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
#endif
{"enable_keep_alive", CONFIG_TYPE_BOOLEAN, "no"},
{"access_control_list", CONFIG_TYPE_STRING_LIST, NULL},
{"extra_mime_types", CONFIG_TYPE_STRING_LIST, NULL},
{"listening_ports", CONFIG_TYPE_STRING_LIST, "8080"},
{"document_root", CONFIG_TYPE_DIRECTORY, NULL},
{"ssl_certificate", CONFIG_TYPE_FILE, NULL},
{"ssl_certificate_chain", CONFIG_TYPE_FILE, NULL},
{"num_threads", CONFIG_TYPE_NUMBER, "50"},
{"run_as_user", CONFIG_TYPE_STRING, NULL},
{"url_rewrite_patterns", CONFIG_TYPE_STRING_LIST, NULL},
{"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL},
{"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
{"keep_alive_timeout_ms", CONFIG_TYPE_NUMBER, "500"},
{"linger_timeout_ms", CONFIG_TYPE_NUMBER, NULL},
/* TODO(Feature): this is no longer a boolean, but yes/no/optional */
{"ssl_verify_peer", CONFIG_TYPE_BOOLEAN, "no"},
{"ssl_ca_path", CONFIG_TYPE_DIRECTORY, NULL},
{"ssl_ca_file", CONFIG_TYPE_FILE, NULL},
{"ssl_verify_depth", CONFIG_TYPE_NUMBER, "9"},
{"ssl_default_verify_paths", CONFIG_TYPE_BOOLEAN, "yes"},
{"ssl_cipher_list", CONFIG_TYPE_STRING, NULL},
{"ssl_protocol_version", CONFIG_TYPE_NUMBER, "0"},
{"ssl_short_trust", CONFIG_TYPE_BOOLEAN, "no"},
#if defined(USE_WEBSOCKET)
{"websocket_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
#endif
{"decode_url", CONFIG_TYPE_BOOLEAN, "yes"},
#if defined(USE_LUA)
{"lua_preload_file", CONFIG_TYPE_FILE, NULL},
{"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
{"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
#endif
#if defined(USE_DUKTAPE)
/* The support for duktape is still in alpha version state.
* The name of this config option might change. */
{"duktape_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"},
#endif
#if defined(USE_WEBSOCKET)
{"websocket_root", CONFIG_TYPE_DIRECTORY, NULL},
#endif
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
{"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
#endif
{"access_control_allow_origin", CONFIG_TYPE_STRING, "*"},
{"access_control_allow_methods", CONFIG_TYPE_STRING, "*"},
{"access_control_allow_headers", CONFIG_TYPE_STRING, "*"},
{"error_pages", CONFIG_TYPE_DIRECTORY, NULL},
{"tcp_nodelay", CONFIG_TYPE_NUMBER, "0"},
#if !defined(NO_CACHING)
{"static_file_max_age", CONFIG_TYPE_NUMBER, "3600"},
#endif
#if !defined(NO_SSL)
{"strict_transport_security_max_age", CONFIG_TYPE_NUMBER, NULL},
#endif
#if defined(__linux__)
{"allow_sendfile_call", CONFIG_TYPE_BOOLEAN, "yes"},
#endif
#if defined(_WIN32)
{"case_sensitive", CONFIG_TYPE_BOOLEAN, "no"},
#endif
#if defined(USE_LUA)
{"lua_background_script", CONFIG_TYPE_FILE, NULL},
{"lua_background_script_params", CONFIG_TYPE_STRING_LIST, NULL},
#endif
{"additional_header", CONFIG_TYPE_STRING_MULTILINE, NULL},
{"max_request_size", CONFIG_TYPE_NUMBER, "16384"},
{"allow_index_script_resource", CONFIG_TYPE_BOOLEAN, "no"},
{NULL, CONFIG_TYPE_UNKNOWN, NULL}};
/* Check if the config_options and the corresponding enum have compatible
* sizes. */
mg_static_assert((sizeof(config_options) / sizeof(config_options[0]))
== (NUM_OPTIONS + 1),
"config_options and enum not sync");
enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER };
struct mg_handler_info {
/* Name/Pattern of the URI. */
char *uri;
size_t uri_len;
/* handler type */
int handler_type;
/* Handler for http/https or authorization requests. */
mg_request_handler handler;
/* Handler for ws/wss (websocket) requests. */
mg_websocket_connect_handler connect_handler;
mg_websocket_ready_handler ready_handler;
mg_websocket_data_handler data_handler;
mg_websocket_close_handler close_handler;
/* accepted subprotocols for ws/wss requests. */
struct mg_websocket_subprotocols *subprotocols;
/* Handler for authorization requests */
mg_authorization_handler auth_handler;
/* User supplied argument for the handler function. */
void *cbdata;
/* next handler in a linked list */
struct mg_handler_info *next;
};
enum {
CONTEXT_INVALID,
CONTEXT_SERVER,
CONTEXT_HTTP_CLIENT,
CONTEXT_WS_CLIENT
};
struct mg_context {
volatile int stop_flag; /* Should we stop event loop */
SSL_CTX *ssl_ctx; /* SSL context */
char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */
struct mg_callbacks callbacks; /* User-defined callback function */
void *user_data; /* User-defined data */
int context_type; /* See CONTEXT_* above */
struct socket *listening_sockets;
struct pollfd *listening_socket_fds;
unsigned int num_listening_sockets;
pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */
#ifdef ALTERNATIVE_QUEUE
struct socket *client_socks;
void **client_wait_events;
#else
struct socket queue[MGSQLEN]; /* Accepted sockets */
volatile int sq_head; /* Head of the socket queue */
volatile int sq_tail; /* Tail of the socket queue */
pthread_cond_t sq_full; /* Signaled when socket is produced */
pthread_cond_t sq_empty; /* Signaled when socket is consumed */
#endif
unsigned int max_request_size; /* The max request size */
pthread_t masterthreadid; /* The master thread ID */
unsigned int
cfg_worker_threads; /* The number of configured worker threads. */
pthread_t *worker_threadids; /* The worker thread IDs */
struct mg_connection *worker_connections; /* The connection struct, pre-
* allocated for each worker */
time_t start_time; /* Server start time, used for authentication
* and for diagnstics. */
uint64_t auth_nonce_mask; /* Mask for all nonce values */
pthread_mutex_t nonce_mutex; /* Protects nonce_count */
unsigned long nonce_count; /* Used nonces, used for authentication */
char *systemName; /* What operating system is running */
/* linked list of uri handlers */
struct mg_handler_info *handlers;
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
/* linked list of shared lua websockets */
struct mg_shared_lua_websocket_list *shared_lua_websockets;
#endif
#if defined(USE_TIMERS)
struct ttimers *timers;
#endif
#if defined(USE_LUA)
void *lua_background_state;
#endif
#if defined(USE_SERVER_STATS)
int active_connections;
int max_connections;
int64_t total_connections;
int64_t total_requests;
struct mg_memory_stat ctx_memory;
int64_t total_data_read;
int64_t total_data_written;
#endif
};
#if defined(USE_SERVER_STATS)
static struct mg_memory_stat mg_common_memory = {0, 0, 0};
static struct mg_memory_stat *
get_memory_stat(struct mg_context *ctx)
{
if (ctx) {
return &(ctx->ctx_memory);
}
return &mg_common_memory;
}
#endif
enum {
CONNECTION_TYPE_INVALID,
CONNECTION_TYPE_REQUEST,
CONNECTION_TYPE_RESPONSE
};
struct mg_connection {
int connection_type; /* see CONNECTION_TYPE_* above */
struct mg_request_info request_info;
struct mg_response_info response_info;
struct mg_context *ctx;
#if defined(USE_SERVER_STATS)
int conn_state; /* 0 = undef, numerical value may change in different
* versions. For the current definition, see
* mg_get_connection_info_impl */
#endif
SSL *ssl; /* SSL descriptor */
SSL_CTX *client_ssl_ctx; /* SSL context for client connections */
struct socket client; /* Connected client */
time_t conn_birth_time; /* Time (wall clock) when connection was
* established */
struct timespec req_time; /* Time (since system start) when the request
* was received */
int64_t num_bytes_sent; /* Total bytes sent to client */
int64_t content_len; /* Content-Length header value */
int64_t consumed_content; /* How many bytes of content have been read */
int is_chunked; /* Transfer-Encoding is chunked:
* 0 = not chunked,
* 1 = chunked, do data read yet,
* 2 = chunked, some data read,
* 3 = chunked, all data read
*/
size_t chunk_remainder; /* Unread data from the last chunk */
char *buf; /* Buffer for received data */
char *path_info; /* PATH_INFO part of the URL */
int must_close; /* 1 if connection must be closed */
int accept_gzip; /* 1 if gzip encoding is accepted */
int in_error_handler; /* 1 if in handler for user defined error
* pages */
#if defined(USE_WEBSOCKET)
int in_websocket_handling; /* 1 if in read_websocket */
#endif
int handled_requests; /* Number of requests handled by this connection
*/
int buf_size; /* Buffer size */
int request_len; /* Size of the request + headers in a buffer */
int data_len; /* Total size of data in a buffer */
int status_code; /* HTTP reply status code, e.g. 200 */
int throttle; /* Throttling, bytes/sec. <= 0 means no
* throttle */
time_t last_throttle_time; /* Last time throttled data was sent */
int64_t last_throttle_bytes; /* Bytes sent this second */
pthread_mutex_t mutex; /* Used by mg_(un)lock_connection to ensure
* atomic transmissions for websockets */
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
void *lua_websocket_state; /* Lua_State for a websocket connection */
#endif
int thread_index; /* Thread index within ctx */
};
/* Directory entry */
struct de {
struct mg_connection *conn;
char *file_name;
struct mg_file_stat file;
};
#if defined(USE_WEBSOCKET)
static int is_websocket_protocol(const struct mg_connection *conn);
#else
#define is_websocket_protocol(conn) (0)
#endif
#if !defined(NO_THREAD_NAME)
#if defined(_WIN32) && defined(_MSC_VER)
/* Set the thread name for debugging purposes in Visual Studio
* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
*/
#pragma pack(push, 8)
typedef struct tagTHREADNAME_INFO {
DWORD dwType; /* Must be 0x1000. */
LPCSTR szName; /* Pointer to name (in user addr space). */
DWORD dwThreadID; /* Thread ID (-1=caller thread). */
DWORD dwFlags; /* Reserved for future use, must be zero. */
} THREADNAME_INFO;
#pragma pack(pop)
#elif defined(__linux__)
#include <sys/prctl.h>
#include <sys/sendfile.h>
#ifdef ALTERNATIVE_QUEUE
#include <sys/eventfd.h>
#endif /* ALTERNATIVE_QUEUE */
#if defined(ALTERNATIVE_QUEUE)
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
/* For every system, "(sizeof(int) == sizeof(void *))" is either always
* true or always false. One of the two branches is unreachable in any case.
* Unfortunately the C standard does not define a way to check this at
* compile time, since the #if preprocessor conditions can not use the sizeof
* operator as an argument. */
#endif
#if defined(__GNUC__) || defined(__MINGW32__)
/* GCC does not realize one branch is unreachable, so it raises some
* pointer cast warning within the unreachable branch.
*/
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
#endif
static void *
event_create(void)
{
int evhdl = eventfd(0, EFD_CLOEXEC);
int *ret;
if (evhdl == -1) {
/* Linux uses -1 on error, Windows NULL. */
/* However, Linux does not return 0 on success either. */
return 0;
}
if (sizeof(int) == sizeof(void *)) {
ret = (void *)evhdl;
} else {
ret = (int *)mg_malloc(sizeof(int));
if (ret) {
*ret = evhdl;
} else {
(void)close(evhdl);
}
}
return (void *)ret;
}
static int
event_wait(void *eventhdl)
{
uint64_t u;
int evhdl, s;
if (sizeof(int) == sizeof(void *)) {
evhdl = (int)eventhdl;
} else {
if (!eventhdl) {
/* error */
return 0;
}
evhdl = *(int *)eventhdl;
}
s = (int)read(evhdl, &u, sizeof(u));
if (s != sizeof(uint64_t)) {
/* error */
return 0;
}
(void)u; /* the value is not required */
return 1;
}
static int
event_signal(void *eventhdl)
{
uint64_t u = 1;
int evhdl, s;
if (sizeof(int) == sizeof(void *)) {
evhdl = (int)eventhdl;
} else {
if (!eventhdl) {
/* error */
return 0;
}
evhdl = *(int *)eventhdl;
}
s = (int)write(evhdl, &u, sizeof(u));
if (s != sizeof(uint64_t)) {
/* error */
return 0;
}
return 1;
}
static void
event_destroy(void *eventhdl)
{
int evhdl;
if (sizeof(int) == sizeof(void *)) {
evhdl = (int)eventhdl;
close(evhdl);
} else {
if (!eventhdl) {
/* error */
return;
}
evhdl = *(int *)eventhdl;
close(evhdl);
mg_free(eventhdl);
}
}
#if defined(__GNUC__) || defined(__MINGW32__)
#pragma GCC diagnostic pop
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif
#endif
#if !defined(__linux__) && !defined(_WIN32) && defined(ALTERNATIVE_QUEUE)
struct posix_event {
pthread_mutex_t mutex;
pthread_cond_t cond;
};
static void *
event_create(void)
{
struct posix_event *ret = mg_malloc(sizeof(struct posix_event));
if (ret == 0) {
/* out of memory */
return 0;
}
if (0 != pthread_mutex_init(&(ret->mutex), NULL)) {
/* pthread mutex not available */
mg_free(ret);
return 0;
}
if (0 != pthread_cond_init(&(ret->cond), NULL)) {
/* pthread cond not available */
pthread_mutex_destroy(&(ret->mutex));
mg_free(ret);
return 0;
}
return (void *)ret;
}
static int
event_wait(void *eventhdl)
{
struct posix_event *ev = (struct posix_event *)eventhdl;
pthread_mutex_lock(&(ev->mutex));
pthread_cond_wait(&(ev->cond), &(ev->mutex));
pthread_mutex_unlock(&(ev->mutex));
return 1;
}
static int
event_signal(void *eventhdl)
{
struct posix_event *ev = (struct posix_event *)eventhdl;
pthread_mutex_lock(&(ev->mutex));
pthread_cond_signal(&(ev->cond));
pthread_mutex_unlock(&(ev->mutex));
return 1;
}
static void
event_destroy(void *eventhdl)
{
struct posix_event *ev = (struct posix_event *)eventhdl;
pthread_cond_destroy(&(ev->cond));
pthread_mutex_destroy(&(ev->mutex));
mg_free(ev);
}
#endif
static void
mg_set_thread_name(const char *name)
{
char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */
mg_snprintf(
NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name);
#if defined(_WIN32)
#if defined(_MSC_VER)
/* Windows and Visual Studio Compiler */
__try
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = threadName;
info.dwThreadID = ~0U;
info.dwFlags = 0;
RaiseException(0x406D1388,
0,
sizeof(info) / sizeof(ULONG_PTR),
(ULONG_PTR *)&info);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
#elif defined(__MINGW32__)
/* No option known to set thread name for MinGW */
#endif
#elif defined(__GLIBC__) \
&& ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12)))
/* pthread_setname_np first appeared in glibc in version 2.12*/
(void)pthread_setname_np(pthread_self(), threadName);
#elif defined(__linux__)
/* on linux we can use the old prctl function */
(void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
#endif
}
#else /* !defined(NO_THREAD_NAME) */
void
mg_set_thread_name(const char *threadName)
{
}
#endif
#if defined(MG_LEGACY_INTERFACE)
const char **
mg_get_valid_option_names(void)
{
/* This function is deprecated. Use mg_get_valid_options instead. */
static const char *
data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0};
int i;
for (i = 0; config_options[i].name != NULL; i++) {
data[i * 2] = config_options[i].name;
data[i * 2 + 1] = config_options[i].default_value;
}
return data;
}
#endif
const struct mg_option *
mg_get_valid_options(void)
{
return config_options;
}
/* Do not open file (used in is_file_in_memory) */
#define MG_FOPEN_MODE_NONE (0)
/* Open file for read only access */
#define MG_FOPEN_MODE_READ (1)
/* Open file for writing, create and overwrite */
#define MG_FOPEN_MODE_WRITE (2)
/* Open file for writing, create and append */
#define MG_FOPEN_MODE_APPEND (4)
/* If a file is in memory, set all "stat" members and the membuf pointer of
* output filep and return 1, otherwise return 0 and don't modify anything.
*/
static int
open_file_in_memory(const struct mg_connection *conn,
const char *path,
struct mg_file *filep,
int mode)
{
#if defined(MG_USE_OPEN_FILE)
size_t size = 0;
const char *buf = NULL;
if (!conn) {
return 0;
}
if ((mode != MG_FOPEN_MODE_NONE) && (mode != MG_FOPEN_MODE_READ)) {
return 0;
}
if (conn->ctx->callbacks.open_file) {
buf = conn->ctx->callbacks.open_file(conn, path, &size);
if (buf != NULL) {
if (filep == NULL) {
/* This is a file in memory, but we cannot store the
* properties
* now.
* Called from "is_file_in_memory" function. */
return 1;
}
/* NOTE: override filep->size only on success. Otherwise, it
* might
* break constructs like if (!mg_stat() || !mg_fopen()) ... */
filep->access.membuf = buf;
filep->access.fp = NULL;
/* Size was set by the callback */
filep->stat.size = size;
/* Assume the data may change during runtime by setting
* last_modified = now */
filep->stat.last_modified = time(NULL);
filep->stat.is_directory = 0;
filep->stat.is_gzipped = 0;
}
}
return (buf != NULL);
#else
(void)conn;
(void)path;
(void)filep;
(void)mode;
return 0;
#endif
}
static int
is_file_in_memory(const struct mg_connection *conn, const char *path)
{
return open_file_in_memory(conn, path, NULL, MG_FOPEN_MODE_NONE);
}
static int
is_file_opened(const struct mg_file_access *fileacc)
{
if (!fileacc) {